aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/typing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/typing.rs')
-rw-r--r--crates/ra_ide/src/typing.rs54
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
18use ra_db::{FilePosition, SourceDatabase}; 18use ra_db::{FilePosition, SourceDatabase};
19use ra_fmt::leading_indent; 19use ra_fmt::leading_indent;
20use ra_ide_db::{source_change::SingleFileChange, RootDatabase}; 20use ra_ide_db::RootDatabase;
21use ra_syntax::{ 21use 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
47fn on_char_typed_inner( 47fn 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.
64fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 60fn 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.
90fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 82fn 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() -> { ... }`
119fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { 107fn 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}