Detailed changes
@@ -98,6 +98,7 @@ text.workspace = true
theme = { workspace = true, features = ["test-support"] }
tree-sitter-bash.workspace = true
tree-sitter-c.workspace = true
+tree-sitter-cpp.workspace = true
tree-sitter-css.workspace = true
tree-sitter-go.workspace = true
tree-sitter-python.workspace = true
@@ -395,10 +395,10 @@ mod tests {
use language::{AutoindentMode, Buffer};
use settings::SettingsStore;
use std::num::NonZeroU32;
+ use unindent::Unindent;
#[gpui::test]
- async fn test_c_autoindent(cx: &mut TestAppContext) {
- // cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX);
+ async fn test_c_autoindent_basic(cx: &mut TestAppContext) {
cx.update(|cx| {
let test_settings = SettingsStore::test(cx);
cx.set_global(test_settings);
@@ -413,23 +413,230 @@ mod tests {
cx.new(|cx| {
let mut buffer = Buffer::local("", cx).with_language(language, cx);
- // empty function
buffer.edit([(0..0, "int main() {}")], None, cx);
- // indent inside braces
let ix = buffer.len() - 1;
buffer.edit([(ix..ix, "\n\n")], Some(AutoindentMode::EachLine), cx);
- assert_eq!(buffer.text(), "int main() {\n \n}");
+ assert_eq!(
+ buffer.text(),
+ "int main() {\n \n}",
+ "content inside braces should be indented"
+ );
- // indent body of single-statement if statement
- let ix = buffer.len() - 2;
- buffer.edit([(ix..ix, "if (a)\nb;")], Some(AutoindentMode::EachLine), cx);
- assert_eq!(buffer.text(), "int main() {\n if (a)\n b;\n}");
+ buffer
+ });
+ }
- // indent inside field expression
- let ix = buffer.len() - 3;
+ #[gpui::test]
+ async fn test_c_autoindent_if_else(cx: &mut TestAppContext) {
+ cx.update(|cx| {
+ let test_settings = SettingsStore::test(cx);
+ cx.set_global(test_settings);
+ language::init(cx);
+ 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() {
+ if (a)
+ b;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a)
+ b;
+ }
+ "#
+ .unindent(),
+ "body of if-statement without braces should be indented"
+ );
+
+ let ix = buffer.len() - 4;
buffer.edit([(ix..ix, "\n.c")], Some(AutoindentMode::EachLine), cx);
- assert_eq!(buffer.text(), "int main() {\n if (a)\n b\n .c;\n}");
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a)
+ b
+ .c;
+ }
+ "#
+ .unindent(),
+ "field expression (.c) should be indented further than the statement body"
+ );
+
+ buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
+ buffer.edit(
+ [(
+ 0..0,
+ r#"
+ int main() {
+ if (a) a++;
+ else b++;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a) a++;
+ else b++;
+ }
+ "#
+ .unindent(),
+ "single-line if/else without braces should align at the same level"
+ );
+
+ buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
+ buffer.edit(
+ [(
+ 0..0,
+ r#"
+ int main() {
+ if (a)
+ b++;
+ else
+ c++;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a)
+ b++;
+ else
+ c++;
+ }
+ "#
+ .unindent(),
+ "multi-line if/else without braces should indent statement bodies"
+ );
+
+ buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
+ buffer.edit(
+ [(
+ 0..0,
+ r#"
+ int main() {
+ if (a)
+ if (b)
+ c++;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a)
+ if (b)
+ c++;
+ }
+ "#
+ .unindent(),
+ "nested if statements without braces should indent properly"
+ );
+
+ buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
+ buffer.edit(
+ [(
+ 0..0,
+ r#"
+ int main() {
+ if (a)
+ b++;
+ else if (c)
+ d++;
+ else
+ f++;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a)
+ b++;
+ else if (c)
+ d++;
+ else
+ f++;
+ }
+ "#
+ .unindent(),
+ "else-if chains should align all conditions at same level with indented bodies"
+ );
+
+ buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
+ buffer.edit(
+ [(
+ 0..0,
+ r#"
+ int main() {
+ if (a) {
+ b++;
+ } else
+ c++;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a) {
+ b++;
+ } else
+ c++;
+ }
+ "#
+ .unindent(),
+ "mixed braces should indent properly"
+ );
buffer
});
@@ -4,7 +4,7 @@ path_suffixes = ["c"]
line_comments = ["// "]
decrease_indent_patterns = [
{ pattern = "^\\s*\\{.*\\}?\\s*$", valid_after = ["if", "for", "while", "do", "switch", "else"] },
- { pattern = "^\\s*else\\s*$", valid_after = ["if"] }
+ { pattern = "^\\s*else\\b", valid_after = ["if"] }
]
autoclose_before = ";:.,=}])>"
brackets = [
@@ -0,0 +1,254 @@
+#[cfg(test)]
+mod tests {
+ use gpui::{AppContext as _, BorrowAppContext, TestAppContext};
+ use language::{AutoindentMode, Buffer};
+ use settings::SettingsStore;
+ use std::num::NonZeroU32;
+ use unindent::Unindent;
+
+ #[gpui::test]
+ async fn test_cpp_autoindent_basic(cx: &mut TestAppContext) {
+ cx.update(|cx| {
+ let test_settings = SettingsStore::test(cx);
+ cx.set_global(test_settings);
+ language::init(cx);
+ 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, "int main() {}")], None, cx);
+
+ let ix = buffer.len() - 1;
+ buffer.edit([(ix..ix, "\n\n")], Some(AutoindentMode::EachLine), cx);
+ assert_eq!(
+ buffer.text(),
+ "int main() {\n \n}",
+ "content inside braces should be indented"
+ );
+
+ buffer
+ });
+ }
+
+ #[gpui::test]
+ async fn test_cpp_autoindent_if_else(cx: &mut TestAppContext) {
+ cx.update(|cx| {
+ let test_settings = SettingsStore::test(cx);
+ cx.set_global(test_settings);
+ language::init(cx);
+ 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() {
+ if (a)
+ b;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a)
+ b;
+ }
+ "#
+ .unindent(),
+ "body of if-statement without braces should be indented"
+ );
+
+ let ix = buffer.len() - 4;
+ buffer.edit([(ix..ix, "\n.c")], Some(AutoindentMode::EachLine), cx);
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a)
+ b
+ .c;
+ }
+ "#
+ .unindent(),
+ "field expression (.c) should be indented further than the statement body"
+ );
+
+ buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
+ buffer.edit(
+ [(
+ 0..0,
+ r#"
+ int main() {
+ if (a) a++;
+ else b++;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a) a++;
+ else b++;
+ }
+ "#
+ .unindent(),
+ "single-line if/else without braces should align at the same level"
+ );
+
+ buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
+ buffer.edit(
+ [(
+ 0..0,
+ r#"
+ int main() {
+ if (a)
+ b++;
+ else
+ c++;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a)
+ b++;
+ else
+ c++;
+ }
+ "#
+ .unindent(),
+ "multi-line if/else without braces should indent statement bodies"
+ );
+
+ buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
+ buffer.edit(
+ [(
+ 0..0,
+ r#"
+ int main() {
+ if (a)
+ if (b)
+ c++;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a)
+ if (b)
+ c++;
+ }
+ "#
+ .unindent(),
+ "nested if statements without braces should indent properly"
+ );
+
+ buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
+ buffer.edit(
+ [(
+ 0..0,
+ r#"
+ int main() {
+ if (a)
+ b++;
+ else if (c)
+ d++;
+ else
+ f++;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a)
+ b++;
+ else if (c)
+ d++;
+ else
+ f++;
+ }
+ "#
+ .unindent(),
+ "else-if chains should align all conditions at same level with indented bodies"
+ );
+
+ buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
+ buffer.edit(
+ [(
+ 0..0,
+ r#"
+ int main() {
+ if (a) {
+ b++;
+ } else
+ c++;
+ }
+ "#
+ .unindent(),
+ )],
+ Some(AutoindentMode::EachLine),
+ cx,
+ );
+ assert_eq!(
+ buffer.text(),
+ r#"
+ int main() {
+ if (a) {
+ b++;
+ } else
+ c++;
+ }
+ "#
+ .unindent(),
+ "mixed braces should indent properly"
+ );
+
+ buffer
+ });
+ }
+}
@@ -4,7 +4,7 @@ path_suffixes = ["cc", "hh", "cpp", "h", "hpp", "cxx", "hxx", "c++", "ipp", "inl
line_comments = ["// ", "/// ", "//! "]
decrease_indent_patterns = [
{ pattern = "^\\s*\\{.*\\}?\\s*$", valid_after = ["if", "for", "while", "do", "switch", "else"] },
- { pattern = "^\\s*else\\s*$", valid_after = ["if"] }
+ { pattern = "^\\s*else\\b", valid_after = ["if"] }
]
autoclose_before = ";:.,=}])>"
brackets = [
@@ -19,6 +19,7 @@ use crate::{
mod bash;
mod c;
+mod cpp;
mod css;
mod go;
mod json;