language: Improve auto-indentation when using round brackets in Python (#31260)

Finn Evers created

Follow-up to #29625 and #30902

This PR reintroduces auto-intents for brackets in Python and fixes some
cases where an indentation would be triggered if it should not. For
example, upon typing

```python
a = []
```
and inserting a newline after, the next line would be indented although
it shoud not be.

Bracket auto-indentation was tested prior to #29625 but removed there
and the test updated accordingly. #30902 reintroduced this for all
brackets but `()`. I reintroduced this here, reverted the changes to the
test so that indents also happen after typing `()`. This is frequently
used for tuples and multiline statements in Python.

Release Notes:

- Improved auto-indentation when using round brackets in Python.

Change summary

crates/editor/src/editor_tests.rs       | 28 ++++++++++++++++++++++++++
crates/language/src/buffer.rs           |  2 
crates/languages/src/python.rs          |  4 +-
crates/languages/src/python/indents.scm |  1 
4 files changed, 31 insertions(+), 4 deletions(-)

Detailed changes

crates/editor/src/editor_tests.rs 🔗

@@ -20860,7 +20860,7 @@ async fn test_indent_on_newline_for_python(cx: &mut TestAppContext) {
         ˇ
     "});
 
-    // test correct indent after newline in curly brackets
+    // test correct indent after newline in brackets
     cx.set_state(indoc! {"
         {ˇ}
     "});
@@ -20873,6 +20873,32 @@ async fn test_indent_on_newline_for_python(cx: &mut TestAppContext) {
             ˇ
         }
     "});
+
+    cx.set_state(indoc! {"
+        (ˇ)
+    "});
+    cx.update_editor(|editor, window, cx| {
+        editor.newline(&Newline, window, cx);
+    });
+    cx.run_until_parked();
+    cx.assert_editor_state(indoc! {"
+        (
+            ˇ
+        )
+    "});
+
+    // do not indent after empty lists or dictionaries
+    cx.set_state(indoc! {"
+        a = []ˇ
+    "});
+    cx.update_editor(|editor, window, cx| {
+        editor.newline(&Newline, window, cx);
+    });
+    cx.run_until_parked();
+    cx.assert_editor_state(indoc! {"
+        a = []
+        ˇ
+    "});
 }
 
 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {

crates/language/src/buffer.rs 🔗

@@ -2901,7 +2901,7 @@ impl BufferSnapshot {
                 end
             };
             if let Some((start, end)) = start.zip(end) {
-                if start.row == end.row && !significant_indentation {
+                if start.row == end.row && (!significant_indentation || start.column < end.column) {
                     continue;
                 }
                 let range = start..end;

crates/languages/src/python.rs 🔗

@@ -1236,12 +1236,12 @@ mod tests {
                 "def a():\n  \n  if a:\n    b()\n  else:\n    "
             );
 
-            // indent after an open paren. the closing  paren is not indented
+            // indent after an open paren. the closing paren is not indented
             // because there is another token before it on the same line.
             append(&mut buffer, "foo(\n1)", cx);
             assert_eq!(
                 buffer.text(),
-                "def a():\n  \n  if a:\n    b()\n  else:\n    foo(\n    1)"
+                "def a():\n  \n  if a:\n    b()\n  else:\n    foo(\n      1)"
             );
 
             // dedent the closing paren if it is shifted to the beginning of the line