1- //! Handles the `Enter` key press. At the momently, this only continues
2- //! comments, but should handle indent some time in the future as well .
1+ //! Handles the `Enter` key press, including comment continuation and
2+ //! indentation in brace-delimited constructs .
33
44use ide_db:: { FilePosition , RootDatabase } ;
55use syntax:: {
66 AstNode , SmolStr , SourceFile ,
77 SyntaxKind :: * ,
8- SyntaxNode , SyntaxToken , TextRange , TextSize , TokenAtOffset ,
9- algo:: find_node_at_offset,
8+ SyntaxToken , TextRange , TextSize , TokenAtOffset ,
109 ast:: { self , AstToken , edit:: IndentLevel } ,
1110} ;
1211
@@ -19,7 +18,8 @@ use ide_db::text_edit::TextEdit;
1918// - <kbd>Enter</kbd> inside triple-slash comments automatically inserts `///`
2019// - <kbd>Enter</kbd> in the middle or after a trailing space in `//` inserts `//`
2120// - <kbd>Enter</kbd> inside `//!` doc comments automatically inserts `//!`
22- // - <kbd>Enter</kbd> after `{` indents contents and closing `}` of single-line block
21+ // - <kbd>Enter</kbd> after `{` reformats single-line brace-delimited contents by
22+ // moving the text between `{` and the matching `}` onto an indented line
2323//
2424// This action needs to be assigned to shortcut explicitly.
2525//
@@ -59,22 +59,11 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
5959 return on_enter_in_comment ( & comment, & file, position. offset ) ;
6060 }
6161
62- if token. kind ( ) == L_CURLY {
63- // Typing enter after the `{` of a block expression, where the `}` is on the same line
64- if let Some ( edit) = find_node_at_offset ( file. syntax ( ) , position. offset - TextSize :: of ( '{' ) )
65- . and_then ( |block| on_enter_in_block ( block, position) )
66- {
67- cov_mark:: hit!( indent_block_contents) ;
68- return Some ( edit) ;
69- }
70-
71- // Typing enter after the `{` of a use tree list.
72- if let Some ( edit) = find_node_at_offset ( file. syntax ( ) , position. offset - TextSize :: of ( '{' ) )
73- . and_then ( |list| on_enter_in_use_tree_list ( list, position) )
74- {
75- cov_mark:: hit!( indent_block_contents) ;
76- return Some ( edit) ;
77- }
62+ if token. kind ( ) == L_CURLY
63+ && let Some ( edit) = on_enter_in_braces ( token, position)
64+ {
65+ cov_mark:: hit!( indent_block_contents) ;
66+ return Some ( edit) ;
7867 }
7968
8069 None
@@ -119,44 +108,54 @@ fn on_enter_in_comment(
119108 Some ( edit)
120109}
121110
122- fn on_enter_in_block ( block : ast:: BlockExpr , position : FilePosition ) -> Option < TextEdit > {
123- let contents = block_contents ( & block) ?;
124-
125- if block. syntax ( ) . text ( ) . contains_char ( '\n' ) {
126- return None ;
127- }
128-
129- let indent = IndentLevel :: from_node ( block. syntax ( ) ) ;
130- let mut edit = TextEdit :: insert ( position. offset , format ! ( "\n {}$0" , indent + 1 ) ) ;
131- edit. union ( TextEdit :: insert ( contents. text_range ( ) . end ( ) , format ! ( "\n {indent}" ) ) ) . ok ( ) ?;
132- Some ( edit)
133- }
134-
135- fn on_enter_in_use_tree_list ( list : ast:: UseTreeList , position : FilePosition ) -> Option < TextEdit > {
136- if list. syntax ( ) . text ( ) . contains_char ( '\n' ) {
111+ fn on_enter_in_braces ( l_curly : SyntaxToken , position : FilePosition ) -> Option < TextEdit > {
112+ if l_curly. text_range ( ) . end ( ) != position. offset {
137113 return None ;
138114 }
139115
140- let indent = IndentLevel :: from_node ( list. syntax ( ) ) ;
141- let mut edit = TextEdit :: insert ( position. offset , format ! ( "\n {}$0" , indent + 1 ) ) ;
142- edit. union ( TextEdit :: insert ( list. r_curly_token ( ) ?. text_range ( ) . start ( ) , format ! ( "\n {indent}" ) ) )
143- . ok ( ) ?;
144- Some ( edit)
116+ let ( r_curly, content) = brace_contents_on_same_line ( & l_curly) ?;
117+ let indent = IndentLevel :: from_token ( & l_curly) ;
118+ Some ( TextEdit :: replace (
119+ TextRange :: new ( position. offset , r_curly. text_range ( ) . start ( ) ) ,
120+ format ! ( "\n {}$0{}\n {indent}" , indent + 1 , content) ,
121+ ) )
145122}
146123
147- fn block_contents ( block : & ast:: BlockExpr ) -> Option < SyntaxNode > {
148- let mut node = block. tail_expr ( ) . map ( |e| e. syntax ( ) . clone ( ) ) ;
124+ fn brace_contents_on_same_line ( l_curly : & SyntaxToken ) -> Option < ( SyntaxToken , String ) > {
125+ let mut depth = 0_u32 ;
126+ let mut tokens = Vec :: new ( ) ;
127+ let mut token = l_curly. next_token ( ) ?;
149128
150- for stmt in block. statements ( ) {
151- if node. is_some ( ) {
152- // More than 1 node in the block
129+ loop {
130+ if token. kind ( ) == WHITESPACE && token. text ( ) . contains ( '\n' ) {
153131 return None ;
154132 }
155133
156- node = Some ( stmt. syntax ( ) . clone ( ) ) ;
157- }
134+ match token. kind ( ) {
135+ L_CURLY => {
136+ depth += 1 ;
137+ tokens. push ( token. clone ( ) ) ;
138+ }
139+ R_CURLY if depth == 0 => {
140+ let first = tokens. iter ( ) . position ( |it| it. kind ( ) != WHITESPACE ) ;
141+ let last = tokens. iter ( ) . rposition ( |it| it. kind ( ) != WHITESPACE ) ;
142+ let content = match first. zip ( last) {
143+ Some ( ( first, last) ) => {
144+ tokens[ first..=last] . iter ( ) . map ( |it| it. text ( ) ) . collect ( )
145+ }
146+ None => String :: new ( ) ,
147+ } ;
148+ return Some ( ( token, content) ) ;
149+ }
150+ R_CURLY => {
151+ depth -= 1 ;
152+ tokens. push ( token. clone ( ) ) ;
153+ }
154+ _ => tokens. push ( token. clone ( ) ) ,
155+ }
158156
159- node
157+ token = token. next_token ( ) ?;
158+ }
160159}
161160
162161fn followed_by_comment ( comment : & ast:: Comment ) -> bool {
@@ -382,10 +381,58 @@ fn main() {
382381 }
383382
384383 #[ test]
385- fn indents_fn_body_block ( ) {
384+ fn indents_empty_brace_pairs ( ) {
386385 cov_mark:: check!( indent_block_contents) ;
387386 do_check (
388387 r#"
388+ fn f() {$0}
389+ "# ,
390+ r#"
391+ fn f() {
392+ $0
393+ }
394+ "# ,
395+ ) ;
396+ do_check (
397+ r#"
398+ fn f() {
399+ let x = {$0};
400+ }
401+ "# ,
402+ r#"
403+ fn f() {
404+ let x = {
405+ $0
406+ };
407+ }
408+ "# ,
409+ ) ;
410+ do_check (
411+ r#"
412+ use crate::{$0};
413+ "# ,
414+ r#"
415+ use crate::{
416+ $0
417+ };
418+ "# ,
419+ ) ;
420+ do_check (
421+ r#"
422+ mod m {$0}
423+ "# ,
424+ r#"
425+ mod m {
426+ $0
427+ }
428+ "# ,
429+ ) ;
430+ }
431+
432+ #[ test]
433+ fn indents_fn_body_block ( ) {
434+ do_check (
435+ r#"
389436fn f() {$0()}
390437 "# ,
391438 r#"
@@ -477,29 +524,39 @@ fn f() {
477524 }
478525
479526 #[ test]
480- fn does_not_indent_empty_block ( ) {
481- do_check_noop (
527+ fn indents_block_with_multiple_statements ( ) {
528+ do_check (
482529 r#"
483- fn f() {$0}
530+ fn f() {$0 a = b; ()}
531+ "# ,
532+ r#"
533+ fn f() {
534+ $0a = b; ()
535+ }
484536 "# ,
485537 ) ;
486- do_check_noop (
538+ do_check (
487539 r#"
488- fn f() {{$0}}
540+ fn f() {$0 a = b; a = b; }
541+ "# ,
542+ r#"
543+ fn f() {
544+ $0a = b; a = b;
545+ }
489546 "# ,
490547 ) ;
491548 }
492549
493550 #[ test]
494- fn does_not_indent_block_with_too_much_content ( ) {
495- do_check_noop (
551+ fn trims_spaces_around_brace_contents ( ) {
552+ do_check (
496553 r#"
497- fn f() {$0 a = b; () }
554+ fn f() {$0 () }
498555 "# ,
499- ) ;
500- do_check_noop (
501556 r#"
502- fn f() {$0 a = b; a = b; }
557+ fn f() {
558+ $0()
559+ }
503560 "# ,
504561 ) ;
505562 }
@@ -569,6 +626,20 @@ use {
569626 ) ;
570627 }
571628
629+ #[ test]
630+ fn indents_item_lists ( ) {
631+ do_check (
632+ r#"
633+ mod m {$0}
634+ "# ,
635+ r#"
636+ mod m {
637+ $0
638+ }
639+ "# ,
640+ ) ;
641+ }
642+
572643 #[ test]
573644 fn does_not_indent_use_tree_list_when_not_at_curly_brace ( ) {
574645 do_check_noop (
0 commit comments