Repository: nix-community/nixpkgs-fmt
Pull request #212 (2020-05-14 10:57:35)

Remove unnecessary predicates for NODE_PAREN rules
@zimbatm @matklad this is the first attempt to remove unnecessary rules for NODE_PAREN. I have add some node like `NODE_LAMBDA` `NODE_APPLY` `NODE_WITH` `NODE_BIN_OP` and `NODE_IF_ELSE` is to minimize some failed test on the current test_data.

That being said, this modified `test_data` still has weird output, but somehow better than if I don't include all the node above.

 

Rizary: Latest commit trying to solve some ugly result in https://github.com/NixOS/nixpkgs/blob/c38398aaa19aaeb5f8f379305c06195fb5cecd8e/lib/deprecated.nix#L89-L112

I just realize couple of thing when refactoring the code that need further discussion.

Overall, the commit try to simplify multiple predicates on each rules and have some syntax changes in the test_data.



Patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..0243fe9
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+  "nixEnvSelector.nixShellConfig": "${workspaceRoot}/shell.nix"
+}
\ No newline at end of file
diff --git a/flake.nix b/flake.nix
index 505403d..9302720 100644
--- a/flake.nix
+++ b/flake.nix
@@ -22,15 +22,13 @@
         forAllSystems (
           system:
           {
-            nixpkgs-fmt = (
-              import nixpkgs {
-                inherit system;
-                overlays = [
-                  naerskOverlay
-                  self.overlay
-                ];
-              }
-            ).nixpkgs-fmt;
+            nixpkgs-fmt = (import nixpkgs {
+              inherit system;
+              overlays = [
+                naerskOverlay
+                self.overlay
+              ];
+            }).nixpkgs-fmt;
           }
         );
 
diff --git a/shell.nix b/shell.nix
index cd8959a..6dee1d0 100644
--- a/shell.nix
+++ b/shell.nix
@@ -6,13 +6,11 @@ let
     darwin
     ;
   rust =
