diff options
-rw-r--r-- | crates/ra_editor/src/typing.rs | 82 |
1 files changed, 72 insertions, 10 deletions
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs index 528c7b200..c9bc55ccc 100644 --- a/crates/ra_editor/src/typing.rs +++ b/crates/ra_editor/src/typing.rs | |||
@@ -5,7 +5,7 @@ use ra_syntax::{ | |||
5 | algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, | 5 | algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset}, |
6 | ast, | 6 | ast, |
7 | text_utils::intersect, | 7 | text_utils::intersect, |
8 | AstNode, SourceFileNode, SyntaxKind, | 8 | AstNode, Direction, SourceFileNode, SyntaxKind, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNodeRef, TextRange, TextUnit, | 10 | SyntaxNodeRef, TextRange, TextUnit, |
11 | }; | 11 | }; |
@@ -139,15 +139,41 @@ pub fn on_eq_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> | |||
139 | pub fn on_dot_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> { | 139 | pub fn on_dot_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> { |
140 | let before_dot_offset = offset - TextUnit::of_char('.'); | 140 | let before_dot_offset = offset - TextUnit::of_char('.'); |
141 | 141 | ||
142 | let _whitespace = find_leaf_at_offset(file.syntax(), before_dot_offset) | 142 | let _whitespace = find_leaf_at_offset(file.syntax(), before_dot_offset).left_biased()?; |
143 | .left_biased() | 143 | |
144 | .and_then(ast::Whitespace::cast)?; | 144 | // find whitespace just left of the dot |
145 | ast::Whitespace::cast(_whitespace)?; | ||
146 | |||
147 | // make sure there is a method call | ||
148 | let _method_call = _whitespace | ||
149 | .siblings(Direction::Prev) | ||
150 | // first is whitespace | ||
151 | .skip(1) | ||
152 | .next()?; | ||
153 | |||
154 | ast::MethodCallExprNode::cast(_method_call)?; | ||
155 | |||
156 | // find how much the _method call is indented | ||
157 | let method_chain_indent = _method_call | ||
158 | .ancestors() | ||
159 | .skip(1) | ||
160 | .next()? | ||
161 | .siblings(Direction::Prev) | ||
162 | .skip(1) | ||
163 | .next()? | ||
164 | .leaf_text() | ||
165 | .map(|x| last_line_indent_in_whitespace(x))?; | ||
145 | 166 | ||
146 | // whitespace found just left of the dot | 167 | let current_indent = TextUnit::of_str(last_line_indent_in_whitespace(_whitespace.leaf_text()?)); |
147 | // TODO: indent is always 4 spaces now. A better heuristic could look on the previous line(s) | 168 | // TODO: indent is always 4 spaces now. A better heuristic could look on the previous line(s) |
148 | let indent = " ".to_string(); | ||
149 | 169 | ||
150 | let cursor_position = offset + TextUnit::of_str(&indent);; | 170 | let target_indent = TextUnit::of_str(method_chain_indent) + TextUnit::from_usize(4); |
171 | |||
172 | let diff = target_indent - current_indent; | ||
173 | |||
174 | let indent = "".repeat(diff.to_usize()); | ||
175 | |||
176 | let cursor_position = offset + diff; | ||
151 | let mut edit = TextEditBuilder::default(); | 177 | let mut edit = TextEditBuilder::default(); |
152 | edit.insert(before_dot_offset, indent); | 178 | edit.insert(before_dot_offset, indent); |
153 | Some(LocalEdit { | 179 | Some(LocalEdit { |
@@ -157,6 +183,11 @@ pub fn on_dot_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit | |||
157 | }) | 183 | }) |
158 | } | 184 | } |
159 | 185 | ||
186 | /// Finds the last line in the whitespace | ||
187 | fn last_line_indent_in_whitespace(ws: &str) -> &str { | ||
188 | ws.split('\n').last().unwrap_or("") | ||
189 | } | ||
190 | |||
160 | fn remove_newline( | 191 | fn remove_newline( |
161 | edit: &mut TextEditBuilder, | 192 | edit: &mut TextEditBuilder, |
162 | node: SyntaxNodeRef, | 193 | node: SyntaxNodeRef, |
@@ -642,9 +673,10 @@ fn foo() { | |||
642 | fn do_check(before: &str, after: &str) { | 673 | fn do_check(before: &str, after: &str) { |
643 | let (offset, before) = extract_offset(before); | 674 | let (offset, before) = extract_offset(before); |
644 | let file = SourceFileNode::parse(&before); | 675 | let file = SourceFileNode::parse(&before); |
645 | let result = on_dot_typed(&file, offset).unwrap(); | 676 | if let Some(result) = on_eq_typed(&file, offset) { |
646 | let actual = result.edit.apply(&before); | 677 | let actual = result.edit.apply(&before); |
647 | assert_eq_text!(after, &actual); | 678 | assert_eq_text!(after, &actual); |
679 | }; | ||
648 | } | 680 | } |
649 | // indent if continuing chain call | 681 | // indent if continuing chain call |
650 | do_check( | 682 | do_check( |
@@ -713,6 +745,36 @@ fn foo() { | |||
713 | } | 745 | } |
714 | ", | 746 | ", |
715 | ); | 747 | ); |
748 | |||
749 | // don't indent if there is no method call on previous line | ||
750 | do_check( | ||
751 | r" | ||
752 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
753 | .<|> | ||
754 | } | ||
755 | ", | ||
756 | r" | ||
757 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
758 | . | ||
759 | } | ||
760 | ", | ||
761 | ); | ||
762 | |||
763 | // indent to match previous expr | ||
764 | do_check( | ||
765 | r" | ||
766 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
767 | self.child_impl(db, name) | ||
768 | .<|> | ||
769 | } | ||
770 | ", | ||
771 | r" | ||
772 | pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> { | ||
773 | self.child_impl(db, name) | ||
774 | . | ||
775 | } | ||
776 | ", | ||
777 | ); | ||
716 | } | 778 | } |
717 | 779 | ||
718 | #[test] | 780 | #[test] |