Commit faa3fa65ac

Isaac Freund <ifreund@ifreund.xyz>
2021-02-11 22:38:59
zig fmt: implement line comments
1 parent b6448a5
Changed files (2)
lib/std/zig/parser_test.zig
@@ -663,7 +663,9 @@ test "zig fmt: anon list literal 3 element comma" {
         \\test {
         \\    const x = .{
         \\        a,
+        \\        // foo
         \\        b,
+        \\
         \\        c,
         \\    };
         \\}
@@ -874,20 +876,20 @@ test "zig fmt: enum literal" {
     );
 }
 
-//test "zig fmt: enum literal inside array literal" {
-//    try testCanonical(
-//        \\test "enums in arrays" {
-//        \\    var colors = []Color{.Green};
-//        \\    colors = []Colors{ .Green, .Cyan };
-//        \\    colors = []Colors{
-//        \\        .Grey,
-//        \\        .Green,
-//        \\        .Cyan,
-//        \\    };
-//        \\}
-//        \\
-//    );
-//}
+test "zig fmt: enum literal inside array literal" {
+    try testCanonical(
+        \\test "enums in arrays" {
+        \\    var colors = []Color{.Green};
+        \\    colors = []Colors{ .Green, .Cyan };
+        \\    colors = []Colors{
+        \\        .Grey,
+        \\        .Green,
+        \\        .Cyan,
+        \\    };
+        \\}
+        \\
+    );
+}
 
 test "zig fmt: character literal larger than u8" {
     try testCanonical(
@@ -954,56 +956,56 @@ test "zig fmt: linksection" {
 //        \\
 //    );
 //}
-//
-//test "zig fmt: correctly space struct fields with doc comments" {
-//    try testTransform(
-//        \\pub const S = struct {
-//        \\    /// A
-//        \\    a: u8,
-//        \\    /// B
-//        \\    /// B (cont)
-//        \\    b: u8,
-//        \\
-//        \\
-//        \\    /// C
-//        \\    c: u8,
-//        \\};
-//        \\
-//    ,
-//        \\pub const S = struct {
-//        \\    /// A
-//        \\    a: u8,
-//        \\    /// B
-//        \\    /// B (cont)
-//        \\    b: u8,
-//        \\
-//        \\    /// C
-//        \\    c: u8,
-//        \\};
-//        \\
-//    );
-//}
-//
-//test "zig fmt: doc comments on param decl" {
-//    try testCanonical(
-//        \\pub const Allocator = struct {
-//        \\    shrinkFn: fn (
-//        \\        self: *Allocator,
-//        \\        /// Guaranteed to be the same as what was returned from most recent call to
-//        \\        /// `allocFn`, `reallocFn`, or `shrinkFn`.
-//        \\        old_mem: []u8,
-//        \\        /// Guaranteed to be the same as what was returned from most recent call to
-//        \\        /// `allocFn`, `reallocFn`, or `shrinkFn`.
-//        \\        old_alignment: u29,
-//        \\        /// Guaranteed to be less than or equal to `old_mem.len`.
-//        \\        new_byte_count: usize,
-//        \\        /// Guaranteed to be less than or equal to `old_alignment`.
-//        \\        new_alignment: u29,
-//        \\    ) []u8,
-//        \\};
-//        \\
-//    );
-//}
+
+test "zig fmt: correctly space struct fields with doc comments" {
+    try testTransform(
+        \\pub const S = struct {
+        \\    /// A
+        \\    a: u8,
+        \\    /// B
+        \\    /// B (cont)
+        \\    b: u8,
+        \\
+        \\
+        \\    /// C
+        \\    c: u8,
+        \\};
+        \\
+    ,
+        \\pub const S = struct {
+        \\    /// A
+        \\    a: u8,
+        \\    /// B
+        \\    /// B (cont)
+        \\    b: u8,
+        \\
+        \\    /// C
+        \\    c: u8,
+        \\};
+        \\
+    );
+}
+
+test "zig fmt: doc comments on param decl" {
+    try testCanonical(
+        \\pub const Allocator = struct {
+        \\    shrinkFn: fn (
+        \\        self: *Allocator,
+        \\        /// Guaranteed to be the same as what was returned from most recent call to
+        \\        /// `allocFn`, `reallocFn`, or `shrinkFn`.
+        \\        old_mem: []u8,
+        \\        /// Guaranteed to be the same as what was returned from most recent call to
+        \\        /// `allocFn`, `reallocFn`, or `shrinkFn`.
+        \\        old_alignment: u29,
+        \\        /// Guaranteed to be less than or equal to `old_mem.len`.
+        \\        new_byte_count: usize,
+        \\        /// Guaranteed to be less than or equal to `old_alignment`.
+        \\        new_alignment: u29,
+        \\    ) []u8,
+        \\};
+        \\
+    );
+}
 
 test "zig fmt: aligned struct field" {
     try testCanonical(
@@ -1142,13 +1144,13 @@ test "zig fmt: aligned struct field" {
 //        \\
 //    );
 //}
-//
-//test "zig fmt: pointer of unknown length" {
-//    try testCanonical(
-//        \\fn foo(ptr: [*]u8) void {}
-//        \\
-//    );
-//}
+
+test "zig fmt: pointer of unknown length" {
+    try testCanonical(
+        \\fn foo(ptr: [*]u8) void {}
+        \\
+    );
+}
 
 test "zig fmt: spaces around slice operator" {
     try testCanonical(
@@ -1370,25 +1372,25 @@ test "zig fmt: async call in if condition" {
 //        \\
 //    );
 //}
-//
-//test "zig fmt: if-else with comment before else" {
-//    try testCanonical(
-//        \\comptime {
-//        \\    // cexp(finite|nan +- i inf|nan) = nan + i nan
-//        \\    if ((hx & 0x7fffffff) != 0x7f800000) {
-//        \\        return Complex(f32).new(y - y, y - y);
-//        \\    } // cexp(-inf +- i inf|nan) = 0 + i0
-//        \\    else if (hx & 0x80000000 != 0) {
-//        \\        return Complex(f32).new(0, 0);
-//        \\    } // cexp(+inf +- i inf|nan) = inf + i nan
-//        \\    else {
-//        \\        return Complex(f32).new(x, y - y);
-//        \\    }
-//        \\}
-//        \\
-//    );
-//}
-//
+
+test "zig fmt: if-else with comment before else" {
+    try testCanonical(
+        \\comptime {
+        \\    // cexp(finite|nan +- i inf|nan) = nan + i nan
+        \\    if ((hx & 0x7fffffff) != 0x7f800000) {
+        \\        return Complex(f32).new(y - y, y - y);
+        \\    } // cexp(-inf +- i inf|nan) = 0 + i0
+        \\    else if (hx & 0x80000000 != 0) {
+        \\        return Complex(f32).new(0, 0);
+        \\    } // cexp(+inf +- i inf|nan) = inf + i nan
+        \\    else {
+        \\        return Complex(f32).new(x, y - y);
+        \\    }
+        \\}
+        \\
+    );
+}
+
 //test "zig fmt: if nested" {
 //    try testCanonical(
 //        \\pub fn foo() void {
@@ -1467,17 +1469,17 @@ test "zig fmt: enum decl with no trailing comma" {
     );
 }
 
-//test "zig fmt: switch comment before prong" {
-//    try testCanonical(
-//        \\comptime {
-//        \\    switch (a) {
-//        \\        // hi
-//        \\        0 => {},
-//        \\    }
-//        \\}
-//        \\
-//    );
-//}
+test "zig fmt: switch comment before prong" {
+    try testCanonical(
+        \\comptime {
+        \\    switch (a) {
+        \\        // hi
+        \\        0 => {},
+        \\    }
+        \\}
+        \\
+    );
+}
 
 test "zig fmt: struct literal no trailing comma" {
     try testTransform(
@@ -1709,17 +1711,17 @@ test "zig fmt: multi line arguments without last comma" {
     );
 }
 
-//test "zig fmt: empty block with only comment" {
-//    try testCanonical(
-//        \\comptime {
-//        \\    {
-//        \\        // comment
-//        \\    }
-//        \\}
-//        \\
-//    );
-//}
-//
+test "zig fmt: empty block with only comment" {
+    try testCanonical(
+        \\comptime {
+        \\    {
+        \\        // comment
+        \\    }
+        \\}
+        \\
+    );
+}
+
 //test "zig fmt: no trailing comma on struct decl" {
 //    try testCanonical(
 //        \\const RoundParam = struct {
@@ -1781,15 +1783,15 @@ test "zig fmt: extra newlines at the end" {
 //        \\
 //    );
 //}
-//
-//test "zig fmt: nested struct literal with one item" {
-//    try testCanonical(
-//        \\const a = foo{
-//        \\    .item = bar{ .a = b },
-//        \\};
-//        \\
-//    );
-//}
+
+test "zig fmt: nested struct literal with one item" {
+    try testCanonical(
+        \\const a = foo{
+        \\    .item = bar{ .a = b },
+        \\};
+        \\
+    );
+}
 
 test "zig fmt: switch cases trailing comma" {
     try testTransform(
@@ -1848,26 +1850,26 @@ test "zig fmt: slice align" {
 //        \\
 //    );
 //}
-//
-//test "zig fmt: first thing in file is line comment" {
-//    try testCanonical(
-//        \\// Introspection and determination of system libraries needed by zig.
-//        \\
-//        \\// Introspection and determination of system libraries needed by zig.
-//        \\
-//        \\const std = @import("std");
-//        \\
-//    );
-//}
-//
-//test "zig fmt: line comment after doc comment" {
-//    try testCanonical(
-//        \\/// doc comment
-//        \\// line comment
-//        \\fn foo() void {}
-//        \\
-//    );
-//}
+
+test "zig fmt: first thing in file is line comment" {
+    try testCanonical(
+        \\// Introspection and determination of system libraries needed by zig.
+        \\
+        \\// Introspection and determination of system libraries needed by zig.
+        \\
+        \\const std = @import("std");
+        \\
+    );
+}
+
+test "zig fmt: line comment after doc comment" {
+    try testCanonical(
+        \\/// doc comment
+        \\// line comment
+        \\fn foo() void {}
+        \\
+    );
+}
 
 test "zig fmt: bit field alignment" {
     try testCanonical(
@@ -1928,27 +1930,27 @@ test "zig fmt: nested blocks" {
     );
 }
 
-//test "zig fmt: block with same line comment after end brace" {
-//    try testCanonical(
-//        \\comptime {
-//        \\    {
-//        \\        b();
-//        \\    } // comment
-//        \\}
-//        \\
-//    );
-//}
-//
-//test "zig fmt: statements with comment between" {
-//    try testCanonical(
-//        \\comptime {
-//        \\    a = b;
-//        \\    // comment
-//        \\    a = b;
-//        \\}
-//        \\
-//    );
-//}
+test "zig fmt: block with same line comment after end brace" {
+    try testCanonical(
+        \\comptime {
+        \\    {
+        \\        b();
+        \\    } // comment
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: statements with comment between" {
+    try testCanonical(
+        \\comptime {
+        \\    a = b;
+        \\    // comment
+        \\    a = b;
+        \\}
+        \\
+    );
+}
 
 test "zig fmt: statements with empty line between" {
     try testCanonical(
@@ -1969,60 +1971,60 @@ test "zig fmt: ptr deref operator and unwrap optional operator" {
     );
 }
 
-//test "zig fmt: comment after if before another if" {
-//    try testCanonical(
-//        \\test "aoeu" {
-//        \\    // comment
-//        \\    if (x) {
-//        \\        bar();
-//        \\    }
-//        \\}
-//        \\
-//        \\test "aoeu" {
-//        \\    if (x) {
-//        \\        foo();
-//        \\    }
-//        \\    // comment
-//        \\    if (x) {
-//        \\        bar();
-//        \\    }
-//        \\}
-//        \\
-//    );
-//}
-//
-//test "zig fmt: line comment between if block and else keyword" {
-//    try testCanonical(
-//        \\test "aoeu" {
-//        \\    // cexp(finite|nan +- i inf|nan) = nan + i nan
-//        \\    if ((hx & 0x7fffffff) != 0x7f800000) {
-//        \\        return Complex(f32).new(y - y, y - y);
-//        \\    }
-//        \\    // cexp(-inf +- i inf|nan) = 0 + i0
-//        \\    else if (hx & 0x80000000 != 0) {
-//        \\        return Complex(f32).new(0, 0);
-//        \\    }
-//        \\    // cexp(+inf +- i inf|nan) = inf + i nan
-//        \\    // another comment
-//        \\    else {
-//        \\        return Complex(f32).new(x, y - y);
-//        \\    }
-//        \\}
-//        \\
-//    );
-//}
-//
-//test "zig fmt: same line comments in expression" {
-//    try testCanonical(
-//        \\test "aoeu" {
-//        \\    const x = ( // a
-//        \\        0 // b
-//        \\    ); // c
-//        \\}
-//        \\
-//    );
-//}
-//
+test "zig fmt: comment after if before another if" {
+    try testCanonical(
+        \\test "aoeu" {
+        \\    // comment
+        \\    if (x) {
+        \\        bar();
+        \\    }
+        \\}
+        \\
+        \\test "aoeu" {
+        \\    if (x) {
+        \\        foo();
+        \\    }
+        \\    // comment
+        \\    if (x) {
+        \\        bar();
+        \\    }
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: line comment between if block and else keyword" {
+    try testCanonical(
+        \\test "aoeu" {
+        \\    // cexp(finite|nan +- i inf|nan) = nan + i nan
+        \\    if ((hx & 0x7fffffff) != 0x7f800000) {
+        \\        return Complex(f32).new(y - y, y - y);
+        \\    }
+        \\    // cexp(-inf +- i inf|nan) = 0 + i0
+        \\    else if (hx & 0x80000000 != 0) {
+        \\        return Complex(f32).new(0, 0);
+        \\    }
+        \\    // cexp(+inf +- i inf|nan) = inf + i nan
+        \\    // another comment
+        \\    else {
+        \\        return Complex(f32).new(x, y - y);
+        \\    }
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: same line comments in expression" {
+    try testCanonical(
+        \\test "aoeu" {
+        \\    const x = ( // a
+        \\        0 // b
+        \\    ); // c
+        \\}
+        \\
+    );
+}
+
 //test "zig fmt: add comma on last switch prong" {
 //    try testTransform(
 //        \\test "aoeu" {
@@ -2051,127 +2053,126 @@ test "zig fmt: ptr deref operator and unwrap optional operator" {
 //        \\
 //    );
 //}
-//
-//test "zig fmt: same-line comment after a statement" {
-//    try testCanonical(
-//        \\test "" {
-//        \\    a = b;
-//        \\    debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
-//        \\    a = b;
-//        \\}
-//        \\
-//    );
-//}
-//
-//test "zig fmt: same-line comment after var decl in struct" {
-//    try testCanonical(
-//        \\pub const vfs_cap_data = extern struct {
-//        \\    const Data = struct {}; // when on disk.
-//        \\};
-//        \\
-//    );
-//}
-//
-//test "zig fmt: same-line comment after field decl" {
-//    try testCanonical(
-//        \\pub const dirent = extern struct {
-//        \\    d_name: u8,
-//        \\    d_name: u8, // comment 1
-//        \\    d_name: u8,
-//        \\    d_name: u8, // comment 2
-//        \\    d_name: u8,
-//        \\};
-//        \\
-//    );
-//}
-//
-//test "zig fmt: same-line comment after switch prong" {
-//    try testCanonical(
-//        \\test "" {
-//        \\    switch (err) {
-//        \\        error.PathAlreadyExists => {}, // comment 2
-//        \\        else => return err, // comment 1
-//        \\    }
-//        \\}
-//        \\
-//    );
-//}
-//
-//test "zig fmt: same-line comment after non-block if expression" {
-//    try testCanonical(
-//        \\comptime {
-//        \\    if (sr > n_uword_bits - 1) // d > r
-//        \\        return 0;
-//        \\}
-//        \\
-//    );
-//}
-//
-//test "zig fmt: same-line comment on comptime expression" {
-//    try testCanonical(
-//        \\test "" {
-//        \\    comptime assert(@typeInfo(T) == .Int); // must pass an integer to absInt
-//        \\}
-//        \\
-//    );
-//}
 
-test "zig fmt: switch with empty body" {
+test "zig fmt: same-line comment after a statement" {
+    try testCanonical(
+        \\test "" {
+        \\    a = b;
+        \\    debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
+        \\    a = b;
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: same-line comment after var decl in struct" {
+    try testCanonical(
+        \\pub const vfs_cap_data = extern struct {
+        \\    const Data = struct {}; // when on disk.
+        \\};
+        \\
+    );
+}
+
+test "zig fmt: same-line comment after field decl" {
+    try testCanonical(
+        \\pub const dirent = extern struct {
+        \\    d_name: u8,
+        \\    d_name: u8, // comment 1
+        \\    d_name: u8,
+        \\    d_name: u8, // comment 2
+        \\    d_name: u8,
+        \\};
+        \\
+    );
+}
+
+test "zig fmt: same-line comment after switch prong" {
+    try testCanonical(
+        \\test "" {
+        \\    switch (err) {
+        \\        error.PathAlreadyExists => {}, // comment 2
+        \\        else => return err, // comment 1
+        \\    }
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: same-line comment after non-block if expression" {
+    try testCanonical(
+        \\comptime {
+        \\    if (sr > n_uword_bits - 1) // d > r
+        \\        return 0;
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: same-line comment on comptime expression" {
+    try testCanonical(
+        \\test "" {
+        \\    comptime assert(@typeInfo(T) == .Int); // must pass an integer to absInt
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: switch with empty body" {
+    try testCanonical(
+        \\test "" {
+        \\    foo() catch |err| switch (err) {};
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: line comments in struct initializer" {
+    try testCanonical(
+        \\fn foo() void {
+        \\    return Self{
+        \\        .a = b,
+        \\
+        \\        // Initialize these two fields to buffer_size so that
+        \\        // in `readFn` we treat the state as being able to read
+        \\        .start_index = buffer_size,
+        \\        .end_index = buffer_size,
+        \\
+        \\        // middle
+        \\
+        \\        .a = b,
+        \\
+        \\        // end
+        \\    };
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: first line comment in struct initializer" {
     try testCanonical(
-        \\test "" {
-        \\    foo() catch |err| switch (err) {};
+        \\pub fn acquire(self: *Self) HeldLock {
+        \\    return HeldLock{
+        \\        // guaranteed allocation elision
+        \\        .held = self.lock.acquire(),
+        \\        .value = &self.private_data,
+        \\    };
         \\}
         \\
     );
 }
 
-//test "zig fmt: line comments in struct initializer" {
-//    try testCanonical(
-//        \\fn foo() void {
-//        \\    return Self{
-//        \\        .a = b,
-//        \\
-//        \\        // Initialize these two fields to buffer_size so that
-//        \\        // in `readFn` we treat the state as being able to read
-//        \\        .start_index = buffer_size,
-//        \\        .end_index = buffer_size,
-//        \\
-//        \\        // middle
-//        \\
-//        \\        .a = b,
-//        \\
-//        \\        // end
-//        \\    };
-//        \\}
-//        \\
-//    );
-//}
-//
-//test "zig fmt: first line comment in struct initializer" {
-//    try testCanonical(
-//        \\pub fn acquire(self: *Self) HeldLock {
-//        \\    return HeldLock{
-//        \\        // guaranteed allocation elision
-//        \\        .held = self.lock.acquire(),
-//        \\        .value = &self.private_data,
-//        \\    };
-//        \\}
-//        \\
-//    );
-//}
-//
-//test "zig fmt: doc comments before struct field" {
-//    try testCanonical(
-//        \\pub const Allocator = struct {
-//        \\    /// Allocate byte_count bytes and return them in a slice, with the
-//        \\    /// slice's pointer aligned at least to alignment bytes.
-//        \\    allocFn: fn () void,
-//        \\};
-//        \\
-//    );
-//}
+test "zig fmt: doc comments before struct field" {
+    try testCanonical(
+        \\pub const Allocator = struct {
+        \\    /// Allocate byte_count bytes and return them in a slice, with the
+        \\    /// slice's pointer aligned at least to alignment bytes.
+        \\    allocFn: fn () void,
+        \\};
+        \\
+    );
+}
 
-// TODO: replace this with the next test case when possible
 test "zig fmt: error set declaration" {
     try testCanonical(
         \\const E = error{
@@ -2180,58 +2181,30 @@ test "zig fmt: error set declaration" {
         \\
         \\    C,
         \\};
+        \\
         \\const Error = error{
         \\    /// no more memory
         \\    OutOfMemory,
         \\};
+        \\
         \\const Error = error{
         \\    /// no more memory
         \\    OutOfMemory,
         \\
         \\    /// another
         \\    Another,
-        \\    /// and one more
-        \\    Another,
+        \\
+        \\    // end
         \\};
+        \\
         \\const Error = error{OutOfMemory};
         \\const Error = error{};
+        \\
         \\const Error = error{ OutOfMemory, OutOfTime };
         \\
     );
 }
 
-//test "zig fmt: error set declaration" {
-//    try testCanonical(
-//        \\const E = error{
-//        \\    A,
-//        \\    B,
-//        \\
-//        \\    C,
-//        \\};
-//        \\
-//        \\const Error = error{
-//        \\    /// no more memory
-//        \\    OutOfMemory,
-//        \\};
-//        \\
-//        \\const Error = error{
-//        \\    /// no more memory
-//        \\    OutOfMemory,
-//        \\
-//        \\    /// another
-//        \\    Another,
-//        \\
-//        \\    // end
-//        \\};
-//        \\
-//        \\const Error = error{OutOfMemory};
-//        \\const Error = error{};
-//        \\
-//        \\const Error = error{ OutOfMemory, OutOfTime };
-//        \\
-//    );
-//}
-
 test "zig fmt: union(enum(u32)) with assigned enum values" {
     try testCanonical(
         \\const MultipleChoice = union(enum(u32)) {
@@ -2255,110 +2228,110 @@ test "zig fmt: resume from suspend block" {
     );
 }
 
-//test "zig fmt: comments before error set decl" {
-//    try testCanonical(
-//        \\const UnexpectedError = error{
-//        \\    /// The Operating System returned an undocumented error code.
-//        \\    Unexpected,
-//        \\    // another
-//        \\    Another,
-//        \\
-//        \\    // in between
-//        \\
-//        \\    // at end
-//        \\};
-//        \\
-//    );
-//}
-//
-//test "zig fmt: comments before switch prong" {
-//    try testCanonical(
-//        \\test "" {
-//        \\    switch (err) {
-//        \\        error.PathAlreadyExists => continue,
-//        \\
-//        \\        // comment 1
-//        \\
-//        \\        // comment 2
-//        \\        else => return err,
-//        \\        // at end
-//        \\    }
-//        \\}
-//        \\
-//    );
-//}
-//
-//test "zig fmt: comments before var decl in struct" {
-//    try testCanonical(
-//        \\pub const vfs_cap_data = extern struct {
-//        \\    // All of these are mandated as little endian
-//        \\    // when on disk.
-//        \\    const Data = struct {
-//        \\        permitted: u32,
-//        \\        inheritable: u32,
-//        \\    };
-//        \\
-//        \\    // in between
-//        \\
-//        \\    /// All of these are mandated as little endian
-//        \\    /// when on disk.
-//        \\    const Data = struct {
-//        \\        permitted: u32,
-//        \\        inheritable: u32,
-//        \\    };
-//        \\
-//        \\    // at end
-//        \\};
-//        \\
-//    );
-//}
-//
-//test "zig fmt: array literal with 1 item on 1 line" {
-//    try testCanonical(
-//        \\var s = []const u64{0} ** 25;
-//        \\
-//    );
-//}
-//
-//test "zig fmt: comments before global variables" {
-//    try testCanonical(
-//        \\/// Foo copies keys and values before they go into the map, and
-//        \\/// frees them when they get removed.
-//        \\pub const Foo = struct {};
-//        \\
-//    );
-//}
-//
-//test "zig fmt: comments in statements" {
-//    try testCanonical(
-//        \\test "std" {
-//        \\    // statement comment
-//        \\    _ = @import("foo/bar.zig");
-//        \\
-//        \\    // middle
-//        \\    // middle2
-//        \\
-//        \\    // end
-//        \\}
-//        \\
-//    );
-//}
-//
-//test "zig fmt: comments before test decl" {
-//    try testCanonical(
-//        \\/// top level doc comment
-//        \\test "hi" {}
-//        \\
-//        \\// top level normal comment
-//        \\test "hi" {}
-//        \\
-//        \\// middle
-//        \\
-//        \\// end
-//        \\
-//    );
-//}
-//
+test "zig fmt: comments before error set decl" {
+    try testCanonical(
+        \\const UnexpectedError = error{
+        \\    /// The Operating System returned an undocumented error code.
+        \\    Unexpected,
+        \\    // another
+        \\    Another,
+        \\
+        \\    // in between
+        \\
+        \\    // at end
+        \\};
+        \\
+    );
+}
+
+test "zig fmt: comments before switch prong" {
+    try testCanonical(
+        \\test "" {
+        \\    switch (err) {
+        \\        error.PathAlreadyExists => continue,
+        \\
+        \\        // comment 1
+        \\
+        \\        // comment 2
+        \\        else => return err,
+        \\        // at end
+        \\    }
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: comments before var decl in struct" {
+    try testCanonical(
+        \\pub const vfs_cap_data = extern struct {
+        \\    // All of these are mandated as little endian
+        \\    // when on disk.
+        \\    const Data = struct {
+        \\        permitted: u32,
+        \\        inheritable: u32,
+        \\    };
+        \\
+        \\    // in between
+        \\
+        \\    /// All of these are mandated as little endian
+        \\    /// when on disk.
+        \\    const Data = struct {
+        \\        permitted: u32,
+        \\        inheritable: u32,
+        \\    };
+        \\
+        \\    // at end
+        \\};
+        \\
+    );
+}
+
+test "zig fmt: array literal with 1 item on 1 line" {
+    try testCanonical(
+        \\var s = []const u64{0} ** 25;
+        \\
+    );
+}
+
+test "zig fmt: comments before global variables" {
+    try testCanonical(
+        \\/// Foo copies keys and values before they go into the map, and
+        \\/// frees them when they get removed.
+        \\pub const Foo = struct {};
+        \\
+    );
+}
+
+test "zig fmt: comments in statements" {
+    try testCanonical(
+        \\test "std" {
+        \\    // statement comment
+        \\    _ = @import("foo/bar.zig");
+        \\
+        \\    // middle
+        \\    // middle2
+        \\
+        \\    // end
+        \\}
+        \\
+    );
+}
+
+test "zig fmt: comments before test decl" {
+    try testCanonical(
+        \\/// top level doc comment
+        \\test "hi" {}
+        \\
+        \\// top level normal comment
+        \\test "hi" {}
+        \\
+        \\// middle
+        \\
+        \\// end
+        \\
+    );
+}
+
 //test "zig fmt: preserve spacing" {
 //    try testCanonical(
 //        \\const std = @import("std");
@@ -2373,7 +2346,7 @@ test "zig fmt: resume from suspend block" {
 //        \\
 //    );
 //}
-//
+
 //test "zig fmt: return types" {
 //    try testCanonical(
 //        \\pub fn main() !void {}
@@ -2798,43 +2771,43 @@ test "zig fmt: union declaration" {
     );
 }
 
-//test "zig fmt: arrays" {
-//    try testCanonical(
-//        \\test "test array" {
-//        \\    const a: [2]u8 = [2]u8{
-//        \\        1,
-//        \\        2,
-//        \\    };
-//        \\    const a: [2]u8 = []u8{
-//        \\        1,
-//        \\        2,
-//        \\    };
-//        \\    const a: [0]u8 = []u8{};
-//        \\    const x: [4:0]u8 = undefined;
-//        \\}
-//        \\
-//    );
-//}
+test "zig fmt: arrays" {
+    try testCanonical(
+        \\test "test array" {
+        \\    const a: [2]u8 = [2]u8{
+        \\        1,
+        \\        2,
+        \\    };
+        \\    const a: [2]u8 = []u8{
+        \\        1,
+        \\        2,
+        \\    };
+        \\    const a: [0]u8 = []u8{};
+        \\    const x: [4:0]u8 = undefined;
+        \\}
+        \\
+    );
+}
 
-//test "zig fmt: container initializers" {
-//    try testCanonical(
-//        \\const a0 = []u8{};
-//        \\const a1 = []u8{1};
-//        \\const a2 = []u8{
-//        \\    1,
-//        \\    2,
-//        \\    3,
-//        \\    4,
-//        \\};
-//        \\const s0 = S{};
-//        \\const s1 = S{ .a = 1 };
-//        \\const s2 = S{
-//        \\    .a = 1,
-//        \\    .b = 2,
-//        \\};
-//        \\
-//    );
-//}
+test "zig fmt: container initializers" {
+    try testCanonical(
+        \\const a0 = []u8{};
+        \\const a1 = []u8{1};
+        \\const a2 = []u8{
+        \\    1,
+        \\    2,
+        \\    3,
+        \\    4,
+        \\};
+        \\const s0 = S{};
+        \\const s1 = S{ .a = 1 };
+        \\const s2 = S{
+        \\    .a = 1,
+        \\    .b = 2,
+        \\};
+        \\
+    );
+}
 
 test "zig fmt: catch" {
     try testCanonical(
@@ -3563,62 +3536,62 @@ test "zig fmt: integer literals with underscore separators" {
     );
 }
 
-//test "zig fmt: hex literals with underscore separators" {
-//    try testTransform(
-//        \\pub fn orMask(a: [ 1_000 ]u64, b: [  1_000]  u64) [1_000]u64 {
-//        \\    var c: [1_000]u64 =  [1]u64{ 0xFFFF_FFFF_FFFF_FFFF}**1_000;
-//        \\    for (c [ 0_0 .. ]) |_, i| {
-//        \\        c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;
-//        \\    }
-//        \\    return c;
-//        \\}
-//        \\
-//        \\
-//    ,
-//        \\pub fn orMask(a: [1_000]u64, b: [1_000]u64) [1_000]u64 {
-//        \\    var c: [1_000]u64 = [1]u64{0xFFFF_FFFF_FFFF_FFFF} ** 1_000;
-//        \\    for (c[0_0..]) |_, i| {
-//        \\        c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;
-//        \\    }
-//        \\    return c;
-//        \\}
-//        \\
-//    );
-//}
+test "zig fmt: hex literals with underscore separators" {
+    try testTransform(
+        \\pub fn orMask(a: [ 1_000 ]u64, b: [  1_000]  u64) [1_000]u64 {
+        \\    var c: [1_000]u64 =  [1]u64{ 0xFFFF_FFFF_FFFF_FFFF}**1_000;
+        \\    for (c [ 0_0 .. ]) |_, i| {
+        \\        c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;
+        \\    }
+        \\    return c;
+        \\}
+        \\
+        \\
+    ,
+        \\pub fn orMask(a: [1_000]u64, b: [1_000]u64) [1_000]u64 {
+        \\    var c: [1_000]u64 = [1]u64{0xFFFF_FFFF_FFFF_FFFF} ** 1_000;
+        \\    for (c[0_0..]) |_, i| {
+        \\        c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;
+        \\    }
+        \\    return c;
+        \\}
+        \\
+    );
+}
 
-//test "zig fmt: decimal float literals with underscore separators" {
-//    try testTransform(
-//        \\pub fn main() void {
-//        \\    const a:f64=(10.0e-0+(10.e+0))+10_00.00_00e-2+00_00.00_10e+4;
-//        \\    const b:f64=010.0--0_10.+0_1_0.0_0+1e2;
-//        \\    std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
-//        \\}
-//    ,
-//        \\pub fn main() void {
-//        \\    const a: f64 = (10.0e-0 + (10.e+0)) + 10_00.00_00e-2 + 00_00.00_10e+4;
-//        \\    const b: f64 = 010.0 - -0_10. + 0_1_0.0_0 + 1e2;
-//        \\    std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
-//        \\}
-//        \\
-//    );
-//}
+test "zig fmt: decimal float literals with underscore separators" {
+    try testTransform(
+        \\pub fn main() void {
+        \\    const a:f64=(10.0e-0+(10.e+0))+10_00.00_00e-2+00_00.00_10e+4;
+        \\    const b:f64=010.0--0_10.+0_1_0.0_0+1e2;
+        \\    std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
+        \\}
+    ,
+        \\pub fn main() void {
+        \\    const a: f64 = (10.0e-0 + (10.e+0)) + 10_00.00_00e-2 + 00_00.00_10e+4;
+        \\    const b: f64 = 010.0 - -0_10. + 0_1_0.0_0 + 1e2;
+        \\    std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
+        \\}
+        \\
+    );
+}
 
-//test "zig fmt: hexadeciaml float literals with underscore separators" {
-//    try testTransform(
-//        \\pub fn main() void {
-//        \\    const a: f64 = (0x10.0p-0+(0x10.p+0))+0x10_00.00_00p-8+0x00_00.00_10p+16;
-//        \\    const b: f64 = 0x0010.0--0x00_10.+0x10.00+0x1p4;
-//        \\    std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
-//        \\}
-//    ,
-//        \\pub fn main() void {
-//        \\    const a: f64 = (0x10.0p-0 + (0x10.p+0)) + 0x10_00.00_00p-8 + 0x00_00.00_10p+16;
-//        \\    const b: f64 = 0x0010.0 - -0x00_10. + 0x10.00 + 0x1p4;
-//        \\    std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
-//        \\}
-//        \\
-//    );
-//}
+test "zig fmt: hexadeciaml float literals with underscore separators" {
+    try testTransform(
+        \\pub fn main() void {
+        \\    const a: f64 = (0x10.0p-0+(0x10.p+0))+0x10_00.00_00p-8+0x00_00.00_10p+16;
+        \\    const b: f64 = 0x0010.0--0x00_10.+0x10.00+0x1p4;
+        \\    std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
+        \\}
+    ,
+        \\pub fn main() void {
+        \\    const a: f64 = (0x10.0p-0 + (0x10.p+0)) + 0x10_00.00_00p-8 + 0x00_00.00_10p+16;
+        \\    const b: f64 = 0x0010.0 - -0x00_10. + 0x10.00 + 0x1p4;
+        \\    std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
+        \\}
+        \\
+    );
+}
 
 //test "zig fmt: C var args" {
 //    try testCanonical(
lib/std/zig/render.zig
@@ -37,66 +37,56 @@ pub fn render(gpa: *mem.Allocator, tree: ast.Tree) Error![]u8 {
     return buffer.toOwnedSlice();
 }
 
-/// Assumes there are no tokens in between start and end.
-fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize, prefix: []const u8) Error!usize {
+/// Assumes that start is the first byte past the previous token and
+/// that end is the last byte before the next token.
+fn renderCommentsAndNewlines(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!bool {
     var index: usize = start;
-    var count: usize = 0;
-    while (true) {
-        const comment_start = index +
-            (mem.indexOf(u8, tree.source[index..end], "//") orelse return count);
+    while (mem.indexOf(u8, tree.source[index..end], "//")) |offset| {
+        const comment_start = index + offset;
         const newline = comment_start +
             mem.indexOfScalar(u8, tree.source[comment_start..end], '\n').?;
         const untrimmed_comment = tree.source[comment_start..newline];
         const trimmed_comment = mem.trimRight(u8, untrimmed_comment, " \r\t");
-        if (count == 0) {
-            count += 1;
-            try ais.writer().writeAll(prefix);
-        } else {
-            // If another newline occurs between prev comment and this one
-            // we honor it, but not any additional ones.
-            if (mem.indexOfScalar(u8, tree.source[index..comment_start], '\n') != null) {
-                try ais.insertNewline();
-            }
+
+        // Leave up to one empty line before the comment
+        if (index == start and mem.containsAtLeast(u8, tree.source[index..comment_start], 2, "\n")) {
+            try ais.insertNewline();
+            try ais.insertNewline();
+        } else if (mem.indexOfScalar(u8, tree.source[index..comment_start], '\n') != null) {
+            // Respect the newline directly before the comment. This allows an
+            // empty line between comments
+            try ais.insertNewline();
+        } else if (index == start and start != 0) {
+            // If the comment is on the same line as the token before it,
+            // prefix it with a single space
+            try ais.writer().writeByte(' ');
         }
+
         try ais.writer().print("{s}\n", .{trimmed_comment});
         index = newline + 1;
     }
+
+    // Leave up to one empty line if present in the source
+    if (index > start) index -= 1;
+    if (end != tree.source.len and mem.containsAtLeast(u8, tree.source[index..end], 2, "\n")) {
+        try ais.insertNewline();
+    }
+
+    return index != start;
 }
 
 fn renderRoot(ais: *Ais, tree: ast.Tree) Error!void {
     // Render all the line comments at the beginning of the file.
     const src_start: usize = if (mem.startsWith(u8, tree.source, "\xEF\xBB\xBF")) 3 else 0;
     const comment_end_loc: usize = tree.tokens.items(.start)[0];
-    _ = try renderComments(ais, tree, src_start, comment_end_loc, "");
+    _ = try renderCommentsAndNewlines(ais, tree, src_start, comment_end_loc);
 
     // Root is always index 0.
     const nodes_data = tree.nodes.items(.data);
     const root_decls = tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs];
 
-    return renderAllMembers(ais, tree, root_decls);
-}
-
-fn renderAllMembers(ais: *Ais, tree: ast.Tree, members: []const ast.Node.Index) Error!void {
-    if (members.len == 0) return;
-
-    const first_member = members[0];
-    try renderMember(ais, tree, first_member, .Newline);
-
-    for (members[1..]) |member| {
-        try renderExtraNewline(ais, tree, member);
-        try renderMember(ais, tree, member, .Newline);
-    }
-}
-
-fn renderExtraNewline(ais: *Ais, tree: ast.Tree, node: ast.Node.Index) Error!void {
-    return renderExtraNewlineToken(ais, tree, tree.firstToken(node));
-}
-
-fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, first_token: ast.TokenIndex) Error!void {
-    if (first_token == 0) return;
-    const token_starts = tree.tokens.items(.start);
-    if (tree.tokenLocation(token_starts[first_token - 1], first_token).line >= 2) {
-        return ais.insertNewline();
+    for (root_decls) |decl| {
+        try renderMember(ais, tree, decl, .Newline);
     }
 }
 
@@ -499,9 +489,11 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
         },
 
         .GroupedExpression => {
-            try renderToken(ais, tree, main_tokens[node], .None);
+            ais.pushIndentNextLine();
+            try renderToken(ais, tree, main_tokens[node], .None); // lparen
             try renderExpression(ais, tree, datas[node].lhs, .None);
-            return renderToken(ais, tree, datas[node].rhs, space);
+            ais.popIndent();
+            return renderToken(ais, tree, datas[node].rhs, space); // rparen
         },
 
         .ContainerDecl,
@@ -552,7 +544,6 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
                 ais.pushIndent();
                 var i = lbrace + 1;
                 while (i < rbrace) : (i += 1) {
-                    try renderExtraNewlineToken(ais, tree, i);
                     switch (token_tags[i]) {
                         .DocComment => try renderToken(ais, tree, i, .Newline),
                         .Identifier => try renderToken(ais, tree, i, .Comma),
@@ -635,11 +626,9 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
                 try renderToken(ais, tree, rparen + 1, .None); // lbrace
                 return renderToken(ais, tree, rparen + 2, space); // rbrace
             }
+            ais.pushIndentNextLine();
             try renderToken(ais, tree, rparen + 1, .Newline); // lbrace
-            ais.pushIndent();
-            try renderExpression(ais, tree, cases[0], .Comma);
-            for (cases[1..]) |case| {
-                try renderExtraNewline(ais, tree, case);
+            for (cases) |case| {
                 try renderExpression(ais, tree, case, .Comma);
             }
             ais.popIndent();
@@ -1469,9 +1458,7 @@ fn renderSwitchCase(
         try renderExpression(ais, tree, switch_case.ast.values[0], .Space);
     } else if (trailing_comma) {
         // Render each value on a new line
-        try renderExpression(ais, tree, switch_case.ast.values[0], .Comma);
-        for (switch_case.ast.values[1..]) |value_expr| {
-            try renderExtraNewline(ais, tree, value_expr);
+        for (switch_case.ast.values) |value_expr| {
             try renderExpression(ais, tree, value_expr, .Comma);
         }
     } else {
@@ -1519,12 +1506,14 @@ fn renderBlock(
     }
 
     if (statements.len == 0) {
+        ais.pushIndentNextLine();
         try renderToken(ais, tree, lbrace, .None);
+        ais.popIndent();
         return renderToken(ais, tree, lbrace + 1, space); // rbrace
     }
 
+    ais.pushIndentNextLine();
     try renderToken(ais, tree, lbrace, .Newline);
-    ais.pushIndent();
     for (statements) |stmt, i| {
         switch (node_tags[stmt]) {
             .GlobalVarDecl => try renderVarDecl(ais, tree, tree.globalVarDecl(stmt)),
@@ -1533,9 +1522,6 @@ fn renderBlock(
             .AlignedVarDecl => try renderVarDecl(ais, tree, tree.alignedVarDecl(stmt)),
             else => try renderExpression(ais, tree, stmt, .Semicolon),
         }
-        if (i + 1 < statements.len) {
-            try renderExtraNewline(ais, tree, statements[i + 1]);
-        }
     }
     ais.popIndent();
 
@@ -1566,18 +1552,14 @@ fn renderStructInit(
         ais.pushIndent();
         try renderToken(ais, tree, struct_init.ast.lbrace, .Newline);
 
-        try renderToken(ais, tree, struct_init.ast.lbrace + 1, .None); // .
-        try renderToken(ais, tree, struct_init.ast.lbrace + 2, .Space); // name
-        try renderToken(ais, tree, struct_init.ast.lbrace + 3, .Space); // =
-        try renderExpression(ais, tree, struct_init.ast.fields[0], .Comma);
-
-        for (struct_init.ast.fields[1..]) |field_init| {
+        for (struct_init.ast.fields) |field_init| {
             const init_token = tree.firstToken(field_init);
             try renderToken(ais, tree, init_token - 3, .None); // .
             try renderToken(ais, tree, init_token - 2, .Space); // name
             try renderToken(ais, tree, init_token - 1, .Space); // =
-            try renderExpressionNewlined(ais, tree, field_init, .Comma);
+            try renderExpression(ais, tree, field_init, .Comma);
         }
+
         ais.popIndent();
         return renderToken(ais, tree, last_field_token + 2, space); // rbrace
     } else {
@@ -1620,9 +1602,8 @@ fn renderArrayInit(
         ais.pushIndent();
         try renderToken(ais, tree, array_init.ast.lbrace, .Newline);
 
-        try renderExpression(ais, tree, array_init.ast.elements[0], .Comma);
-        for (array_init.ast.elements[1..]) |elem| {
-            try renderExpressionNewlined(ais, tree, elem, .Comma);
+        for (array_init.ast.elements) |elem| {
+            try renderExpression(ais, tree, elem, .Comma);
         }
 
         ais.popIndent();
@@ -1693,7 +1674,7 @@ fn renderContainerDecl(
     const last_member_token = tree.lastToken(last_member);
     const rbrace = switch (token_tags[last_member_token + 1]) {
         .DocComment => last_member_token + 2,
-        .Comma => switch (token_tags[last_member_token + 2]) {
+        .Comma, .Semicolon => switch (token_tags[last_member_token + 2]) {
             .DocComment => last_member_token + 3,
             .RBrace => last_member_token + 2,
             else => unreachable,
@@ -1719,7 +1700,9 @@ fn renderContainerDecl(
     // One member per line.
     ais.pushIndent();
     try renderToken(ais, tree, lbrace, .Newline); // lbrace
-    try renderAllMembers(ais, tree, container_decl.ast.members);
+    for (container_decl.ast.members) |member| {
+        try renderMember(ais, tree, member, .Newline);
+    }
     ais.popIndent();
 
     return renderToken(ais, tree, rbrace, space); // rbrace
@@ -1781,7 +1764,6 @@ fn renderAsm(
 
                 const comma = tree.firstToken(next_asm_output) - 1;
                 try renderToken(ais, tree, comma, .Newline); // ,
-                try renderExtraNewlineToken(ais, tree, tree.firstToken(next_asm_output));
             } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) {
                 try renderAsmOutput(ais, tree, asm_output, .Newline);
                 ais.popIndent();
@@ -1813,7 +1795,6 @@ fn renderAsm(
 
                 const first_token = tree.firstToken(next_asm_input);
                 try renderToken(ais, tree, first_token - 1, .Newline); // ,
-                try renderExtraNewlineToken(ais, tree, first_token);
             } else if (asm_node.first_clobber == null) {
                 try renderAsmInput(ais, tree, asm_input, .Newline);
                 ais.popIndent();
@@ -1894,8 +1875,6 @@ fn renderCall(
                 try renderToken(ais, tree, comma, Space.Newline); // ,
 
                 if (is_multiline_string) ais.pushIndent();
-
-                try renderExtraNewline(ais, tree, params[i + 1]);
             } else {
                 try renderExpression(ais, tree, param_node, Space.Comma);
             }
@@ -1929,22 +1908,6 @@ fn renderExpressionComma(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space:
     }
 }
 
-/// Render an expression, but first insert an extra newline if the previous token is 2 or
-/// more lines away.
-fn renderExpressionNewlined(
-    ais: *Ais,
-    tree: ast.Tree,
-    node: ast.Node.Index,
-    space: Space,
-) Error!void {
-    const token_starts = tree.tokens.items(.start);
-    const first_token = tree.firstToken(node);
-    if (tree.tokenLocation(token_starts[first_token - 1], first_token).line >= 2) {
-        try ais.insertNewline();
-    }
-    return renderExpression(ais, tree, node, space);
-}
-
 fn renderTokenComma(ais: *Ais, tree: ast.Tree, token: ast.TokenIndex, space: Space) Error!void {
     const token_tags = tree.tokens.items(.tag);
     const maybe_comma = token + 1;
@@ -1996,40 +1959,39 @@ fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Sp
 
     switch (space) {
         .NoComment => {},
-        .None => {},
+        .None => _ = try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]),
         .Comma => {
-            const count = try renderComments(ais, tree, token_start + lexeme.len, token_starts[token_index + 1], ", ");
-            if (count == 0 and token_tags[token_index + 1] == .Comma) {
-                return renderToken(ais, tree, token_index + 1, Space.Newline);
-            }
-            try ais.writer().writeAll(",");
-
-            if (token_tags[token_index + 2] != .MultilineStringLiteralLine) {
-                try ais.insertNewline();
+            const comment = try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]);
+            if (token_tags[token_index + 1] == .Comma) {
+                return renderToken(ais, tree, token_index + 1, .Newline);
+            } else if (!comment) {
+                return ais.insertNewline();
             }
         },
         .CommaSpace => {
-            _ = try renderComments(ais, tree, token_start + lexeme.len, token_starts[token_index + 1], "");
+            const comment = try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]);
             if (token_tags[token_index + 1] == .Comma) {
                 return renderToken(ais, tree, token_index + 1, .Space);
-            } else {
+            } else if (!comment) {
                 return ais.writer().writeByte(' ');
             }
         },
         .Semicolon => {
-            _ = try renderComments(ais, tree, token_start + lexeme.len, token_starts[token_index + 1], "");
+            const comment = try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]);
             if (token_tags[token_index + 1] == .Semicolon) {
                 return renderToken(ais, tree, token_index + 1, .Newline);
-            } else {
+            } else if (!comment) {
                 return ais.insertNewline();
             }
         },
         .Space => {
-            _ = try renderComments(ais, tree, token_start + lexeme.len, token_starts[token_index + 1], "");
-            return ais.writer().writeByte(' ');
+            const comment = try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]);
+            if (!comment) {
+                return ais.writer().writeByte(' ');
+            }
         },
         .Newline => {
-            if (token_tags[token_index + 1] != .MultilineStringLiteralLine) {
+            if (!try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1])) {
                 try ais.insertNewline();
             }
         },