-    (
-      pkgs.rustChannelOf {
-        date = "2019-09-13";
-        channel = "nightly";
-        sha256 = "06881g7ba2hzmfq5vaz888d2q762zf4bxjc621rw3g8z702ps7w9";
-      }
-    ).rust.override {
+    (pkgs.rustChannelOf {
+      date = "2019-09-13";
+      channel = "nightly";
+      sha256 = "06881g7ba2hzmfq5vaz888d2q762zf4bxjc621rw3g8z702ps7w9";
+    }).rust.override {
       extensions = [
         "clippy-preview"
         "rls-preview"
diff --git a/src/rules.rs b/src/rules.rs
index 22968dc..07f09db 100644
--- a/src/rules.rs
+++ b/src/rules.rs
@@ -12,7 +12,7 @@ use crate::{
     tree_utils::{
         has_newline, next_non_whitespace_sibling, next_sibling, next_token_sibling,
         not_on_top_level, on_top_level, prev_non_whitespace_parent, prev_non_whitespace_sibling,
-        prev_sibling, prev_token_parent, prev_token_sibling,
+        prev_sibling, prev_token_sibling,
     },
 };
 
@@ -31,6 +31,9 @@ pub(crate) fn spacing() -> SpacingDsl {
         .test("{ a = 92 ; }", "{ a = 92; }")
         .inside(NODE_KEY_VALUE).before(T![;]).no_space_or_optional_newline()
         .inside(NODE_KEY_VALUE).before(T![;]).when(after_literal).no_space()
+        .inside(NODE_KEY_VALUE).before(NODE_IF_ELSE).when(not_inline_if).single_space_or_newline()
+        // .inside(NODE_KEY_VALUE).around(NODE_APPLY).single_space_or_optional_newline()
+        // .inside(NODE_KEY_VALUE).before(NODE_APPLY).when(multiple_argument).single_space()
 
         .test("a++\nb", "a ++\nb")
         .test("a==  b", "a == b")
@@ -44,11 +47,10 @@ pub(crate) fn spacing() -> SpacingDsl {
 
         .test("foo . bar . baz", "foo.bar.baz")
         .inside(NODE_SELECT).around(T![.]).no_space()
-
         .test("{} :92", "{}: 92")
         .inside(NODE_LAMBDA).before(T![:]).no_space()
         .inside(NODE_LAMBDA).after(T![:]).single_space_or_optional_newline()
-        .inside(NODE_LAMBDA).before(NODE_IDENT).when(prev_parent_is_newline).when(next_is_select_attrset).newline()
+        .inside(NODE_LAMBDA).before(NODE_IF_ELSE).when(not_inline_if).single_space_or_newline()
 
         .test("[1 2 3]", "[ 1 2 3 ]")
         .inside(NODE_LIST).after(T!["["]).single_space_or_newline()
@@ -65,13 +67,9 @@ pub(crate) fn spacing() -> SpacingDsl {
 
         .inside(NODE_PAREN).after(T!["("]).no_space_or_optional_newline()
         .inside(NODE_PAREN).before(T![")"]).no_space_or_optional_newline()
-        .inside(NODE_PAREN).before(T![")"]).when(paren_open_newline).newline()
-        .inside(NODE_PAREN).before(T![")"]).when(node_inside_paren).when(between_open_paren_newline).newline()
-        .inside(NODE_PAREN).before(T![")"]).when(node_inside_paren).when(between_open_paren_not_newline).no_space()
+        .inside(NODE_PAREN).after(T!["("]).when(has_no_brackets).no_space_or_newline()
+        .inside(NODE_PAREN).before(T![")"]).when(has_no_brackets).no_space_or_newline()
 
-        .inside(NODE_PAREN).before(T![")"]).when(prev_is_let).when(paren_on_top_level).newline()
-        .inside(NODE_PAREN).before(T![")"]).when(prev_is_if).when(not_inside_node_interpol).newline()
-        .inside(NODE_PAREN).before(T![")"]).when(inside_multiple_argument_function).newline()
         .test("{foo = 92;}", "{ foo = 92; }")
         .inside(NODE_ATTR_SET).after(T!["{"]).single_space_or_newline()
         .inside(NODE_ATTR_SET).before(T!["}"]).single_space_or_newline()
@@ -112,31 +110,13 @@ pub(crate) fn spacing() -> SpacingDsl {
 
         .test("f  x", "f x")
         .inside(NODE_APPLY).between(VALUES, VALUES).single_space_or_optional_newline()
-        .inside(NODE_APPLY).before(NODE_PAREN).when(last_argument_in_function).single_space()
-        .inside(NODE_APPLY).before(NODE_PAREN).when(last_argument_in_function).when(multi_argument_in_function).when(between_argument_has_newline).newline()
-        .inside(NODE_APPLY).before(NODE_PAREN).when(non_last_argument_in_function).when(between_argument_has_newline).when(not_inside_node_interpol).newline()
-        .inside(NODE_APPLY).before(NODE_PAREN).when(inside_node_interpol).single_space_or_optional_newline()
-        .inside(NODE_APPLY).before(NODE_LIST).when(last_argument_in_function).single_space()
-        .inside(NODE_APPLY).before(NODE_LIST).when(last_argument_in_function).when(multi_argument_in_function).when(between_argument_has_newline).newline()
-        .inside(NODE_APPLY).before(NODE_ATTR_SET).when(last_argument_in_function).single_space()
-        .inside(NODE_APPLY).before(NODE_ATTR_SET).when(non_last_argument_in_function).when(node_apply_has_newline).when(not_inside_node_interpol).newline()
-        .inside(NODE_APPLY).before(NODE_IDENT).when(node_is_argument).when(last_argument_in_function).when(node_apply_has_newline).when(not_inside_node_interpol).newline()
-        .inside(NODE_APPLY).before(NODE_IDENT).when(node_is_argument).when(non_last_argument_in_function).when(between_argument_has_newline).when(not_inside_node_interpol).newline()
-        .inside(NODE_APPLY).before(NODE_SELECT).when(node_is_argument).when(last_argument_in_function).when(node_apply_has_newline).when(not_inside_node_interpol).newline()
-        .inside(NODE_APPLY).before(NODE_SELECT).when(node_is_argument).when(non_last_argument_in_function).when(between_argument_has_newline).when(not_inside_node_interpol).newline()
-        .inside(NODE_APPLY).before(NODE_IDENT).when(node_is_function).when(outside_inline_pattern).when(between_argument_has_newline).when(not_inside_node_interpol).newline()
-        .inside(NODE_APPLY).before(NODE_SELECT).when(node_is_function).when(outside_inline_pattern).when(between_argument_has_newline).when(not_inside_node_interpol).newline()
+        .inside(NODE_APPLY).before(VALUES).when(should_be_newline).single_space_or_newline()
 
         .test("if  cond  then  tru  else  fls", "if cond then tru else fls")
-        .inside(NODE_IF_ELSE).before(T![if]).when(not_on_top_level).when(not_inline_if).single_space_or_newline()
-        .inside(NODE_IF_ELSE).before(T![if]).when(after_else_is_inline_if).single_space()
-        .inside(NODE_IF_ELSE).before(T![if]).when(inside_node_interpol).when(between_if_then_not_newline).no_space()
-        .inside(NODE_IF_ELSE).before(T![then]).when(before_token_has_newline).newline()
-        .inside(NODE_IF_ELSE).before(T![then]).single_space_or_optional_newline()
-        .inside(NODE_IF_ELSE).after([T![if],T![then]]).single_space_or_optional_newline()
-        .inside(NODE_IF_ELSE).around(T![else]).single_space_or_optional_newline()
-        .inside(NODE_IF_ELSE).after(T![else]).when(after_else_has_newline).newline()
-
+        .inside(NODE_IF_ELSE).after(T![if]).single_space_or_optional_newline()
+        .inside(NODE_IF_ELSE).around([T![else],T![then]]).single_space_or_optional_newline()
+        .inside(NODE_IF_ELSE).after(T![then]).when(has_expression_node).single_space_or_newline()
+        .inside(NODE_IF_ELSE).after(T![else]).when(has_expression_node).single_space_or_newline()
         // special-case to force a linebreak before `=` in
         //
         // ```nix
@@ -195,6 +175,31 @@ fn after_literal(element: &SyntaxElement) -> bool {
     };
 }
 
+fn has_no_brackets(element: &SyntaxElement) -> bool {
+    let parent = match element.parent() {
+        None => return false,
+        Some(it) => it,
+    };
+    parent.children().all(|it| match it.kind() {
+        NODE_ATTR_SET | NODE_PATTERN | NODE_LIST => false,
+        NODE_IF_ELSE | NODE_BIN_OP | NODE_WITH | NODE_LAMBDA => {
+            before_token_has_newline(&it.into())
+        }
+        NODE_APPLY => {
+            if let Some(value) =
+                it.first_child().and_then(rnix::types::Apply::cast).and_then(|e| e.value())
+            {
+                match value.kind() {
+                    NODE_ATTR_SET | NODE_PAREN => return false,
+                    _ => return true && before_token_has_newline(&it.into()),
+                }
+            }
+            return false;
+        }
+        _ => true,
+    })
+}
+
 fn inline_with_attr_set(element: &SyntaxElement) -> bool {
     fn inline_attr_set(element: &SyntaxElement) -> Option<bool> {
         let inline_attr = element
@@ -209,233 +214,81 @@ fn inline_with_attr_set(element: &SyntaxElement) -> bool {
             == Some(true)
 }
 
-fn node_apply_has_newline(element: &SyntaxElement) -> bool {
-    fn top_level_node_function(element: &SyntaxElement) -> Option<bool> {
-        element
-            .parent()?
-            .ancestors()
-            .take_while(|e| {
-                e.kind() != NODE_KEY_VALUE && e.kind() != NODE_IF_ELSE && e.kind() != NODE_PAREN
-            })
-            .filter(|e| e.kind() == NODE_APPLY)
-            .max_by_key(|e| e.text_range().start())
-            .map(|e| has_newline(&e))
-    }
-    top_level_node_function(element).unwrap_or(false)
-}
+fn should_be_newline(element: &SyntaxElement) -> bool {
+    let parent = match element.parent() {
+        None => return false,
+        Some(it) => it,
+    };
 
-fn between_argument_has_newline(element: &SyntaxElement) -> bool {
-    fn newline_in_between(element: &SyntaxElement) -> bool {
-        let prev_argument =
-            prev_token_sibling(element).map(|e| e.text().contains("\n")).unwrap_or(false);
-        let prev_sibling = prev_sibling(element).map(|e| has_newline(&e)).unwrap_or(false);
-        prev_argument || prev_sibling
-    }
+    let apply = match element.parent() {
+        None => return false,
+        Some(it) => rnix::types::Apply::cast(it),
+    };
 
-    fn between_arg_newline(element: &SyntaxElement) -> Option<bool> {
-        let list_node_apply = element
-            .parent()?
-            .ancestors()
-            .take_while(|e| {
-                e.kind() != NODE_KEY_VALUE && e.kind() != NODE_IF_ELSE && e.kind() != NODE_PAREN
-            })
-            .filter_map(|e| match e.kind() {
-                NODE_APPLY => e.last_child(),
-                _ => None,
-            });
-        let exist_newline = list_node_apply.fold(false, |b, e| b || newline_in_between(&e.into()));
-        Some(exist_newline)
+    match element.kind() {
+        NODE_APPLY => false,
+        NODE_SELECT | NODE_IDENT => {
+            if is_argument(element) {
+                return has_newline(&parent);
+            }
+            false
+        }
+        NODE_PAREN | NODE_ATTR_SET => match last_argument_in_function(element) {
+            true => {
+                if let Some(fun) = apply.clone().and_then(|e| e.lambda()) {
+                    match fun.kind() {
+                        // node is in multi-argument function
+                        NODE_APPLY => {
+                            if let Some(node) = prev_non_whitespace_sibling(element) {
+                                return node.as_node().map(|t| has_newline(t)).unwrap_or(false);
+                            }
+                            return false;
+                        }
+                        // node is unary function
+                        _ => return false,
+                    }
+                }
+                return false;
+            }
+            false => return has_newline(&parent),
+        },
+        _ => false,
     }
-
-    between_arg_newline(element).unwrap_or(false)
 }
 
-fn node_is_argument(element: &SyntaxElement) -> bool {
-    fn is_argument(element: &SyntaxElement) -> Option<bool> {
-        let element_text_range = element.text_range().start();
-        Some(element.parent()?.last_child()?.text_range().start() == element_text_range)
+fn is_argument(element: &SyntaxElement) -> bool {
+    match prev_sibling(element) {
+        None => false,
+        Some(it) => it.kind() == NODE_APPLY,
     }
-    is_argument(element).unwrap_or(false)
 }
 
 fn last_argument_in_function(element: &SyntaxElement) -> bool {
-    fn is_last_argument(element: &SyntaxElement) -> Option<bool> {
-        let inside_apply = element.parent()?.kind() == NODE_APPLY;
-        let last_argument = element.parent()?.parent()?.kind() != NODE_APPLY;
-        Some(last_argument && inside_apply)
-    }
-    is_last_argument(element).unwrap_or(false)
-}
-
-// Check whether the function is unary function or not
-fn multi_argument_in_function(element: &SyntaxElement) -> bool {
-    prev_sibling(element).map(|e| e.kind() == NODE_APPLY).unwrap_or(false)
-}
-
-fn non_last_argument_in_function(element: &SyntaxElement) -> bool {
-    !last_argument_in_function(element)
-}
-
-fn node_is_function(element: &SyntaxElement) -> bool {
-    fn is_function(element: &SyntaxElement) -> Option<bool> {
-        let element_text_range = element.text_range().start();
-        Some(element.parent()?.first_child()?.text_range().start() == element_text_range)
-    }
-    is_function(element).unwrap_or(false) && not_on_top_level(element)
-}
-
-// Special case if function is nested inside certain node
-fn outside_inline_pattern(element: &SyntaxElement) -> bool {
-    let node = element.ancestors().find(|e| {
-        e.kind() == NODE_PATTERN
-            || e.kind() == NODE_BIN_OP
-            || e.kind() == NODE_IF_ELSE
-            || e.kind() == NODE_WITH
-            || e.kind() == NODE_INHERIT
-            || e.kind() == NODE_OR_DEFAULT
-            || e.kind() == NODE_ASSERT
-            || e.kind() == NODE_LAMBDA
-    });
-
-    node.and_then(|e| match e.kind() {
-        NODE_ASSERT => {
-            let closing_semicolon = element.text_range().start();
-            let has_newline = e
-                .descendants_with_tokens()
-                .filter(|element| match element {
-                    NodeOrToken::Token(_) => true,
-                    _ => false,
-                })
-                .take_while(|e| e.text_range().start() != closing_semicolon)
-                .any(|t| t.as_token().map(|n| n.text().contains("\n")).unwrap_or(false));
-            Some(has_newline)
-        }
-        NODE_LAMBDA => Some(false),
-
-        NODE_IF_ELSE => Some(false),
-
-        _ => Some(true),
-    })
-    .unwrap_or(true)
-}
+    let is_last_argument = match element.parent() {
+        None => false,
+        Some(it) => match next_sibling(&it.into()) {
+            None => true,
+            _ => false,
+        },
+    };
 
-fn paren_open_newline(element: &SyntaxElement) -> bool {
-    fn after_paren_open_newline(element: &SyntaxElement) -> Option<bool> {
-        element
-            .parent()?
-            .first_child_or_token()
-            .and_then(|e| next_token_sibling(&e).map(|e| e.text().contains("\n")))
-    }
-    after_paren_open_newline(element) == Some(true)
+    is_last_argument
 }
 
-// Check whether there is exists function call inside parentheses
-fn inside_multiple_argument_function(element: &SyntaxElement) -> bool {
-    fn multiple_argument_function(element: &SyntaxElement) -> Option<bool> {
-        let first_node = element.parent()?.first_child()?;
-        let contain_node_apply = first_node.clone().kind() == NODE_APPLY;
-        let contain_multiple_argument_function =
-            multi_argument_in_function(&first_node.clone().last_child()?.into());
-        if contain_node_apply && contain_multiple_argument_function {
-            let exist_newline = first_node
-                .clone()
-                .descendants_with_tokens()
-                .filter(|element| match element {
-                    NodeOrToken::Token(_) => true,
-                    _ => false,
-                })
-                .any(|t| t.as_token().map(|n| n.text().contains("\n")).unwrap_or(false));
-            Some(exist_newline)
-        } else {
-            None
+fn has_expression_node(element: &SyntaxElement) -> bool {
+    if let Some(el) = next_non_whitespace_sibling(element) {
+        match el.kind() {
+            NODE_APPLY | NODE_PAREN | NODE_LET_IN => {
+                let node = match el.as_node() {
+                    Some(val) => val,
+                    None => return false,
+                };
+                return has_newline(node);
+            }
+            _ => return false,
         }
     }
-
-    multiple_argument_function(element).unwrap_or(false)
-}
-
-fn multiline_string(element: &SyntaxElement) -> bool {
-    if element.kind() == NODE_STRING {
-        element.as_node().map(|e| has_newline(&e)).unwrap_or(false);
-    }
-    false
-}
-
-fn node_inside_paren(element: &SyntaxElement) -> bool {
-    fn inside_paren_exist_node(element: &SyntaxElement) -> Option<bool> {
-        let open_paren_token_unit = element.parent()?.first_child_or_token()?.text_range().start();
-        let paren_contain_node_unit = element
-            .parent()?
-            .descendants()
-            .find(|e| {
-                e.kind() == NODE_LIST || e.kind() == NODE_ATTR_SET || multiline_string(element)
-            })?
-            .ancestors()
-            .find(|e| e.kind() == NODE_PAREN)?
-            .text_range()
-            .start();
-        Some(open_paren_token_unit == paren_contain_node_unit)
-    }
-
-    fn node_outside_key_value(element: &SyntaxElement) -> Option<bool> {
-        let paren_contain_node_unit = element
-            .parent()?
-            .descendants_with_tokens()
-            .take_while(|e| {
-                e.kind() != NODE_LET_IN && e.kind() != NODE_KEY_VALUE && e.kind() != NODE_IF_ELSE
-            })
-            .any(|t| t.kind() == NODE_LIST || t.kind() == NODE_ATTR_SET || t.kind() == NODE_STRING);
-
-        Some(paren_contain_node_unit)
-    }
-
-    inside_paren_exist_node(element).unwrap_or(false)
-        && node_outside_key_value(element) == Some(true)
-}
-
-fn between_open_paren_not_newline(element: &SyntaxElement) -> bool {
-    !between_open_paren_newline(element)
-}
-
-fn between_open_paren_newline(element: &SyntaxElement) -> bool {
-    fn after_paren_open_newline(element: &SyntaxElement) -> Option<bool> {
-        let between_paren_node_has_newline = element
-            .parent()?
-            .descendants_with_tokens()
-            .take_while(|n| {
-                n.kind() != NODE_ATTR_SET && n.kind() != NODE_LIST && n.kind() != NODE_STRING
-            })
-            .filter(|element| match element {
-                NodeOrToken::Token(_) => true,
-                _ => false,
-            })
-            .any(|t| t.as_token().map(|n| n.text().contains("\n")).unwrap_or(false));
-
-        Some(between_paren_node_has_newline)
-    }
-    after_paren_open_newline(element) == Some(true)
-}
-
-fn next_is_select_attrset(element: &SyntaxElement) -> bool {
-    let select = next_sibling(element).map(|n| n.kind() == NODE_SELECT).unwrap_or(false);
-    let attrset = next_sibling(element).map(|n| n.kind() == NODE_ATTR_SET).unwrap_or(false);
-    select || attrset
-}
-
-fn after_else_is_inline_if(element: &SyntaxElement) -> bool {
-    let token_else = prev_non_whitespace_parent(element)
-        .and_then(|e| e.into_token().map(|t| t.kind() == T![else]))
-        .unwrap_or(false);
-    let has_newline = prev_token_parent(element).map(|t| t.text().contains("\n")).unwrap_or(false);
-    token_else & !has_newline
-}
-
-fn not_inside_node_interpol(element: &SyntaxElement) -> bool {
-    !inside_node_interpol(element)
-}
-
-fn inside_node_interpol(element: &SyntaxElement) -> bool {
-    element.ancestors().any(|n| n.kind() == NODE_STRING_INTERPOL)
+    return false;
 }
 
 fn not_inline_if(element: &SyntaxElement) -> bool {
@@ -456,70 +309,6 @@ fn not_inline_if(element: &SyntaxElement) -> bool {
     not_inline_if_then_else(element) == Some(true)
 }
 
-fn between_if_then_not_newline(element: &SyntaxElement) -> bool {
-    fn not_inline_if_then_else(element: &SyntaxElement) -> Option<bool> {
-        let first_el = element
-            .parent()?
-            .descendants_with_tokens()
-            .take_while(|e| e.kind() != T![then])
-            .filter(|element| match element {
-                NodeOrToken::Token(_) => true,
-                _ => false,
-            })
-            .any(|t| t.as_token().map(|e| e.text().contains("\n")).unwrap_or(false));
-
-        Some(first_el)
-    }
-
-    not_inline_if_then_else(element) == Some(false)
-}
-
-// This function to make sure let..in inside else get expanded
-fn after_else_has_newline(element: &SyntaxElement) -> bool {
-    next_non_whitespace_sibling(element)
-        .and_then(|e| match e.as_node() {
-            Some(node) => {
-                if node.kind() == NODE_LET_IN {
-                    Some(has_newline(&node))
-                } else {
-                    Some(false)
-                }
-            }
-            _ => Some(false),
-        })
-        .unwrap_or(false)
-}
-
-fn prev_is_let(element: &SyntaxElement) -> bool {
-    prev_non_whitespace_sibling(element)
-        .and_then(|e| e.into_node().map(|n| n.kind() == NODE_LET_IN))
-        .unwrap_or(false)
-}
-
-fn prev_is_if(element: &SyntaxElement) -> bool {
-    let node_if = prev_non_whitespace_sibling(element)
-        .and_then(|e| e.into_node().map(|n| n.kind() == NODE_IF_ELSE))
-        .unwrap_or(false);
-    let is_expanded_if = element.parent().map(|e| has_newline(&e)).unwrap_or(false);
-    node_if && is_expanded_if
-}
-
-fn prev_parent_is_newline(element: &SyntaxElement) -> bool {
-    prev_token_parent(element).map(|n| n.text().contains('\n')).unwrap_or(false)
-}
-
-fn paren_on_top_level(element: &SyntaxElement) -> bool {
-    let parent = match element.parent() {
-        None => return true,
-        Some(it) => it,
-    };
-    match parent.kind() {
-        NODE_ROOT => true,
-        NODE_SELECT | NODE_PAREN | NODE_APPLY => paren_on_top_level(&parent.into()),
-        _ => false,
-    }
-}
-
 fn before_token_has_newline(element: &SyntaxElement) -> bool {
     prev_token_sibling(element).map(|e| e.text().contains("\n")).unwrap_or(false)
 }
diff --git a/src/tree_utils.rs b/src/tree_utils.rs
index 7e5c2a5..98df189 100644
--- a/src/tree_utils.rs
+++ b/src/tree_utils.rs
@@ -100,10 +100,12 @@ pub(crate) fn next_token_sibling(element: &SyntaxElement) -> Option<SyntaxToken>
 }
 
 pub(crate) fn prev_token_sibling(element: &SyntaxElement) -> Option<SyntaxToken> {
-    match element.prev_sibling_or_token()? {
-        NodeOrToken::Node(_) => None,
-        NodeOrToken::Token(it) => Some(it),
-    }
+    successors(element.prev_sibling_or_token(), |it| it.prev_sibling_or_token()).find_map(
+        |element| match element {
+            NodeOrToken::Node(_) => None,
+            NodeOrToken::Token(it) => Some(it),
+        },
+    )
 }
 
 pub(crate) fn prev_non_whitespace_token_sibling(element: &SyntaxElement) -> Option<SyntaxToken> {
@@ -121,13 +123,6 @@ pub(crate) fn prev_non_whitespace_token_sibling(element: &SyntaxElement) -> Opti
     )
 }
 
-pub(crate) fn prev_token_parent(element: &SyntaxElement) -> Option<SyntaxToken> {
-    match element.parent()?.prev_sibling_or_token()? {
-        NodeOrToken::Node(_) => None,
-        NodeOrToken::Token(it) => Some(it),
-    }
-}
-
 pub(crate) fn prev_non_whitespace_parent(element: &SyntaxElement) -> Option<SyntaxElement> {
     successors(element.parent()?.prev_sibling_or_token(), |it| it.prev_sibling_or_token())
         .find(|it| it.kind() != TOKEN_WHITESPACE)


Test patch:
diff --git a/test_data/function_call.good.nix b/test_data/function_call.good.nix
index 719371b..ced2802 100644
--- a/test_data/function_call.good.nix
+++ b/test_data/function_call.good.nix
@@ -5,35 +5,22 @@
   fooD = fun a b (with c; {
     inherit d;
   });
-  fooF =
-    fun
-      (with a; b)
-      c
-      d;
-  fooG =
-    fun
-      (with a; b)
-      c
-      d;
-  fooH =
-    fun
-      a
-      (with b; c)
-      d;
-  fooI =
-    fun
-      a
-      (with b; c)
-      d;
-  fooJ =
-    fun
-      a
-      (with b; c)
-      d.a;
+  fooF = fun (with a; b)
+    c
+    d;
+  fooG = fun (with a; b) c
+    d;
+  fooH = fun a (with b; c)
+    d;
+  fooI = fun a
+    (with b; c)
+    d;
+  fooJ = fun a
+    (with b; c)
+    d.a;
   fooK = fun a;
-  fooL =
-    fun
-      a;
+  fooL = fun
+    a;
 
 
   barA = fun a b { inherit d; };
@@ -42,24 +29,13 @@
   barD = fun a b {
     inherit d;
   };
-  barF =
-    fun
-      { inherit b; }
-      c
-      d;
-  barG =
-    fun
-      { inherit b; }
-      c
-      d;
-  barH =
-    fun
-      a
-      { inherit c; }
-      d;
-  barI =
-    fun
-      a
-      { inherit c; }
-      d;
+  barF = fun { inherit b; } c
+    d;
+  barG = fun { inherit b; } c
+    d;
+  barH = fun a { inherit c; }
+    d;
+  barI = fun a
+    { inherit c; }
+    d;
 }
diff --git a/test_data/indent_paren.bad.nix b/test_data/indent_paren.bad.nix
index c9ec1a1..351f5fb 100644
--- a/test_data/indent_paren.bad.nix
+++ b/test_data/indent_paren.bad.nix
@@ -6,8 +6,7 @@
 )
 )
 );
-  xxx = listToAttrs (
-  concatMap (name:
+  xxx = listToAttrs (concatMap (name:
   let a = 4; in 5
 ));
 foldAttrs = op: nul: list_of_attrs:
@@ -15,5 +14,8 @@ foldAttrs = op: nul: list_of_attrs:
     fold (name: o:
    o // { ${name} = op n.${name} (a.${name} or nul); }
   ) a (attrNames n)
-  ) {} list_of_attrs; 
+  ) {} list_of_attrs;
+bar = fun "arg" (callPackage ./. {
+  inherit foo;
+});
 }
diff --git a/test_data/indent_paren.good.nix b/test_data/indent_paren.good.nix
index fae98c0..8b99d96 100644
--- a/test_data/indent_paren.good.nix
+++ b/test_data/indent_paren.good.nix
@@ -6,11 +6,9 @@
       )
     )
   );
-  xxx = listToAttrs (
-    concatMap (name:
-      let a = 4; in 5
-    )
-  );
+  xxx = listToAttrs (concatMap (name:
+    let a = 4; in 5
+  ));
   foldAttrs = op: nul: list_of_attrs:
     fold
       (n: a:
@@ -23,4 +21,7 @@
       )
       { }
       list_of_attrs;
+  bar = fun "arg" (callPackage ./. {
+    inherit foo;
+  });
 }
diff --git a/test_data/indent_string_literal_interpolation.bad.nix b/test_data/indent_string_literal_interpolation.bad.nix
index f76540f..415f67a 100644
--- a/test_data/indent_string_literal_interpolation.bad.nix
+++ b/test_data/indent_string_literal_interpolation.bad.nix
@@ -44,6 +44,18 @@ qux =
     bla = hoi
   '';
 
+singleAsciiDoc = value: ''
+    ${
+      if lib.hasAttr "example" value then ''
+        Example::
+        ${
+          builtins.toJSON value.example
+        }
+      ''
+      else "No Example:: {blank}"
+    }
+'';
+
 nested_antiquotation = mkBefore
   ''
     ${optionalString cfg.earlySetup ''
diff --git a/test_data/indent_string_literal_interpolation.good.nix b/test_data/indent_string_literal_interpolation.good.nix
index 36adc04..d7bae50 100644
--- a/test_data/indent_string_literal_interpolation.good.nix
+++ b/test_data/indent_string_literal_interpolation.good.nix
@@ -44,16 +44,27 @@
       bla = hoi
     '';
 
-  nested_antiquotation =
-    mkBefore
+  singleAsciiDoc = value: ''
+    ${
+      if lib.hasAttr "example" value then ''
+        Example::
+        ${
+          builtins.toJSON value.example
+        }
       ''
+      else "No Example:: {blank}"
+    }
+  '';
+
+  nested_antiquotation = mkBefore
+    ''
+      ${optionalString cfg.earlySetup ''
         ${optionalString cfg.earlySetup ''
-          ${optionalString cfg.earlySetup ''
-            setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
-          ''}
           setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
         ''}
-      '';
+        setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
+      ''}
+    '';
 
   singleAsciiDoc = value: ''
     Example::
diff --git a/test_data/issue-126.bad.nix b/test_data/issue-126.bad.nix
index 3e5c843..871d142 100644
--- a/test_data/issue-126.bad.nix
+++ b/test_data/issue-126.bad.nix
@@ -1,11 +1,11 @@
-{ 
+{
   testA= [(fetchurl {
   url = "bla";
 })];
 
   testB = [( fetchurl{url="bla";} )];
 
-  testC = { 
+  testC = {
   testA1= [(fetchurl {
   url = "bla";
 })];
@@ -42,6 +42,5 @@ in
 buildLinux ({
   version = "${foo}-mptcp_v1.0.0";
   inherit bar;
-}
-);
+});
 }
diff --git a/test_data/issue-178.good.nix b/test_data/issue-178.good.nix
index d190f9d..d024a3b 100644
--- a/test_data/issue-178.good.nix
+++ b/test_data/issue-178.good.nix
@@ -5,11 +5,8 @@
     src = sources.nixpkgs-fmt;
   }).nixpkgs-fmt.build;
 
-  testB = buildLinux (
-    map
-      ({ name }:
-        { inherit name; }
-      )
-      cfg.feeds
-  );
+  testB = buildLinux (map
+    ({ name }:
+      { inherit name; })
+    cfg.feeds);
 }
diff --git a/test_data/issue-181.bad.nix b/test_data/issue-181.bad.nix
index 7f7b82b..0b5b81d 100644
--- a/test_data/issue-181.bad.nix
+++ b/test_data/issue-181.bad.nix
@@ -2,8 +2,7 @@
 python3.pkgs.buildPythonApplication {
    propagatedBuildInputs = (with python3.pkgs; [
 pkg1
-   ]
-   );
+]);
 testB = forAllSystems (
     system:
     self.apps.${system}.nixpkgs-fmt);
diff --git a/test_data/nested_if_else.good.nix b/test_data/nested_if_else.good.nix
index e7873c0..3a256ca 100644
--- a/test_data/nested_if_else.good.nix
+++ b/test_data/nested_if_else.good.nix
@@ -2,15 +2,17 @@
   modifyA = n: fn: v:
     if (n == 0) then fn v
     else if isList v then map (modify (n - 1) fn) v
-    else if isAttrs v then mapAttrs
-      (const (modify (n - 1) fn))
-      v
+    else if isAttrs v then
+      mapAttrs
+        (const (modify (n - 1) fn))
+        v
     else v;
   modifyB = n: fn: v:
     if (n == 0) then fn v
     else if isList v then map (modify (n - 1) fn) v
-    else if isAttrs v then mapAttrs
-      (const (modify (n - 1) fn))
-      v
+    else if isAttrs v then
+      mapAttrs
+        (const (modify (n - 1) fn))
+        v
     else v;
 }
diff --git a/test_data/nixpkgs_repository/lib_attrset.good.nix b/test_data/nixpkgs_repository/lib_attrset.good.nix
index 759fc11..3f19103 100644
--- a/test_data/nixpkgs_repository/lib_attrset.good.nix
+++ b/test_data/nixpkgs_repository/lib_attrset.good.nix
@@ -1,17 +1,13 @@
 {
   matchAttrs = pattern: attrs: assert isAttrs pattern;
-    fold and true (attrValues (
-      zipAttrsWithNames
-        (attrNames pattern)
-        (n: values:
-          let
-            pat = head values;
-            val = head (tail values);
-          in
-          if length values == 1 then false
-          else if isAttrs pat then isAttrs val && matchAttrs pat val
-          else pat == val
-        )
-        [ pattern attrs ]
-    ));
+    fold and true (attrValues (zipAttrsWithNames (attrNames pattern)
+      (n: values:
+        let
+          pat = head values;
+          val = head (tail values);
+        in
+        if length values == 1 then false
+        else if isAttrs pat then isAttrs val && matchAttrs pat val
+        else pat == val
+      ) [ pattern attrs ]));
 }
diff --git a/test_data/nixpkgs_repository/lib_customisation.good.nix b/test_data/nixpkgs_repository/lib_customisation.good.nix
index 8be314f..23ce6fc 100644
--- a/test_data/nixpkgs_repository/lib_customisation.good.nix
+++ b/test_data/nixpkgs_repository/lib_customisation.good.nix
@@ -11,13 +11,11 @@
       //
       (drv.passthru or { })
       //
-      (
-        if (drv ? crossDrv && drv ? nativeDrv)
-        then {
-          crossDrv = overrideDerivation drv.crossDrv f;
-          nativeDrv = overrideDerivation drv.nativeDrv f;
-        }
-        else { }
-      )
+      (if (drv ? crossDrv && drv ? nativeDrv)
+      then {
+        crossDrv = overrideDerivation drv.crossDrv f;
+        nativeDrv = overrideDerivation drv.nativeDrv f;
+      }
+      else { })
     );
 }
diff --git a/test_data/nixpkgs_repository/lib_deprecated.bad.nix b/test_data/nixpkgs_repository/lib_deprecated.bad.nix
index d2c5c11..92c0847 100644
--- a/test_data/nixpkgs_repository/lib_deprecated.bad.nix
+++ b/test_data/nixpkgs_repository/lib_deprecated.bad.nix
@@ -13,6 +13,5 @@
                                            ++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y)
                                            ++ ys;
                                   acc = acc';
