@@ -89,6 +89,8 @@ pub fn indent_edit(doc: &Document, line: usize) -> anyhow::Result<Option<Vec<Ark
89
89
( brace_parent_indent ( parent) , config. indent_size )
90
90
} ;
91
91
92
+ let ( old_indent, old_indent_byte) = line_indent ( text, line, config) ;
93
+
92
94
// Structured in two stages as in Emacs TS rules: first match, then
93
95
// return anchor and indent size. We can add more rules here as needed.
94
96
let ( anchor, indent) = match bol_parent {
@@ -109,11 +111,42 @@ pub fn indent_edit(doc: &Document, line: usize) -> anyhow::Result<Option<Vec<Ark
109
111
110
112
( node_line_indent ( anchor) , config. indent_size )
111
113
} ,
112
- _ => return Ok ( None ) ,
114
+ _ => {
115
+ // Find nearest containing braced expression or top-level node. We'll use
116
+ // that to prevent ever indenting past these in unhandled cases for which we
117
+ // don't have rules yet: https://github.com/posit-dev/positron/issues/1683
118
+
119
+ // First climb one level if cursor is in front of a `{` character.
120
+ // In that case `node` is the `{` token which is an immediate child
121
+ // of the containing `{` expression. We want to indent that braced
122
+ // expression relative to the next enclosing `{` expression.
123
+ let mut node = node;
124
+ if let Some ( c) = text_at_indent ( ) . next ( ) {
125
+ if c == '{' {
126
+ if let Some ( parent) = node. parent ( ) {
127
+ node = parent;
128
+ }
129
+ }
130
+ }
131
+
132
+ // Find nearest enclosing brace. If there is none, just use current indentation.
133
+ let Some ( enclosing_brace) = find_enclosing_brace ( node) else {
134
+ return Ok ( None ) ;
135
+ } ;
136
+ let ( anchor, indent) = brace_indent ( enclosing_brace) ;
137
+
138
+ // Only correct if we're too far on the left, past the indentation
139
+ // implied by the enclosing brace
140
+ let min_indent = anchor + indent;
141
+ if old_indent >= min_indent {
142
+ return Ok ( None ) ;
143
+ }
144
+
145
+ ( anchor, indent)
146
+ } ,
113
147
} ;
114
148
115
149
let new_indent = anchor + indent;
116
- let ( old_indent, old_indent_byte) = line_indent ( text, line, config) ;
117
150
118
151
if old_indent == new_indent {
119
152
return Ok ( None ) ;
@@ -204,6 +237,15 @@ pub fn new_line_indent(config: &IndentationConfig, indent: usize) -> String {
204
237
}
205
238
}
206
239
240
+ /// Find the nearest node that is a braced expression
241
+ pub fn find_enclosing_brace ( node : tree_sitter:: Node ) -> Option < tree_sitter:: Node > {
242
+ if let Some ( parent) = node. parent ( ) {
243
+ parent. ancestors ( ) . find ( |n| n. is_braced_expression ( ) )
244
+ } else {
245
+ None
246
+ }
247
+ }
248
+
207
249
#[ cfg( test) ]
208
250
mod tests {
209
251
use stdext:: assert_match;
@@ -406,6 +448,36 @@ mod tests {
406
448
assert_eq ! ( text, String :: from( "function(\n ) {\n \n }" ) ) ;
407
449
}
408
450
451
+ #[ test]
452
+ fn test_line_indent_minimum ( ) {
453
+ // https://github.com/posit-dev/positron/issues/1683
454
+ let mut text = String :: from ( "function() {\n ({\n }\n )\n }" ) ;
455
+ let doc = test_doc ( & text) ;
456
+
457
+ let edit = indent_edit ( & doc, 3 ) . unwrap ( ) . unwrap ( ) ;
458
+ apply_text_edits ( edit, & mut text) . unwrap ( ) ;
459
+ assert_eq ! ( text, String :: from( "function() {\n ({\n }\n )\n }" ) ) ;
460
+ }
461
+
462
+ #[ test]
463
+ fn test_line_indent_minimum_nested ( ) {
464
+ // Nested R function test with multiple levels of nesting
465
+ let mut text = String :: from ( "{\n {\n ({\n }\n )\n }\n }" ) ;
466
+ let doc = test_doc ( & text) ;
467
+
468
+ let edit = indent_edit ( & doc, 4 ) . unwrap ( ) . unwrap ( ) ;
469
+ apply_text_edits ( edit, & mut text) . unwrap ( ) ;
470
+ assert_eq ! ( text, String :: from( "{\n {\n ({\n }\n )\n }\n }" ) ) ;
471
+ }
472
+
473
+ #[ test]
474
+ fn test_line_indent_function_opening_brace_own_line ( ) {
475
+ let text = String :: from ( "object <- function()\n {\n body\n }" ) ;
476
+ let doc = test_doc ( & text) ;
477
+
478
+ assert_match ! ( indent_edit( & doc, 1 ) . unwrap( ) , None ) ;
479
+ }
480
+
409
481
#[ test]
410
482
fn test_new_line_indent ( ) {
411
483
let tab_cfg = IndentationConfig {
0 commit comments