Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/typing/on_enter.rs')
| -rw-r--r-- | crates/ide/src/typing/on_enter.rs | 196 |
1 files changed, 133 insertions, 63 deletions
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index fdc583a15c..7d04594a5b 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs @@ -1,13 +1,11 @@ -//! Handles the `Enter` key press. At the momently, this only continues -//! comments, but should handle indent some time in the future as well. +//! Handles the `Enter` key press, including comment continuation and +//! indentation in brace-delimited constructs. -use ide_db::base_db::RootQueryDb; use ide_db::{FilePosition, RootDatabase}; use syntax::{ AstNode, SmolStr, SourceFile, SyntaxKind::*, - SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, - algo::find_node_at_offset, + SyntaxToken, TextRange, TextSize, TokenAtOffset, ast::{self, AstToken, edit::IndentLevel}, }; @@ -20,7 +18,8 @@ use ide_db::text_edit::TextEdit; // - <kbd>Enter</kbd> inside triple-slash comments automatically inserts `///` // - <kbd>Enter</kbd> in the middle or after a trailing space in `//` inserts `//` // - <kbd>Enter</kbd> inside `//!` doc comments automatically inserts `//!` -// - <kbd>Enter</kbd> after `{` indents contents and closing `}` of single-line block +// - <kbd>Enter</kbd> after `{` reformats single-line brace-delimited contents by +// moving the text between `{` and the matching `}` onto an indented line // // This action needs to be assigned to shortcut explicitly. // @@ -52,7 +51,7 @@ use ide_db::text_edit::TextEdit; pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { let editioned_file_id_wrapper = ide_db::base_db::EditionedFileId::current_edition(db, position.file_id); - let parse = db.parse(editioned_file_id_wrapper); + let parse = editioned_file_id_wrapper.parse(db); let file = parse.tree(); let token = file.syntax().token_at_offset(position.offset).left_biased()?; @@ -60,22 +59,11 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text return on_enter_in_comment(&comment, &file, position.offset); } - if token.kind() == L_CURLY { - // Typing enter after the `{` of a block expression, where the `}` is on the same line - if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{')) - .and_then(|block| on_enter_in_block(block, position)) - { - cov_mark::hit!(indent_block_contents); - return Some(edit); - } - - // Typing enter after the `{` of a use tree list. - if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{')) - .and_then(|list| on_enter_in_use_tree_list(list, position)) - { - cov_mark::hit!(indent_block_contents); - return Some(edit); - } + if token.kind() == L_CURLY + && let Some(edit) = on_enter_in_braces(token, position) + { + cov_mark::hit!(indent_block_contents); + return Some(edit); } None @@ -120,44 +108,54 @@ fn on_enter_in_comment( Some(edit) } -fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<TextEdit> { - let contents = block_contents(&block)?; - - if block.syntax().text().contains_char('\n') { - return None; - } - - let indent = IndentLevel::from_node(block.syntax()); - let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1)); - edit.union(TextEdit::insert(contents.text_range().end(), format!("\n{indent}"))).ok()?; - Some(edit) -} - -fn on_enter_in_use_tree_list(list: ast::UseTreeList, position: FilePosition) -> Option<TextEdit> { - if list.syntax().text().contains_char('\n') { +fn on_enter_in_braces(l_curly: SyntaxToken, position: FilePosition) -> Option<TextEdit> { + if l_curly.text_range().end() != position.offset { return None; } - let indent = IndentLevel::from_node(list.syntax()); - let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1)); - edit.union(TextEdit::insert(list.r_curly_token()?.text_range().start(), format!("\n{indent}"))) - .ok()?; - Some(edit) + let (r_curly, content) = brace_contents_on_same_line(&l_curly)?; + let indent = IndentLevel::from_token(&l_curly); + Some(TextEdit::replace( + TextRange::new(position.offset, r_curly.text_range().start()), + format!("\n{}$0{}\n{indent}", indent + 1, content), + )) } -fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> { - let mut node = block.tail_expr().map(|e| e.syntax().clone()); +fn brace_contents_on_same_line(l_curly: &SyntaxToken) -> Option<(SyntaxToken, String)> { + let mut depth = 0_u32; + let mut tokens = Vec::new(); + let mut token = l_curly.next_token()?; - for stmt in block.statements() { - if node.is_some() { - // More than 1 node in the block + loop { + if token.kind() == WHITESPACE && token.text().contains('\n') { return None; } - node = Some(stmt.syntax().clone()); - } + match token.kind() { + L_CURLY => { + depth += 1; + tokens.push(token.clone()); + } + R_CURLY if depth == 0 => { + let first = tokens.iter().position(|it| it.kind() != WHITESPACE); + let last = tokens.iter().rposition(|it| it.kind() != WHITESPACE); + let content = match first.zip(last) { + Some((first, last)) => { + tokens[first..=last].iter().map(|it| it.text()).collect() + } + None => String::new(), + }; + return Some((token, content)); + } + R_CURLY => { + depth -= 1; + tokens.push(token.clone()); + } + _ => tokens.push(token.clone()), + } - node + token = token.next_token()?; + } } fn followed_by_comment(comment: &ast::Comment) -> bool { @@ -383,10 +381,58 @@ fn main() { } #[test] - fn indents_fn_body_block() { + fn indents_empty_brace_pairs() { cov_mark::check!(indent_block_contents); do_check( r#" +fn f() {$0} + "#, + r#" +fn f() { + $0 +} + "#, + ); + do_check( + r#" +fn f() { + let x = {$0}; +} + "#, + r#" +fn f() { + let x = { + $0 + }; +} + "#, + ); + do_check( + r#" +use crate::{$0}; + "#, + r#" +use crate::{ + $0 +}; + "#, + ); + do_check( + r#" +mod m {$0} + "#, + r#" +mod m { + $0 +} + "#, + ); + } + + #[test] + fn indents_fn_body_block() { + do_check( + r#" fn f() {$0()} "#, r#" @@ -478,29 +524,39 @@ fn f() { } #[test] - fn does_not_indent_empty_block() { - do_check_noop( + fn indents_block_with_multiple_statements() { + do_check( r#" -fn f() {$0} +fn f() {$0 a = b; ()} + "#, + r#" +fn f() { + $0a = b; () +} "#, ); - do_check_noop( + do_check( r#" -fn f() {{$0}} +fn f() {$0 a = b; a = b; } + "#, + r#" +fn f() { + $0a = b; a = b; +} "#, ); } #[test] - fn does_not_indent_block_with_too_much_content() { - do_check_noop( + fn trims_spaces_around_brace_contents() { + do_check( r#" -fn f() {$0 a = b; ()} +fn f() {$0 () } "#, - ); - do_check_noop( r#" -fn f() {$0 a = b; a = b; } +fn f() { + $0() +} "#, ); } @@ -571,6 +627,20 @@ use { } #[test] + fn indents_item_lists() { + do_check( + r#" +mod m {$0} + "#, + r#" +mod m { + $0 +} + "#, + ); + } + + #[test] fn does_not_indent_use_tree_list_when_not_at_curly_brace() { do_check_noop( r#" |