-                                }
-                      );
+                                });
 }
diff --git a/test_data/nixpkgs_repository/lib_generators.good.nix b/test_data/nixpkgs_repository/lib_generators.good.nix
index 66ab420..6e03ab7 100644
--- a/test_data/nixpkgs_repository/lib_generators.good.nix
+++ b/test_data/nixpkgs_repository/lib_generators.good.nix
@@ -7,8 +7,7 @@
     let
       # map function to string for each key val
       mapAttrsToStringsSep = sep: mapFn: attrs:
-        libStr.concatStringsSep
-          sep
+        libStr.concatStringsSep sep
           (libAttr.mapAttrsToList mapFn attrs);
       mkSection = sectName: sectValues: ''
         [${mkSectionName sectName}]
diff --git a/test_data/nixpkgs_repository/lib_tests_modules_alias_priority_override.good.nix b/test_data/nixpkgs_repository/lib_tests_modules_alias_priority_override.good.nix
index f3f1220..09d57e5 100644
--- a/test_data/nixpkgs_repository/lib_tests_modules_alias_priority_override.good.nix
+++ b/test_data/nixpkgs_repository/lib_tests_modules_alias_priority_override.good.nix
@@ -8,14 +8,12 @@
     ({ config, lib, ... }:
       {
         enableAlias = lib.mkForce false;
-      }
-    )
+      })
 
     # Enable the normal (non-aliased) option.
     ({ config, lib, ... }:
       {
         enable = true;
-      }
-    )
+      })
   ];
 }
