Indent statements under C/C++ case labels (#60758)

# Objective

Fixes #60261

Nothing under a `case` gets indented in C or C++, and the next `case`
doesn't come back out. It stays flat until you hit `editor: format`.

## Solution

The indent queries had no rule for case labels. Added the same pair
`cpp/indents.scm` uses for `access_specifier`: open an indent range at
the label, close it at the switch's `}`, outdent on the next label.

The part that got me: `@start` uses the *end* of the node you capture.
Fine for `access_specifier`, which is just `public:`. But a
`case_statement` swallows the statements under it, so capturing the node
starts the range after the last one. Capture the `:` instead. `default:`
is a `case_statement` too, so it comes along free.

Only affects what happens after a label, so existing `case` alignment
doesn't move.

## Testing

`test_c_autoindent_switch_case` and `test_cpp_autoindent_switch_case`
cover a plain case, fall-through labels and `default`. Both fail on
main. The `access_specifier` tests still pass.

Not mine, but worth knowing: `typescript::tests::test_outline` and
`test_generator_function_outline` are flaky on main, failing about half
my full `cargo test -p languages --lib` runs with or without this
change.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Fixed statements under a `case` label not being indented in C and C++
([#60261](https://github.com/zed-industries/zed/issues/60261)).
This commit is contained in:
Apoorva Verma authored and GitHub committed 2026-07-12 23:02:26 +00:00
1 parent 23bb2fc135
commit aeeacf5439
4 files changed
+146

No files matched your search

+9
View File
@@ -17,6 +17,15 @@
"{"
"}" @end) @indent
(compound_statement
(case_statement
":" @start)
"}" @end) @indent
(compound_statement
(case_statement)
(case_statement) @outdent)
(_
"("
")" @end) @indent
+9
View File
@@ -25,6 +25,15 @@
(access_specifier)
(access_specifier) @outdent)
(compound_statement
(case_statement
":" @start)
"}" @end) @indent
(compound_statement
(case_statement)
(case_statement) @outdent)
(_
"("
")" @end) @indent
+64
View File
@@ -451,6 +451,70 @@ mod tests {
});
}
#[gpui::test]
async fn test_c_autoindent_switch_case(cx: &mut TestAppContext) {
cx.update(|cx| {
let test_settings = SettingsStore::test(cx);
cx.set_global(test_settings);
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings(cx, |s| {
s.project.all_languages.defaults.tab_size = NonZeroU32::new(2);
});
});
});
let language = crate::language("c", tree_sitter_c::LANGUAGE.into());
cx.new(|cx| {
let mut buffer = Buffer::local("", cx).with_language(language, cx);
buffer.edit(
[(
0..0,
r#"
int main() {
switch (a) {
case 1:
b++;
break;
case 2:
case 3:
c++;
break;
default:
d++;
}
}
"#
.unindent(),
)],
Some(AutoindentMode::EachLine),
cx,
);
assert_eq!(
buffer.text(),
r#"
int main() {
switch (a) {
case 1:
b++;
break;
case 2:
case 3:
c++;
break;
default:
d++;
}
}
"#
.unindent(),
"statements under a case label should be indented, and the next label outdented"
);
buffer
});
}
#[gpui::test]
async fn test_c_autoindent_if_else(cx: &mut TestAppContext) {
cx.update(|cx| {
+64
View File
@@ -16,6 +16,70 @@ mod tests {
use std::num::NonZeroU32;
use unindent::Unindent;
#[gpui::test]
async fn test_cpp_autoindent_switch_case(cx: &mut TestAppContext) {
cx.update(|cx| {
let test_settings = SettingsStore::test(cx);
cx.set_global(test_settings);
cx.update_global::<SettingsStore, _>(|store, cx| {
store.update_user_settings(cx, |s| {
s.project.all_languages.defaults.tab_size = NonZeroU32::new(2);
});
});
});
let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
cx.new(|cx| {
let mut buffer = Buffer::local("", cx).with_language(language, cx);
buffer.edit(
[(
0..0,
r#"
int main() {
switch (a) {
case 1:
b++;
break;
case 2:
case 3:
c++;
break;
default:
d++;
}
}
"#
.unindent(),
)],
Some(AutoindentMode::EachLine),
cx,
);
assert_eq!(
buffer.text(),
r#"
int main() {
switch (a) {
case 1:
b++;
break;
case 2:
case 3:
c++;
break;
default:
d++;
}
}
"#
.unindent(),
"statements under a case label should be indented, and the next label outdented"
);
buffer
});
}
#[gpui::test]
async fn test_cpp_autoindent_access_specifier(cx: &mut TestAppContext) {
cx.update(|cx| {