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:
parent
1753432406
commit
d09c7eb317
6 changed files with 211 additions and 181 deletions
|
@ -21771,9 +21771,9 @@ async fn test_tab_in_leading_whitespace_auto_indents_for_python(cx: &mut TestApp
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
def main():
|
def main():
|
||||||
ˇ try:
|
ˇ try:
|
||||||
ˇ fetch()
|
ˇ fetch()
|
||||||
ˇ except ValueError:
|
ˇ except ValueError:
|
||||||
ˇ handle_error()
|
ˇ handle_error()
|
||||||
ˇ else:
|
ˇ else:
|
||||||
ˇ match value:
|
ˇ match value:
|
||||||
ˇ case _:
|
ˇ case _:
|
||||||
|
@ -21901,74 +21901,101 @@ async fn test_outdent_after_input_for_python(cx: &mut TestAppContext) {
|
||||||
finally:ˇ
|
finally:ˇ
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// TODO: test `except` auto outdents when typed inside `try` block right after for block
|
// test `else` does not outdents when typed inside `except` block right after for block
|
||||||
// cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
// def main():
|
def main():
|
||||||
// try:
|
try:
|
||||||
// for i in range(n):
|
i = 2
|
||||||
// pass
|
except:
|
||||||
// ˇ
|
for i in range(n):
|
||||||
// "});
|
pass
|
||||||
// cx.update_editor(|editor, window, cx| {
|
ˇ
|
||||||
// editor.handle_input("except:", window, cx);
|
"});
|
||||||
// });
|
cx.update_editor(|editor, window, cx| {
|
||||||
// cx.assert_editor_state(indoc! {"
|
editor.handle_input("else:", window, cx);
|
||||||
// def main():
|
});
|
||||||
// try:
|
cx.assert_editor_state(indoc! {"
|
||||||
// for i in range(n):
|
def main():
|
||||||
// pass
|
try:
|
||||||
// except:ˇ
|
i = 2
|
||||||
// "});
|
except:
|
||||||
|
for i in range(n):
|
||||||
|
pass
|
||||||
|
else:ˇ
|
||||||
|
"});
|
||||||
|
|
||||||
// TODO: test `else` auto outdents when typed inside `except` block right after for block
|
// test `finally` auto outdents when typed inside `else` block right after for block
|
||||||
// cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
// def main():
|
def main():
|
||||||
// try:
|
try:
|
||||||
// i = 2
|
i = 2
|
||||||
// except:
|
except:
|
||||||
// for i in range(n):
|
j = 2
|
||||||
// pass
|
else:
|
||||||
// ˇ
|
for i in range(n):
|
||||||
// "});
|
pass
|
||||||
// cx.update_editor(|editor, window, cx| {
|
ˇ
|
||||||
// editor.handle_input("else:", window, cx);
|
"});
|
||||||
// });
|
cx.update_editor(|editor, window, cx| {
|
||||||
// cx.assert_editor_state(indoc! {"
|
editor.handle_input("finally:", window, cx);
|
||||||
// def main():
|
});
|
||||||
// try:
|
cx.assert_editor_state(indoc! {"
|
||||||
// i = 2
|
def main():
|
||||||
// except:
|
try:
|
||||||
// for i in range(n):
|
i = 2
|
||||||
// pass
|
except:
|
||||||
// else:ˇ
|
j = 2
|
||||||
// "});
|
else:
|
||||||
|
for i in range(n):
|
||||||
|
pass
|
||||||
|
finally:ˇ
|
||||||
|
"});
|
||||||
|
|
||||||
// TODO: test `finally` auto outdents when typed inside `else` block right after for block
|
// test `except` outdents to inner "try" block
|
||||||
// cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
// def main():
|
def main():
|
||||||
// try:
|
try:
|
||||||
// i = 2
|
i = 2
|
||||||
// except:
|
if i == 2:
|
||||||
// j = 2
|
try:
|
||||||
// else:
|
i = 3
|
||||||
// for i in range(n):
|
ˇ
|
||||||
// pass
|
"});
|
||||||
// ˇ
|
cx.update_editor(|editor, window, cx| {
|
||||||
// "});
|
editor.handle_input("except:", window, cx);
|
||||||
// cx.update_editor(|editor, window, cx| {
|
});
|
||||||
// editor.handle_input("finally:", window, cx);
|
cx.assert_editor_state(indoc! {"
|
||||||
// });
|
def main():
|
||||||
// cx.assert_editor_state(indoc! {"
|
try:
|
||||||
// def main():
|
i = 2
|
||||||
// try:
|
if i == 2:
|
||||||
// i = 2
|
try:
|
||||||
// except:
|
i = 3
|
||||||
// j = 2
|
except:ˇ
|
||||||
// else:
|
"});
|
||||||
// for i in range(n):
|
|
||||||
// pass
|
// test `except` outdents to outer "try" block
|
||||||
// finally:ˇ
|
cx.set_state(indoc! {"
|
||||||
// "});
|
def main():
|
||||||
|
try:
|
||||||
|
i = 2
|
||||||
|
if i == 2:
|
||||||
|
try:
|
||||||
|
i = 3
|
||||||
|
ˇ
|
||||||
|
"});
|
||||||
|
cx.update_editor(|editor, window, cx| {
|
||||||
|
editor.handle_input("except:", window, cx);
|
||||||
|
});
|
||||||
|
cx.assert_editor_state(indoc! {"
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
i = 2
|
||||||
|
if i == 2:
|
||||||
|
try:
|
||||||
|
i = 3
|
||||||
|
except:ˇ
|
||||||
|
"});
|
||||||
|
|
||||||
// test `else` stays at correct indent when typed after `for` block
|
// test `else` stays at correct indent when typed after `for` block
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
|
|
|
@ -2913,7 +2913,12 @@ impl BufferSnapshot {
|
||||||
) -> Option<impl Iterator<Item = Option<IndentSuggestion>> + '_> {
|
) -> Option<impl Iterator<Item = Option<IndentSuggestion>> + '_> {
|
||||||
let config = &self.language.as_ref()?.config;
|
let config = &self.language.as_ref()?.config;
|
||||||
let prev_non_blank_row = self.prev_non_blank_row(row_range.start);
|
let prev_non_blank_row = self.prev_non_blank_row(row_range.start);
|
||||||
let significant_indentation = config.significant_indentation;
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct StartPosition {
|
||||||
|
start: Point,
|
||||||
|
suffix: SharedString,
|
||||||
|
}
|
||||||
|
|
||||||
// Find the suggested indentation ranges based on the syntax tree.
|
// Find the suggested indentation ranges based on the syntax tree.
|
||||||
let start = Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0);
|
let start = Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0);
|
||||||
|
@ -2929,13 +2934,13 @@ impl BufferSnapshot {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut indent_ranges = Vec::<Range<Point>>::new();
|
let mut indent_ranges = Vec::<Range<Point>>::new();
|
||||||
|
let mut start_positions = Vec::<StartPosition>::new();
|
||||||
let mut outdent_positions = Vec::<Point>::new();
|
let mut outdent_positions = Vec::<Point>::new();
|
||||||
while let Some(mat) = matches.peek() {
|
while let Some(mat) = matches.peek() {
|
||||||
let mut start: Option<Point> = None;
|
let mut start: Option<Point> = None;
|
||||||
let mut end: Option<Point> = None;
|
let mut end: Option<Point> = None;
|
||||||
let mut outdent: Option<Point> = None;
|
|
||||||
|
|
||||||
let config = &indent_configs[mat.grammar_index];
|
let config = indent_configs[mat.grammar_index];
|
||||||
for capture in mat.captures {
|
for capture in mat.captures {
|
||||||
if capture.index == config.indent_capture_ix {
|
if capture.index == config.indent_capture_ix {
|
||||||
start.get_or_insert(Point::from_ts_point(capture.node.start_position()));
|
start.get_or_insert(Point::from_ts_point(capture.node.start_position()));
|
||||||
|
@ -2945,21 +2950,18 @@ impl BufferSnapshot {
|
||||||
} else if Some(capture.index) == config.end_capture_ix {
|
} else if Some(capture.index) == config.end_capture_ix {
|
||||||
end = Some(Point::from_ts_point(capture.node.start_position()));
|
end = Some(Point::from_ts_point(capture.node.start_position()));
|
||||||
} else if Some(capture.index) == config.outdent_capture_ix {
|
} else if Some(capture.index) == config.outdent_capture_ix {
|
||||||
let point = Point::from_ts_point(capture.node.start_position());
|
outdent_positions.push(Point::from_ts_point(capture.node.start_position()));
|
||||||
outdent.get_or_insert(point);
|
} else if let Some(suffix) = config.suffixed_start_captures.get(&capture.index) {
|
||||||
outdent_positions.push(point);
|
start_positions.push(StartPosition {
|
||||||
|
start: Point::from_ts_point(capture.node.start_position()),
|
||||||
|
suffix: suffix.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
matches.advance();
|
matches.advance();
|
||||||
// in case of significant indentation expand end to outdent position
|
|
||||||
let end = if significant_indentation {
|
|
||||||
outdent.or(end)
|
|
||||||
} else {
|
|
||||||
end
|
|
||||||
};
|
|
||||||
if let Some((start, end)) = start.zip(end) {
|
if let Some((start, end)) = start.zip(end) {
|
||||||
if start.row == end.row && (!significant_indentation || start.column < end.column) {
|
if start.row == end.row {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let range = start..end;
|
let range = start..end;
|
||||||
|
@ -2997,24 +2999,26 @@ impl BufferSnapshot {
|
||||||
matches.advance();
|
matches.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't use outdent positions to truncate in case of significant indentation
|
outdent_positions.sort();
|
||||||
// rather we use them to expand (handled above)
|
for outdent_position in outdent_positions {
|
||||||
if !significant_indentation {
|
// find the innermost indent range containing this outdent_position
|
||||||
outdent_positions.sort();
|
// set its end to the outdent position
|
||||||
for outdent_position in outdent_positions {
|
if let Some(range_to_truncate) = indent_ranges
|
||||||
// find the innermost indent range containing this outdent_position
|
.iter_mut()
|
||||||
// set its end to the outdent position
|
.filter(|indent_range| indent_range.contains(&outdent_position))
|
||||||
if let Some(range_to_truncate) = indent_ranges
|
.next_back()
|
||||||
.iter_mut()
|
{
|
||||||
.filter(|indent_range| indent_range.contains(&outdent_position))
|
range_to_truncate.end = outdent_position;
|
||||||
.next_back()
|
|
||||||
{
|
|
||||||
range_to_truncate.end = outdent_position;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start_positions.sort_by_key(|b| b.start);
|
||||||
|
|
||||||
// Find the suggested indentation increases and decreased based on regexes.
|
// Find the suggested indentation increases and decreased based on regexes.
|
||||||
|
let mut regex_outdent_map = HashMap::default();
|
||||||
|
let mut last_seen_suffix: HashMap<String, Vec<Point>> = HashMap::default();
|
||||||
|
let mut start_positions_iter = start_positions.iter().peekable();
|
||||||
|
|
||||||
let mut indent_change_rows = Vec::<(u32, Ordering)>::new();
|
let mut indent_change_rows = Vec::<(u32, Ordering)>::new();
|
||||||
self.for_each_line(
|
self.for_each_line(
|
||||||
Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0)
|
Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0)
|
||||||
|
@ -3034,6 +3038,33 @@ impl BufferSnapshot {
|
||||||
{
|
{
|
||||||
indent_change_rows.push((row + 1, Ordering::Greater));
|
indent_change_rows.push((row + 1, Ordering::Greater));
|
||||||
}
|
}
|
||||||
|
while let Some(pos) = start_positions_iter.peek() {
|
||||||
|
if pos.start.row < row {
|
||||||
|
let pos = start_positions_iter.next().unwrap();
|
||||||
|
last_seen_suffix
|
||||||
|
.entry(pos.suffix.to_string())
|
||||||
|
.or_default()
|
||||||
|
.push(pos.start);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for rule in &config.decrease_indent_patterns {
|
||||||
|
if rule.pattern.as_ref().map_or(false, |r| r.is_match(line)) {
|
||||||
|
let row_start_column = self.indent_size_for_line(row).len;
|
||||||
|
let basis_row = rule
|
||||||
|
.valid_after
|
||||||
|
.iter()
|
||||||
|
.filter_map(|valid_suffix| last_seen_suffix.get(valid_suffix))
|
||||||
|
.flatten()
|
||||||
|
.filter(|start_point| start_point.column <= row_start_column)
|
||||||
|
.max_by_key(|start_point| start_point.row);
|
||||||
|
if let Some(outdent_to_row) = basis_row {
|
||||||
|
regex_outdent_map.insert(row, outdent_to_row.row);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3043,6 +3074,7 @@ impl BufferSnapshot {
|
||||||
} else {
|
} else {
|
||||||
row_range.start.saturating_sub(1)
|
row_range.start.saturating_sub(1)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut prev_row_start = Point::new(prev_row, self.indent_size_for_line(prev_row).len);
|
let mut prev_row_start = Point::new(prev_row, self.indent_size_for_line(prev_row).len);
|
||||||
Some(row_range.map(move |row| {
|
Some(row_range.map(move |row| {
|
||||||
let row_start = Point::new(row, self.indent_size_for_line(row).len);
|
let row_start = Point::new(row, self.indent_size_for_line(row).len);
|
||||||
|
@ -3080,17 +3112,17 @@ impl BufferSnapshot {
|
||||||
if range.start.row == prev_row && range.end > row_start {
|
if range.start.row == prev_row && range.end > row_start {
|
||||||
indent_from_prev_row = true;
|
indent_from_prev_row = true;
|
||||||
}
|
}
|
||||||
if significant_indentation && self.is_line_blank(row) && range.start.row == prev_row
|
if range.end > prev_row_start && range.end <= row_start {
|
||||||
{
|
outdent_to_row = outdent_to_row.min(range.start.row);
|
||||||
indent_from_prev_row = true;
|
|
||||||
}
|
|
||||||
if !significant_indentation || !self.is_line_blank(row) {
|
|
||||||
if range.end > prev_row_start && range.end <= row_start {
|
|
||||||
outdent_to_row = outdent_to_row.min(range.start.row);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(basis_row) = regex_outdent_map.get(&row) {
|
||||||
|
indent_from_prev_row = false;
|
||||||
|
outdent_to_row = *basis_row;
|
||||||
|
from_regex = true;
|
||||||
|
}
|
||||||
|
|
||||||
let within_error = error_ranges
|
let within_error = error_ranges
|
||||||
.iter()
|
.iter()
|
||||||
.any(|e| e.start.row < row && e.end > row_start);
|
.any(|e| e.start.row < row && e.end > row_start);
|
||||||
|
|
|
@ -696,10 +696,6 @@ pub struct LanguageConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[schemars(schema_with = "bracket_pair_config_json_schema")]
|
#[schemars(schema_with = "bracket_pair_config_json_schema")]
|
||||||
pub brackets: BracketPairConfig,
|
pub brackets: BracketPairConfig,
|
||||||
/// If set to true, indicates the language uses significant whitespace/indentation
|
|
||||||
/// for syntax structure (like Python) rather than brackets/braces for code blocks.
|
|
||||||
#[serde(default)]
|
|
||||||
pub significant_indentation: bool,
|
|
||||||
/// If set to true, auto indentation uses last non empty line to determine
|
/// If set to true, auto indentation uses last non empty line to determine
|
||||||
/// the indentation level for a new line.
|
/// the indentation level for a new line.
|
||||||
#[serde(default = "auto_indent_using_last_non_empty_line_default")]
|
#[serde(default = "auto_indent_using_last_non_empty_line_default")]
|
||||||
|
@ -717,6 +713,12 @@ pub struct LanguageConfig {
|
||||||
#[serde(default, deserialize_with = "deserialize_regex")]
|
#[serde(default, deserialize_with = "deserialize_regex")]
|
||||||
#[schemars(schema_with = "regex_json_schema")]
|
#[schemars(schema_with = "regex_json_schema")]
|
||||||
pub decrease_indent_pattern: Option<Regex>,
|
pub decrease_indent_pattern: Option<Regex>,
|
||||||
|
/// A list of rules for decreasing indentation. Each rule pairs a regex with a set of valid
|
||||||
|
/// "block-starting" tokens. When a line matches a pattern, its indentation is aligned with
|
||||||
|
/// the most recent line that began with a corresponding token. This enables context-aware
|
||||||
|
/// outdenting, like aligning an `else` with its `if`.
|
||||||
|
#[serde(default)]
|
||||||
|
pub decrease_indent_patterns: Vec<DecreaseIndentConfig>,
|
||||||
/// A list of characters that trigger the automatic insertion of a closing
|
/// A list of characters that trigger the automatic insertion of a closing
|
||||||
/// bracket when they immediately precede the point where an opening
|
/// bracket when they immediately precede the point where an opening
|
||||||
/// bracket is inserted.
|
/// bracket is inserted.
|
||||||
|
@ -776,6 +778,15 @@ pub struct LanguageConfig {
|
||||||
pub documentation: Option<DocumentationConfig>,
|
pub documentation: Option<DocumentationConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
|
||||||
|
pub struct DecreaseIndentConfig {
|
||||||
|
#[serde(default, deserialize_with = "deserialize_regex")]
|
||||||
|
#[schemars(schema_with = "regex_json_schema")]
|
||||||
|
pub pattern: Option<Regex>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub valid_after: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
|
||||||
pub struct LanguageMatcher {
|
pub struct LanguageMatcher {
|
||||||
/// Given a list of `LanguageConfig`'s, the language of a file can be determined based on the path extension matching any of the `path_suffixes`.
|
/// Given a list of `LanguageConfig`'s, the language of a file can be determined based on the path extension matching any of the `path_suffixes`.
|
||||||
|
@ -899,6 +910,7 @@ impl Default for LanguageConfig {
|
||||||
auto_indent_on_paste: None,
|
auto_indent_on_paste: None,
|
||||||
increase_indent_pattern: Default::default(),
|
increase_indent_pattern: Default::default(),
|
||||||
decrease_indent_pattern: Default::default(),
|
decrease_indent_pattern: Default::default(),
|
||||||
|
decrease_indent_patterns: Default::default(),
|
||||||
autoclose_before: Default::default(),
|
autoclose_before: Default::default(),
|
||||||
line_comments: Default::default(),
|
line_comments: Default::default(),
|
||||||
block_comment: Default::default(),
|
block_comment: Default::default(),
|
||||||
|
@ -914,7 +926,6 @@ impl Default for LanguageConfig {
|
||||||
jsx_tag_auto_close: None,
|
jsx_tag_auto_close: None,
|
||||||
completion_query_characters: Default::default(),
|
completion_query_characters: Default::default(),
|
||||||
debuggers: Default::default(),
|
debuggers: Default::default(),
|
||||||
significant_indentation: Default::default(),
|
|
||||||
documentation: None,
|
documentation: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1092,6 +1103,7 @@ struct IndentConfig {
|
||||||
start_capture_ix: Option<u32>,
|
start_capture_ix: Option<u32>,
|
||||||
end_capture_ix: Option<u32>,
|
end_capture_ix: Option<u32>,
|
||||||
outdent_capture_ix: Option<u32>,
|
outdent_capture_ix: Option<u32>,
|
||||||
|
suffixed_start_captures: HashMap<u32, SharedString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OutlineConfig {
|
pub struct OutlineConfig {
|
||||||
|
@ -1522,6 +1534,14 @@ impl Language {
|
||||||
("outdent", &mut outdent_capture_ix),
|
("outdent", &mut outdent_capture_ix),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut suffixed_start_captures = HashMap::default();
|
||||||
|
for (ix, name) in query.capture_names().iter().enumerate() {
|
||||||
|
if let Some(suffix) = name.strip_prefix("start.") {
|
||||||
|
suffixed_start_captures.insert(ix as u32, suffix.to_owned().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(indent_capture_ix) = indent_capture_ix {
|
if let Some(indent_capture_ix) = indent_capture_ix {
|
||||||
grammar.indents_config = Some(IndentConfig {
|
grammar.indents_config = Some(IndentConfig {
|
||||||
query,
|
query,
|
||||||
|
@ -1529,6 +1549,7 @@ impl Language {
|
||||||
start_capture_ix,
|
start_capture_ix,
|
||||||
end_capture_ix,
|
end_capture_ix,
|
||||||
outdent_capture_ix,
|
outdent_capture_ix,
|
||||||
|
suffixed_start_captures,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(self)
|
Ok(self)
|
||||||
|
|
|
@ -1395,7 +1395,7 @@ mod tests {
|
||||||
|
|
||||||
// dedent "else" on the line after a closing paren
|
// dedent "else" on the line after a closing paren
|
||||||
append(&mut buffer, "\n else:\n", cx);
|
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
|
buffer
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,6 +28,11 @@ brackets = [
|
||||||
|
|
||||||
auto_indent_using_last_non_empty_line = false
|
auto_indent_using_last_non_empty_line = false
|
||||||
debuggers = ["Debugpy"]
|
debuggers = ["Debugpy"]
|
||||||
significant_indentation = true
|
increase_indent_pattern = "^[^#].*:\\s*$"
|
||||||
increase_indent_pattern = "^\\s*(try)\\b.*:"
|
decrease_indent_patterns = [
|
||||||
decrease_indent_pattern = "^\\s*(else|elif|except|finally)\\b.*:"
|
{ 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"] }
|
||||||
|
]
|
||||||
|
|
|
@ -1,72 +1,17 @@
|
||||||
(_ "(" ")" @end) @indent
|
|
||||||
(_ "[" "]" @end) @indent
|
(_ "[" "]" @end) @indent
|
||||||
(_ "{" "}" @end) @indent
|
(_ "{" "}" @end) @indent
|
||||||
|
(_ "(" ")" @end) @indent
|
||||||
|
|
||||||
(function_definition
|
(function_definition) @start.def
|
||||||
":" @start
|
(class_definition) @start.class
|
||||||
body: (block) @indent
|
(if_statement) @start.if
|
||||||
)
|
(for_statement) @start.for
|
||||||
|
(while_statement) @start.while
|
||||||
(if_statement
|
(with_statement) @start.with
|
||||||
":" @start
|
(match_statement) @start.match
|
||||||
consequence: (block) @indent
|
(try_statement) @start.try
|
||||||
alternative: (_)? @outdent
|
(elif_clause) @start.elif
|
||||||
)
|
(else_clause) @start.else
|
||||||
|
(except_clause) @start.except
|
||||||
(else_clause
|
(finally_clause) @start.finally
|
||||||
":" @start
|
(case_pattern) @start.case
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue