diff options
Diffstat (limited to 'crates/ra_editor')
-rw-r--r-- | crates/ra_editor/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_editor/src/typing.rs | 101 |
2 files changed, 100 insertions, 3 deletions
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index 2a801f7da..fe0045378 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -35,7 +35,7 @@ pub use self::{ | |||
35 | flip_comma, add_derive, add_impl, | 35 | flip_comma, add_derive, add_impl, |
36 | introduce_variable, | 36 | introduce_variable, |
37 | }, | 37 | }, |
38 | typing::{join_lines, on_eq_typed}, | 38 | typing::{join_lines, on_eq_typed, on_enter}, |
39 | completion::{scope_completion, CompletionItem}, | 39 | completion::{scope_completion, CompletionItem}, |
40 | folding_ranges::{Fold, FoldKind, folding_ranges} | 40 | folding_ranges::{Fold, FoldKind, folding_ranges} |
41 | }; | 41 | }; |
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs index 512076941..3384389d1 100644 --- a/crates/ra_editor/src/typing.rs +++ b/crates/ra_editor/src/typing.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | TextUnit, TextRange, SyntaxNodeRef, File, AstNode, SyntaxKind, | 4 | TextUnit, TextRange, SyntaxNodeRef, File, AstNode, SyntaxKind, |
5 | ast, | 5 | ast, |
6 | algo::{ | 6 | algo::{ |
7 | find_covering_node, | 7 | find_covering_node, find_leaf_at_offset, LeafAtOffset, |
8 | }, | 8 | }, |
9 | text_utils::{intersect, contains_offset_nonstrict}, | 9 | text_utils::{intersect, contains_offset_nonstrict}, |
10 | SyntaxKind::*, | 10 | SyntaxKind::*, |
@@ -56,6 +56,58 @@ pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { | |||
56 | } | 56 | } |
57 | } | 57 | } |
58 | 58 | ||
59 | pub fn on_enter(file: &File, offset: TextUnit) -> Option<LocalEdit> { | ||
60 | let comment = find_leaf_at_offset(file.syntax(), offset).left_biased().filter(|it| it.kind() == COMMENT)?; | ||
61 | let prefix = comment_preffix(comment)?; | ||
62 | if offset < comment.range().start() + TextUnit::of_str(prefix) { | ||
63 | return None; | ||
64 | } | ||
65 | |||
66 | let indent = node_indent(file, comment)?; | ||
67 | let inserted = format!("\n{}{}", indent, prefix); | ||
68 | let cursor_position = offset + TextUnit::of_str(&inserted); | ||
69 | let mut edit = EditBuilder::new(); | ||
70 | edit.insert(offset, inserted); | ||
71 | Some(LocalEdit { | ||
72 | edit: edit.finish(), | ||
73 | cursor_position: Some(cursor_position), | ||
74 | }) | ||
75 | } | ||
76 | |||
77 | fn comment_preffix(comment: SyntaxNodeRef) -> Option<&'static str> { | ||
78 | let text = comment.leaf_text().unwrap(); | ||
79 | let res = if text.starts_with("///") { | ||
80 | "/// " | ||
81 | } else if text.starts_with("//!") { | ||
82 | "//! " | ||
83 | } else if text.starts_with("//") { | ||
84 | "// " | ||
85 | } else { | ||
86 | return None; | ||
87 | }; | ||
88 | Some(res) | ||
89 | } | ||
90 | |||
91 | fn node_indent<'a>(file: &'a File, node: SyntaxNodeRef) -> Option<&'a str> { | ||
92 | let ws = match find_leaf_at_offset(file.syntax(), node.range().start()) { | ||
93 | LeafAtOffset::Between(l, r) => { | ||
94 | assert!(r == node); | ||
95 | l | ||
96 | } | ||
97 | LeafAtOffset::Single(n) => { | ||
98 | assert!(n == node); | ||
99 | return Some("") | ||
100 | } | ||
101 | LeafAtOffset::None => unreachable!(), | ||
102 | }; | ||
103 | if ws.kind() != WHITESPACE { | ||
104 | return None; | ||
105 | } | ||
106 | let text = ws.leaf_text().unwrap(); | ||
107 | let pos = text.as_str().rfind('\n').map(|it| it + 1).unwrap_or(0); | ||
108 | Some(&text[pos..]) | ||
109 | } | ||
110 | |||
59 | pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<LocalEdit> { | 111 | pub fn on_eq_typed(file: &File, offset: TextUnit) -> Option<LocalEdit> { |
60 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; | 112 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; |
61 | if let_stmt.has_semi() { | 113 | if let_stmt.has_semi() { |
@@ -187,7 +239,7 @@ fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str { | |||
187 | #[cfg(test)] | 239 | #[cfg(test)] |
188 | mod tests { | 240 | mod tests { |
189 | use super::*; | 241 | use super::*; |
190 | use test_utils::{check_action, extract_range, extract_offset}; | 242 | use test_utils::{check_action, extract_range, extract_offset, add_cursor}; |
191 | 243 | ||
192 | fn check_join_lines(before: &str, after: &str) { | 244 | fn check_join_lines(before: &str, after: &str) { |
193 | check_action(before, after, |file, offset| { | 245 | check_action(before, after, |file, offset| { |
@@ -344,4 +396,49 @@ fn foo() { | |||
344 | // } | 396 | // } |
345 | // "); | 397 | // "); |
346 | } | 398 | } |
399 | |||
400 | #[test] | ||
401 | fn test_on_enter() { | ||
402 | fn apply_on_enter(before: &str) -> Option<String> { | ||
403 | let (offset, before) = extract_offset(before); | ||
404 | let file = File::parse(&before); | ||
405 | let result = on_enter(&file, offset)?; | ||
406 | let actual = result.edit.apply(&before); | ||
407 | let actual = add_cursor(&actual, result.cursor_position.unwrap()); | ||
408 | Some(actual) | ||
409 | } | ||
410 | |||
411 | fn do_check(before: &str, after: &str) { | ||
412 | let actual = apply_on_enter(before).unwrap(); | ||
413 | assert_eq_text!(after, &actual); | ||
414 | } | ||
415 | |||
416 | fn do_check_noop(text: &str) { | ||
417 | assert!(apply_on_enter(text).is_none()) | ||
418 | } | ||
419 | |||
420 | do_check(r" | ||
421 | /// Some docs<|> | ||
422 | fn foo() { | ||
423 | } | ||
424 | ", r" | ||
425 | /// Some docs | ||
426 | /// <|> | ||
427 | fn foo() { | ||
428 | } | ||
429 | "); | ||
430 | do_check(r" | ||
431 | impl S { | ||
432 | /// Some<|> docs. | ||
433 | fn foo() {} | ||
434 | } | ||
435 | ", r" | ||
436 | impl S { | ||
437 | /// Some | ||
438 | /// <|> docs. | ||
439 | fn foo() {} | ||
440 | } | ||
441 | "); | ||
442 | do_check_noop(r"<|>//! docz"); | ||
443 | } | ||
347 | } | 444 | } |