language: Add context-aware decrease indent for Python (#33370)

Closes #33238, follow-up to
https://github.com/zed-industries/zed/pull/29625.

Changes:

- Removed `significant_indentation`, which was the way to introduce
indentation scoping in languages like Python. However, it turned out to
be unnecessarily complicated to define and maintain.
- Introduced `decrease_indent_patterns`, which takes a `pattern` keyword
to automatically outdent and `valid_after` keywords to treat as valid
code points to snap to. The outdent happens to the most recent
`valid_after` keyword that also has less or equal indentation than the
currently typed keyword.

Fixes:

1. In Python, typing `except`, `finally`, `else`, and so on now
automatically indents intelligently based on the context in which it
appears. For instance:

```py
try:
    if a == 1:
        try:
             b = 2
             ^  # <-- typing "except:" here would indent it to inner try block
```

but,

```py
try:
    if a == 1:
        try:
             b = 2
    ^  # <-- typing "except:" here would indent it to outer try block
```

2. Fixes comments not maintaining indent.

Release Notes:

- Improved auto outdent for Python while typing keywords like `except`,
`else`, `finally`, etc.
- Fixed the issue where comments in Python would not maintain their
indentation.
This commit is contained in:
Smit Barmase 2025-06-26 11:11:03 +05:30 committed by GitHub
parent 1753432406
commit d09c7eb317
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 211 additions and 181 deletions

View file

@ -1395,7 +1395,7 @@ mod tests {
// dedent "else" on the line after a closing paren
append(&mut buffer, "\n else:\n", cx);
assert_eq!(buffer.text(), "if a:\n b(\n )\nelse:\n");
assert_eq!(buffer.text(), "if a:\n b(\n )\nelse:\n ");
buffer
});

View file

@ -28,6 +28,11 @@ brackets = [
auto_indent_using_last_non_empty_line = false
debuggers = ["Debugpy"]
significant_indentation = true
increase_indent_pattern = "^\\s*(try)\\b.*:"
decrease_indent_pattern = "^\\s*(else|elif|except|finally)\\b.*:"
increase_indent_pattern = "^[^#].*:\\s*$"
decrease_indent_patterns = [
{ pattern = "^\\s*elif\\b.*:", valid_after = ["if", "elif"] },
{ pattern = "^\\s*else\\b.*:", valid_after = ["if", "elif", "for", "while", "except"] },
{ pattern = "^\\s*except\\b.*:", valid_after = ["try", "except"] },
{ pattern = "^\\s*finally\\b.*:", valid_after = ["try", "except", "else"] },
{ pattern = "^\\s*case\\b.*:", valid_after = ["match", "case"] }
]

View file

@ -1,72 +1,17 @@
(_ "(" ")" @end) @indent
(_ "[" "]" @end) @indent
(_ "{" "}" @end) @indent
(_ "(" ")" @end) @indent
(function_definition
":" @start
body: (block) @indent
)
(if_statement
":" @start
consequence: (block) @indent
alternative: (_)? @outdent
)
(else_clause
":" @start
body: (block) @indent
)
(elif_clause
":" @start
consequence: (block) @indent
)
(for_statement
":" @start
body: (block) @indent
)
(with_statement
":" @start
body: (block) @indent
)
(while_statement
":" @start
body: (block) @indent
)
(match_statement
":" @start
body: (block) @indent
)
(class_definition
":" @start
body: (block) @indent
)
(case_clause
":" @start
consequence: (block) @indent
)
(try_statement
":" @start
body: (block) @indent
(except_clause)? @outdent
(else_clause)? @outdent
(finally_clause)? @outdent
)
(except_clause
":" @start
(block) @indent
)
(finally_clause
":" @start
(block) @indent
)
(function_definition) @start.def
(class_definition) @start.class
(if_statement) @start.if
(for_statement) @start.for
(while_statement) @start.while
(with_statement) @start.with
(match_statement) @start.match
(try_statement) @start.try
(elif_clause) @start.elif
(else_clause) @start.else
(except_clause) @start.except
(finally_clause) @start.finally
(case_pattern) @start.case