diff options
Diffstat (limited to 'crates/ra_ide/src/typing.rs')
-rw-r--r-- | crates/ra_ide/src/typing.rs | 54 |
1 files changed, 16 insertions, 38 deletions
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 6f04f0be4..39bb3b357 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs | |||
@@ -17,7 +17,7 @@ mod on_enter; | |||
17 | 17 | ||
18 | use ra_db::{FilePosition, SourceDatabase}; | 18 | use ra_db::{FilePosition, SourceDatabase}; |
19 | use ra_fmt::leading_indent; | 19 | use ra_fmt::leading_indent; |
20 | use ra_ide_db::{source_change::SingleFileChange, RootDatabase}; | 20 | use ra_ide_db::RootDatabase; |
21 | use ra_syntax::{ | 21 | use ra_syntax::{ |
22 | algo::find_node_at_offset, | 22 | algo::find_node_at_offset, |
23 | ast::{self, AstToken}, | 23 | ast::{self, AstToken}, |
@@ -40,15 +40,11 @@ pub(crate) fn on_char_typed( | |||
40 | assert!(TRIGGER_CHARS.contains(char_typed)); | 40 | assert!(TRIGGER_CHARS.contains(char_typed)); |
41 | let file = &db.parse(position.file_id).tree(); | 41 | let file = &db.parse(position.file_id).tree(); |
42 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); | 42 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); |
43 | let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?; | 43 | let text_edit = on_char_typed_inner(file, position.offset, char_typed)?; |
44 | Some(single_file_change.into_source_change(position.file_id)) | 44 | Some(SourceChange::source_file_edit_from(position.file_id, text_edit)) |
45 | } | 45 | } |
46 | 46 | ||
47 | fn on_char_typed_inner( | 47 | fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { |
48 | file: &SourceFile, | ||
49 | offset: TextSize, | ||
50 | char_typed: char, | ||
51 | ) -> Option<SingleFileChange> { | ||
52 | assert!(TRIGGER_CHARS.contains(char_typed)); | 48 | assert!(TRIGGER_CHARS.contains(char_typed)); |
53 | match char_typed { | 49 | match char_typed { |
54 | '.' => on_dot_typed(file, offset), | 50 | '.' => on_dot_typed(file, offset), |
@@ -61,7 +57,7 @@ fn on_char_typed_inner( | |||
61 | /// Returns an edit which should be applied after `=` was typed. Primarily, | 57 | /// Returns an edit which should be applied after `=` was typed. Primarily, |
62 | /// this works when adding `let =`. | 58 | /// this works when adding `let =`. |
63 | // FIXME: use a snippet completion instead of this hack here. | 59 | // FIXME: use a snippet completion instead of this hack here. |
64 | fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { | 60 | fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
65 | assert_eq!(file.syntax().text().char_at(offset), Some('=')); | 61 | assert_eq!(file.syntax().text().char_at(offset), Some('=')); |
66 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; | 62 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; |
67 | if let_stmt.semicolon_token().is_some() { | 63 | if let_stmt.semicolon_token().is_some() { |
@@ -79,15 +75,11 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> | |||
79 | return None; | 75 | return None; |
80 | } | 76 | } |
81 | let offset = let_stmt.syntax().text_range().end(); | 77 | let offset = let_stmt.syntax().text_range().end(); |
82 | Some(SingleFileChange { | 78 | Some(TextEdit::insert(offset, ";".to_string())) |
83 | label: "add semicolon".to_string(), | ||
84 | edit: TextEdit::insert(offset, ";".to_string()), | ||
85 | cursor_position: None, | ||
86 | }) | ||
87 | } | 79 | } |
88 | 80 | ||
89 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. | 81 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. |
90 | fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { | 82 | fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
91 | assert_eq!(file.syntax().text().char_at(offset), Some('.')); | 83 | assert_eq!(file.syntax().text().char_at(offset), Some('.')); |
92 | let whitespace = | 84 | let whitespace = |
93 | file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; | 85 | file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; |
@@ -108,15 +100,11 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> | |||
108 | return None; | 100 | return None; |
109 | } | 101 | } |
110 | 102 | ||
111 | Some(SingleFileChange { | 103 | Some(TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent)) |
112 | label: "reindent dot".to_string(), | ||
113 | edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent), | ||
114 | cursor_position: Some(offset + target_indent_len - current_indent_len + TextSize::of('.')), | ||
115 | }) | ||
116 | } | 104 | } |
117 | 105 | ||
118 | /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` | 106 | /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` |
119 | fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { | 107 | fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
120 | let file_text = file.syntax().text(); | 108 | let file_text = file.syntax().text(); |
121 | assert_eq!(file_text.char_at(offset), Some('>')); | 109 | assert_eq!(file_text.char_at(offset), Some('>')); |
122 | let after_arrow = offset + TextSize::of('>'); | 110 | let after_arrow = offset + TextSize::of('>'); |
@@ -127,11 +115,7 @@ fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChang | |||
127 | return None; | 115 | return None; |
128 | } | 116 | } |
129 | 117 | ||
130 | Some(SingleFileChange { | 118 | Some(TextEdit::insert(after_arrow, " ".to_string())) |
131 | label: "add space after return type".to_string(), | ||
132 | edit: TextEdit::insert(after_arrow, " ".to_string()), | ||
133 | cursor_position: Some(after_arrow), | ||
134 | }) | ||
135 | } | 119 | } |
136 | 120 | ||
137 | #[cfg(test)] | 121 | #[cfg(test)] |
@@ -140,29 +124,23 @@ mod tests { | |||
140 | 124 | ||
141 | use super::*; | 125 | use super::*; |
142 | 126 | ||
143 | fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { | 127 | fn do_type_char(char_typed: char, before: &str) -> Option<String> { |
144 | let (offset, before) = extract_offset(before); | 128 | let (offset, before) = extract_offset(before); |
145 | let edit = TextEdit::insert(offset, char_typed.to_string()); | 129 | let edit = TextEdit::insert(offset, char_typed.to_string()); |
146 | let mut before = before.to_string(); | 130 | let mut before = before.to_string(); |
147 | edit.apply(&mut before); | 131 | edit.apply(&mut before); |
148 | let parse = SourceFile::parse(&before); | 132 | let parse = SourceFile::parse(&before); |
149 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { | 133 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { |
150 | it.edit.apply(&mut before); | 134 | it.apply(&mut before); |
151 | (before.to_string(), it) | 135 | before.to_string() |
152 | }) | 136 | }) |
153 | } | 137 | } |
154 | 138 | ||
155 | fn type_char(char_typed: char, before: &str, after: &str) { | 139 | fn type_char(char_typed: char, before: &str, after: &str) { |
156 | let (actual, file_change) = do_type_char(char_typed, before) | 140 | let actual = do_type_char(char_typed, before) |
157 | .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); | 141 | .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); |
158 | 142 | ||
159 | if after.contains("<|>") { | 143 | assert_eq_text!(after, &actual); |
160 | let (offset, after) = extract_offset(after); | ||
161 | assert_eq_text!(&after, &actual); | ||
162 | assert_eq!(file_change.cursor_position, Some(offset)) | ||
163 | } else { | ||
164 | assert_eq_text!(after, &actual); | ||
165 | } | ||
166 | } | 144 | } |
167 | 145 | ||
168 | fn type_char_noop(char_typed: char, before: &str) { | 146 | fn type_char_noop(char_typed: char, before: &str) { |
@@ -350,6 +328,6 @@ fn foo() { | |||
350 | 328 | ||
351 | #[test] | 329 | #[test] |
352 | fn adds_space_after_return_type() { | 330 | fn adds_space_after_return_type() { |
353 | type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }") | 331 | type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -> { 92 }") |
354 | } | 332 | } |
355 | } | 333 | } |