diff --git a/test_data/nixpkgs_repository/maintainers_scripts_update.bad.nix b/test_data/nixpkgs_repository/maintainers_scripts_update.bad.nix
index f02db47..9895a7a 100644
--- a/test_data/nixpkgs_repository/maintainers_scripts_update.bad.nix
+++ b/test_data/nixpkgs_repository/maintainers_scripts_update.bad.nix
@@ -11,10 +11,8 @@
                                  (if builtins.hasAttr "maintainers" pkg.meta
                                    then (if builtins.isList pkg.meta.maintainers
                                            then builtins.elem maintainer pkg.meta.maintainers
-                                           else maintainer == pkg.meta.maintainers
-                                        )
-                                   else false
-                                 )
+                                           else maintainer == pkg.meta.maintainers)
+                                   else false)
                    )
                    (name: pkg: pkg)
                    pkgs;
diff --git a/test_data/nixpkgs_repository/maintainers_scripts_update.good.nix b/test_data/nixpkgs_repository/maintainers_scripts_update.good.nix
index f17ff0f..7637c6f 100644
--- a/test_data/nixpkgs_repository/maintainers_scripts_update.good.nix
+++ b/test_data/nixpkgs_repository/maintainers_scripts_update.good.nix
@@ -9,15 +9,12 @@
     in
     packagesWith
       (name: pkg: builtins.hasAttr "updateScript" pkg &&
-        (
-          if builtins.hasAttr "maintainers" pkg.meta
-          then (
-            if builtins.isList pkg.meta.maintainers
-            then builtins.elem maintainer pkg.meta.maintainers
-            else maintainer == pkg.meta.maintainers
-          )
-          else false
-        )
+        (if builtins.hasAttr "maintainers" pkg.meta
+        then
+          (if builtins.isList pkg.meta.maintainers
+          then builtins.elem maintainer pkg.meta.maintainers
+          else maintainer == pkg.meta.maintainers)
+        else false)
       )
       (name: pkg: pkg)
       pkgs;
