Add option to disable auto indentation (#36259)

Closes https://github.com/zed-industries/zed/issues/11780

While auto indentation is generally nice to have, there are cases where
it is currently just not good enough for some languages (e.g. Haskell)
or users just straight up do not want their editor to auto indent for
them. Hence, this PR adds the possibilty to disable auto indentation for
either all language or on a per-language basis. Manual invocation via
the `editor: auto indent` action will continue to work.

Also takes a similar approach as
https://github.com/zed-industries/zed/pull/31569 to ensure performance
is fine for larger multicursor edits.

Release Notes:

- Added the possibility to configure auto indentation for all languages
and per language. Add `"auto_indent": false"` to your settings or
desired language to disable the feature.
This commit is contained in:
Finn Evers 2025-08-18 14:21:33 +02:00 committed by GitHub
parent 5225844c9e
commit 1add1d042d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 250 additions and 14 deletions

View file

@ -8214,6 +8214,216 @@ async fn test_autoindent(cx: &mut TestAppContext) {
});
}
#[gpui::test]
async fn test_autoindent_disabled(cx: &mut TestAppContext) {
init_test(cx, |settings| settings.defaults.auto_indent = Some(false));
let language = Arc::new(
Language::new(
LanguageConfig {
brackets: BracketPairConfig {
pairs: vec![
BracketPair {
start: "{".to_string(),
end: "}".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: false,
surround: false,
newline: true,
},
],
..Default::default()
},
..Default::default()
},
Some(tree_sitter_rust::LANGUAGE.into()),
)
.with_indents_query(
r#"
(_ "(" ")" @end) @indent
(_ "{" "}" @end) @indent
"#,
)
.unwrap(),
);
let text = "fn a() {}";
let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
let (editor, cx) = cx.add_window_view(|window, cx| build_editor(buffer, window, cx));
editor
.condition::<crate::EditorEvent>(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
.await;
editor.update_in(cx, |editor, window, cx| {
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
s.select_ranges([5..5, 8..8, 9..9])
});
editor.newline(&Newline, window, cx);
assert_eq!(
editor.text(cx),
indoc!(
"
fn a(
) {
}
"
)
);
assert_eq!(
editor.selections.ranges(cx),
&[
Point::new(1, 0)..Point::new(1, 0),
Point::new(3, 0)..Point::new(3, 0),
Point::new(5, 0)..Point::new(5, 0)
]
);
});
}
#[gpui::test]
async fn test_autoindent_disabled_with_nested_language(cx: &mut TestAppContext) {
init_test(cx, |settings| {
settings.defaults.auto_indent = Some(true);
settings.languages.0.insert(
"python".into(),
LanguageSettingsContent {
auto_indent: Some(false),
..Default::default()
},
);
});
let mut cx = EditorTestContext::new(cx).await;
let injected_language = Arc::new(
Language::new(
LanguageConfig {
brackets: BracketPairConfig {
pairs: vec![
BracketPair {
start: "{".to_string(),
end: "}".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: true,
surround: false,
newline: true,
},
],
..Default::default()
},
name: "python".into(),
..Default::default()
},
Some(tree_sitter_python::LANGUAGE.into()),
)
.with_indents_query(
r#"
(_ "(" ")" @end) @indent
(_ "{" "}" @end) @indent
"#,
)
.unwrap(),
);
let language = Arc::new(
Language::new(
LanguageConfig {
brackets: BracketPairConfig {
pairs: vec![
BracketPair {
start: "{".to_string(),
end: "}".to_string(),
close: false,
surround: false,
newline: true,
},
BracketPair {
start: "(".to_string(),
end: ")".to_string(),
close: true,
surround: false,
newline: true,
},
],
..Default::default()
},
name: LanguageName::new("rust"),
..Default::default()
},
Some(tree_sitter_rust::LANGUAGE.into()),
)
.with_indents_query(
r#"
(_ "(" ")" @end) @indent
(_ "{" "}" @end) @indent
"#,
)
.unwrap()
.with_injection_query(
r#"
(macro_invocation
macro: (identifier) @_macro_name
(token_tree) @injection.content
(#set! injection.language "python"))
"#,
)
.unwrap(),
);
cx.language_registry().add(injected_language);
cx.language_registry().add(language.clone());
cx.update_buffer(|buffer, cx| {
buffer.set_language(Some(language), cx);
});
cx.set_state(&r#"struct A {ˇ}"#);
cx.update_editor(|editor, window, cx| {
editor.newline(&Default::default(), window, cx);
});
cx.assert_editor_state(indoc!(
"struct A {
ˇ
}"
));
cx.set_state(&r#"select_biased!(ˇ)"#);
cx.update_editor(|editor, window, cx| {
editor.newline(&Default::default(), window, cx);
editor.handle_input("def ", window, cx);
editor.handle_input("(", window, cx);
editor.newline(&Default::default(), window, cx);
editor.handle_input("a", window, cx);
});
cx.assert_editor_state(indoc!(
"select_biased!(
def (
)
)"
));
}
#[gpui::test]
async fn test_autoindent_selections(cx: &mut TestAppContext) {
init_test(cx, |_| {});