@@ -29,8 +26,7 @@
     if attrSet == null then
       builtins.throw "Attribute path `${path}` does not exists."
     else
-      packagesWith
-        (name: pkg: builtins.hasAttr "updateScript" pkg)
+      packagesWith (name: pkg: builtins.hasAttr "updateScript" pkg)
         (name: pkg: pkg)
         attrSet;
 }
diff --git a/test_data/nixpkgs_repository/nixos_lib_testing_python.bad.nix b/test_data/nixpkgs_repository/nixos_lib_testing_python.bad.nix
index 4af5e03..8110037 100644
--- a/test_data/nixpkgs_repository/nixos_lib_testing_python.bad.nix
+++ b/test_data/nixpkgs_repository/nixos_lib_testing_python.bad.nix
@@ -10,7 +10,7 @@ driver = let warn = if skipLint then lib.warn "Linting is disabled!" else lib.id
       mkdir -p $out/bin
       echo -n "$testScript" > $out/test-script
       ${lib.optionalString (!skipLint) ''
-      ${python3Packages.black}/bin/black --check --diff $out/test-script
+        ${python3Packages.black}/bin/black --check --diff $out/test-script
       ''}
       ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
       vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
diff --git a/test_data/nixpkgs_repository/nixos_lib_testing_python.good.nix b/test_data/nixpkgs_repository/nixos_lib_testing_python.good.nix
index 3482503..98c4515 100644
--- a/test_data/nixpkgs_repository/nixos_lib_testing_python.good.nix
+++ b/test_data/nixpkgs_repository/nixos_lib_testing_python.good.nix
@@ -1,35 +1,33 @@
 {
   driver = let warn = if skipLint then lib.warn "Linting is disabled!" else lib.id; in
-    warn (
-      runCommand
-        testDriverName
-        {
-          buildInputs = [ makeWrapper ];
-          testScript = testScript';
-          preferLocalBuild = true;
-          testName = name;
-        }
-        ''
-          mkdir -p $out/bin
-          echo -n "$testScript" > $out/test-script
-          ${lib.optionalString (!skipLint) ''
+    warn (runCommand testDriverName
+      {
+        buildInputs = [ makeWrapper ];
+        testScript = testScript';
+        preferLocalBuild = true;
+        testName = name;
+      }
+      ''
+        mkdir -p $out/bin
+        echo -n "$testScript" > $out/test-script
+        ${lib.optionalString (!skipLint) ''
           ${python3Packages.black}/bin/black --check --diff $out/test-script
-          ''}
-          ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
-          vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
-          wrapProgram $out/bin/nixos-test-driver \
-            --add-flags "''${vms[*]}" \
-            ${lib.optionalString enableOCR
-            "--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
-            --run "export testScript=\"\$(${coreutils}/bin/cat $out/test-script)\"" \
-            --set VLANS '${toString vlans}'
-          ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
-          wrapProgram $out/bin/nixos-run-vms \
-            --add-flags "''${vms[*]}" \
-            ${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
-            --set tests 'start_all(); join_all();' \
-            --set VLANS '${toString vlans}' \
-            ${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
-        ''
+        ''}
+        ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
+        vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
+        wrapProgram $out/bin/nixos-test-driver \
+          --add-flags "''${vms[*]}" \
+          ${lib.optionalString enableOCR
+          "--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
+          --run "export testScript=\"\$(${coreutils}/bin/cat $out/test-script)\"" \
+          --set VLANS '${toString vlans}'
+        ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
+        wrapProgram $out/bin/nixos-run-vms \
+          --add-flags "''${vms[*]}" \
+          ${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
+          --set tests 'start_all(); join_all();' \
+          --set VLANS '${toString vlans}' \
+          ${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
+      ''
     ); # "
 }
diff --git a/test_data/nixpkgs_repository/nixos_modules_config_console.good.nix b/test_data/nixpkgs_repository/nixos_modules_config_console.good.nix
index 9afcab1..6ab8ed9 100644
--- a/test_data/nixpkgs_repository/nixos_modules_config_console.good.nix
+++ b/test_data/nixpkgs_repository/nixos_modules_config_console.good.nix
@@ -1,7 +1,7 @@
 {
   boot.initrd.preLVMCommands = mkBefore ''
-      kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
-      printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
+      kbd_mode ${ if isUnicode then "-u" else "-a"} -C /dev/console
+      printf "\033%%${ if isUnicode then "G" else "@"}" >> /dev/console
       loadkmap < ${optimizedKeymap}
       ${optionalString cfg.earlySetup ''
     setfont -C /dev/console $extraUtils/share/consolefonts/font.psf