@zigc/lib 0.16.0-dev.3070 → 0.16.0-dev.3091

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -338,9 +338,16 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
338
338
  try ais.maybeInsertNewline();
339
339
 
340
340
  const first_tok, const last_tok = tree.nodeData(node).token_and_token;
341
- for (first_tok..last_tok + 1) |i| {
341
+ for (first_tok..last_tok) |i| {
342
342
  try renderToken(r, @intCast(i), .newline);
343
343
  }
344
+ if (space != .skip) {
345
+ try renderToken(r, last_tok, .newline);
346
+ } else {
347
+ try renderToken(r, last_tok, .skip);
348
+ try ais.insertNewline(); // A newline is part of the token, so it still needs
349
+ // rendered here.
350
+ }
344
351
 
345
352
  const next_token = last_tok + 1;
346
353
  const next_token_tag = tree.tokenTag(next_token);
@@ -645,10 +652,12 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
645
652
  const lhs, const rhs = tree.nodeData(node).node_and_node;
646
653
  const lbracket = tree.firstToken(rhs) - 1;
647
654
  const rbracket = tree.lastToken(rhs) + 1;
655
+ try renderExpression(r, lhs, .none);
656
+ // One lien check must come after rendering lhs since it can influence
657
+ // isLineOverIndented
648
658
  const one_line = tree.tokensOnSameLine(lbracket, rbracket) and
649
- !becomesMultilineExpr(tree, rhs);
659
+ !try rendersMultiline(r, rhs);
650
660
  const inner_space = if (one_line) Space.none else Space.newline;
651
- try renderExpression(r, lhs, .none);
652
661
  try ais.pushIndent(.normal);
653
662
  try renderToken(r, lbracket, inner_space); // [
654
663
  try renderExpression(r, rhs, inner_space);
@@ -793,34 +802,47 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
793
802
  canonicalize: {
794
803
  if (params.len != 1) break :canonicalize;
795
804
 
796
- const CastKind = enum {
805
+ const CastKind = enum(u8) {
797
806
  ptrCast,
798
807
  alignCast,
799
808
  addrSpaceCast,
800
809
  constCast,
801
810
  volatileCast,
802
811
  };
803
- const kind = meta.stringToEnum(CastKind, tree.tokenSlice(builtin_token)[1..]) orelse break :canonicalize;
812
+ const kind = meta.stringToEnum(
813
+ CastKind,
814
+ tree.tokenSlice(builtin_token)[1..],
815
+ ) orelse break :canonicalize;
804
816
 
805
817
  var cast_map = std.EnumMap(CastKind, Ast.TokenIndex).init(.{});
806
818
  cast_map.put(kind, builtin_token);
807
819
 
808
820
  var casts_before: usize = 0;
809
- if (builtin_token >= 2) {
810
- var prev_builtin_token = builtin_token - 2;
811
- while (tree.tokenTag(prev_builtin_token) == .builtin) : (prev_builtin_token -= 2) {
812
- const prev_kind = meta.stringToEnum(CastKind, tree.tokenSlice(prev_builtin_token)[1..]) orelse break;
813
- if (cast_map.contains(prev_kind)) break :canonicalize;
814
- cast_map.put(prev_kind, prev_builtin_token);
815
- casts_before += 1;
816
- }
821
+ var prev_builtin_token = builtin_token;
822
+ while (prev_builtin_token >= 2) {
823
+ prev_builtin_token -= 2;
824
+ if (tree.tokenTag(prev_builtin_token) != .builtin) break;
825
+ const builtin_name = tree.tokenSlice(prev_builtin_token)[1..];
826
+ const prev_kind = meta.stringToEnum(CastKind, builtin_name) orelse break;
827
+ if (cast_map.contains(prev_kind)) break :canonicalize;
828
+ // This must be checked after so that cast builtins as arguments to other
829
+ // builtins containing comments are reordered.
830
+ if (hasComment(tree, prev_builtin_token, prev_builtin_token + 2))
831
+ break :canonicalize;
832
+ cast_map.put(prev_kind, prev_builtin_token);
833
+ casts_before += 1;
817
834
  }
818
835
 
819
836
  var next_builtin_token = builtin_token + 2;
820
- while (tree.tokenTag(next_builtin_token) == .builtin) : (next_builtin_token += 2) {
821
- const next_kind = meta.stringToEnum(CastKind, tree.tokenSlice(next_builtin_token)[1..]) orelse break;
837
+ while (true) {
838
+ if (hasComment(tree, next_builtin_token - 2, next_builtin_token))
839
+ break :canonicalize;
840
+ if (tree.tokenTag(next_builtin_token) != .builtin) break;
841
+ const builtin_name = tree.tokenSlice(next_builtin_token)[1..];
842
+ const next_kind = meta.stringToEnum(CastKind, builtin_name) orelse break;
822
843
  if (cast_map.contains(next_kind)) break :canonicalize;
823
844
  cast_map.put(next_kind, next_builtin_token);
845
+ next_builtin_token += 2;
824
846
  }
825
847
 
826
848
  var it = cast_map.iterator();
@@ -931,380 +953,61 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v
931
953
  }
932
954
  }
933
955
 
934
- /// Same as becomesMultilineExpr, but returns false when `node == .none`
935
- fn optBecomesMultilineExpr(tree: Ast, node: Ast.Node.OptionalIndex) bool {
936
- return if (node.unwrap()) |payload| becomesMultilineExpr(tree, payload) else false;
937
- }
956
+ fn drainNoNewline(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
957
+ if (std.mem.indexOfScalar(u8, w.buffered(), '\n') != null) {
958
+ return error.WriteFailed;
959
+ }
938
960
 
939
- /// May return false if `node` is already multiline
940
- fn becomesMultilineExpr(tree: Ast, node: Ast.Node.Index) bool {
941
- // Conditions related to comments, doc comments, and multiline string literals are ignored
942
- // since they always go to the end of the line, which already make them a multi-line
943
- // expression (since they contain a newline).
944
- switch (tree.nodeTag(node)) {
945
- .identifier,
946
- .number_literal,
947
- .char_literal,
948
- .unreachable_literal,
949
- .anyframe_literal,
950
- .string_literal,
951
- .multiline_string_literal,
952
- .error_value,
953
- .enum_literal,
954
- => return false,
955
- .container_decl_trailing,
956
- .container_decl_arg_trailing,
957
- .container_decl_two_trailing,
958
- .tagged_union_trailing,
959
- .tagged_union_enum_tag_trailing,
960
- .tagged_union_two_trailing,
961
- .switch_comma,
962
- .builtin_call_two_comma,
963
- .builtin_call_comma,
964
- .call_one_comma,
965
- .call_comma,
966
- .struct_init_one_comma,
967
- .struct_init_dot_two_comma,
968
- .struct_init_dot_comma,
969
- .struct_init_comma,
970
- .array_init_one_comma,
971
- .array_init_dot_two_comma,
972
- .array_init_dot_comma,
973
- .array_init_comma,
974
- // The following always have a non-zero amount of members
975
- // which is also the condition for them to be multi-line.
976
- .block,
977
- .block_semicolon,
978
- => return true,
979
- .block_two,
980
- .block_two_semicolon,
981
- => return tree.nodeData(node).opt_node_and_opt_node[0] != .none,
982
- .container_decl,
983
- .container_decl_arg,
984
- .container_decl_two,
985
- .tagged_union,
986
- .tagged_union_enum_tag,
987
- .tagged_union_two,
988
- => {
989
- var buf: [2]Ast.Node.Index = undefined;
990
- const full = tree.fullContainerDecl(&buf, node).?;
991
- if (full.ast.arg.unwrap()) |arg| {
992
- if (becomesMultilineExpr(tree, arg))
993
- return true;
994
- }
995
- // This does the same checks as `isOneLineContainerDecl`, however it avoids unnecessary
996
- // checks related to comments and multiline strings, which would mean the container is
997
- // already multiple lines.
998
- for (full.ast.members) |member| {
999
- if (tree.fullContainerField(member)) |field_full| {
1000
- for ([_]Ast.Node.OptionalIndex{
1001
- field_full.ast.type_expr,
1002
- field_full.ast.align_expr,
1003
- field_full.ast.value_expr,
1004
- }) |opt_expr| {
1005
- if (opt_expr.unwrap()) |expr| {
1006
- if (becomesMultilineExpr(tree, expr))
1007
- return true;
1008
- }
1009
- }
1010
- } else return true;
1011
- }
1012
- return false;
1013
- },
1014
- .error_set_decl => {
1015
- const lbrace, const rbrace = tree.nodeData(node).token_and_token;
1016
- return !isOneLineErrorSetDecl(tree, lbrace, rbrace);
1017
- },
1018
- .@"switch" => {
1019
- const op, const extra_index = tree.nodeData(node).node_and_extra;
1020
- const case_range = tree.extraData(extra_index, Ast.Node.SubRange);
1021
- return @intFromEnum(case_range.end) - @intFromEnum(case_range.start) != 0 or
1022
- becomesMultilineExpr(tree, op);
1023
- },
1024
- .for_simple, .@"for" => {
1025
- const full = tree.fullFor(node).?;
1026
- if (becomesMultilineExpr(tree, full.ast.then_expr) or
1027
- optBecomesMultilineExpr(tree, full.ast.else_expr))
1028
- return true;
1029
-
1030
- for (full.ast.inputs) |expr| {
1031
- if (if (tree.nodeTag(expr) == .for_range) blk: {
1032
- const lhs, const rhs = tree.nodeData(expr).node_and_opt_node;
1033
- break :blk becomesMultilineExpr(tree, lhs) or optBecomesMultilineExpr(tree, rhs);
1034
- } else becomesMultilineExpr(tree, expr))
1035
- return true;
1036
- }
1037
- const final_input_expr = full.ast.inputs[full.ast.inputs.len - 1];
1038
- if (tree.tokenTag(tree.lastToken(final_input_expr) + 1) == .comma)
1039
- return true;
1040
-
1041
- const token_tags = tree.tokens.items(.tag);
1042
- const payload = full.payload_token;
1043
- const pipe = std.mem.indexOfScalarPos(Token.Tag, token_tags, payload, .pipe).?;
1044
- return token_tags[@intCast(pipe - 1)] == .comma;
1045
- },
1046
- .while_simple,
1047
- .while_cont,
1048
- .@"while",
1049
- => {
1050
- const full = tree.fullWhile(node).?;
1051
- return becomesMultilineExpr(tree, full.ast.cond_expr) or
1052
- becomesMultilineExpr(tree, full.ast.then_expr) or
1053
- optBecomesMultilineExpr(tree, full.ast.cont_expr) or
1054
- optBecomesMultilineExpr(tree, full.ast.else_expr);
1055
- },
1056
- .if_simple,
1057
- .@"if",
1058
- => {
1059
- const full = tree.fullIf(node).?;
1060
- return becomesMultilineExpr(tree, full.ast.cond_expr) or
1061
- becomesMultilineExpr(tree, full.ast.then_expr) or
1062
- optBecomesMultilineExpr(tree, full.ast.else_expr);
1063
- },
1064
- .fn_proto_simple,
1065
- .fn_proto_multi,
1066
- .fn_proto_one,
1067
- .fn_proto,
1068
- => {
1069
- var buf: [1]Ast.Node.Index = undefined;
1070
- const fn_proto = tree.fullFnProto(&buf, node).?;
1071
-
1072
- for ([_]Ast.Node.OptionalIndex{
1073
- fn_proto.ast.return_type,
1074
- fn_proto.ast.align_expr,
1075
- fn_proto.ast.addrspace_expr,
1076
- fn_proto.ast.section_expr,
1077
- fn_proto.ast.callconv_expr,
1078
- }) |opt_expr| {
1079
- if (opt_expr.unwrap()) |expr| {
1080
- if (becomesMultilineExpr(tree, expr))
1081
- return true;
1082
- }
1083
- }
1084
- for (fn_proto.ast.params) |expr| {
1085
- if (becomesMultilineExpr(tree, expr))
1086
- return true;
1087
- }
961
+ var n: usize = 0;
962
+ for (data[0 .. data.len - 1]) |v| {
963
+ if (std.mem.indexOfScalar(u8, v, '\n') != null) {
964
+ return error.WriteFailed;
965
+ }
966
+ n += v.len;
967
+ }
1088
968
 
1089
- const lparen = fn_proto.ast.fn_token + 1;
1090
- const return_type = fn_proto.ast.return_type.unwrap().?;
1091
- const maybe_bang = tree.firstToken(return_type) - 1;
1092
- const rparen = fnProtoRparen(tree, fn_proto, maybe_bang);
1093
- return !isOneLineFnProto(tree, fn_proto, lparen, rparen);
1094
- },
1095
- .asm_simple,
1096
- => {
1097
- const lhs = tree.nodeData(node).node_and_token[0];
1098
- return becomesMultilineExpr(tree, lhs);
1099
- },
1100
- .@"asm",
1101
- => {
1102
- const lhs, const extra_index = tree.nodeData(node).node_and_extra;
1103
- const asm_extra = tree.extraData(extra_index, Ast.Node.Asm);
1104
- return @intFromEnum(asm_extra.items_end) - @intFromEnum(asm_extra.items_start) != 0 or
1105
- becomesMultilineExpr(tree, lhs) or optBecomesMultilineExpr(tree, asm_extra.clobbers);
1106
- },
1107
- .array_type, .array_type_sentinel => {
1108
- const array_type = tree.fullArrayType(node).?;
1109
- const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
1110
- return !isOneLineArrayType(tree, array_type, rbracket) or
1111
- becomesMultilineExpr(tree, array_type.ast.elem_type);
1112
- },
1113
- .array_access => {
1114
- const lhs, const rhs = tree.nodeData(node).node_and_node;
1115
- const lbracket = tree.firstToken(rhs) - 1;
1116
- const rbracket = tree.lastToken(rhs) + 1;
1117
- return !tree.tokensOnSameLine(lbracket, rbracket) or
1118
- becomesMultilineExpr(tree, lhs) or
1119
- becomesMultilineExpr(tree, rhs);
1120
- },
1121
- .call_one,
1122
- .call,
1123
- .builtin_call_two,
1124
- .builtin_call,
1125
- .array_init_one,
1126
- .array_init_dot_two,
1127
- .array_init_dot,
1128
- .array_init,
1129
- .struct_init_one,
1130
- .struct_init_dot_two,
1131
- .struct_init_dot,
1132
- .struct_init,
1133
- => |tag| {
1134
- var buf: [2]Ast.Node.Index = undefined;
1135
- const opt_lhs: Ast.Node.OptionalIndex, const items = switch (tag) {
1136
- .call_one, .call => blk: {
1137
- const full = tree.fullCall(buf[0..1], node).?;
1138
- break :blk .{ full.ast.fn_expr.toOptional(), full.ast.params };
1139
- },
1140
- .builtin_call_two, .builtin_call => .{ .none, tree.builtinCallParams(&buf, node).? },
1141
- .array_init_one,
1142
- .array_init_dot_two,
1143
- .array_init_dot,
1144
- .array_init,
1145
- => blk: {
1146
- const full = tree.fullArrayInit(&buf, node).?;
1147
- break :blk .{ full.ast.type_expr, full.ast.elements };
1148
- },
1149
- .struct_init_one,
1150
- .struct_init_dot_two,
1151
- .struct_init_dot,
1152
- .struct_init,
1153
- => blk: {
1154
- const full = tree.fullStructInit(&buf, node).?;
1155
- break :blk .{ full.ast.type_expr, full.ast.fields };
1156
- },
1157
- else => unreachable,
1158
- };
1159
- if (opt_lhs.unwrap()) |lhs| {
1160
- if (becomesMultilineExpr(tree, lhs))
1161
- return true;
1162
- }
1163
- for (items) |expr| {
1164
- if (becomesMultilineExpr(tree, expr))
1165
- return true;
1166
- }
1167
- return false;
1168
- },
1169
- .assign_destructure => {
1170
- const full = tree.assignDestructure(node);
1171
- for (full.ast.variables) |expr| {
1172
- if (becomesMultilineExpr(tree, expr))
1173
- return true;
1174
- }
1175
- return becomesMultilineExpr(tree, full.ast.value_expr);
1176
- },
1177
- .ptr_type_aligned,
1178
- .ptr_type_sentinel,
1179
- .ptr_type,
1180
- .ptr_type_bit_range,
1181
- => {
1182
- const full = tree.fullPtrType(node).?;
1183
- return becomesMultilineExpr(tree, full.ast.child_type) or
1184
- optBecomesMultilineExpr(tree, full.ast.sentinel) or
1185
- optBecomesMultilineExpr(tree, full.ast.align_node) or
1186
- optBecomesMultilineExpr(tree, full.ast.addrspace_node) or
1187
- optBecomesMultilineExpr(tree, full.ast.bit_range_start) or
1188
- optBecomesMultilineExpr(tree, full.ast.bit_range_end);
1189
- },
1190
- .slice_open,
1191
- .slice,
1192
- .slice_sentinel,
1193
- => {
1194
- const full = tree.fullSlice(node).?;
1195
- return becomesMultilineExpr(tree, full.ast.sliced) or
1196
- becomesMultilineExpr(tree, full.ast.start) or
1197
- optBecomesMultilineExpr(tree, full.ast.end) or
1198
- optBecomesMultilineExpr(tree, full.ast.sentinel);
1199
- },
1200
- .@"comptime",
1201
- .@"nosuspend",
1202
- .@"suspend",
1203
- .@"resume",
1204
- .bit_not,
1205
- .bool_not,
1206
- .negation,
1207
- .negation_wrap,
1208
- .optional_type,
1209
- .address_of,
1210
- .deref,
1211
- .@"try",
1212
- => return becomesMultilineExpr(tree, tree.nodeData(node).node),
1213
- .@"return" => return optBecomesMultilineExpr(tree, tree.nodeData(node).opt_node),
1214
- .field_access,
1215
- .unwrap_optional,
1216
- .grouped_expression,
1217
- => return becomesMultilineExpr(tree, tree.nodeData(node).node_and_token[0]),
1218
- .add,
1219
- .add_wrap,
1220
- .add_sat,
1221
- .array_cat,
1222
- .array_mult,
1223
- .bang_equal,
1224
- .bit_and,
1225
- .bit_or,
1226
- .shl,
1227
- .shl_sat,
1228
- .shr,
1229
- .bit_xor,
1230
- .bool_and,
1231
- .bool_or,
1232
- .div,
1233
- .equal_equal,
1234
- .greater_or_equal,
1235
- .greater_than,
1236
- .less_or_equal,
1237
- .less_than,
1238
- .merge_error_sets,
1239
- .mod,
1240
- .mul,
1241
- .mul_wrap,
1242
- .mul_sat,
1243
- .sub,
1244
- .sub_wrap,
1245
- .sub_sat,
1246
- .@"orelse",
1247
- .@"catch",
1248
- .error_union,
1249
- .assign,
1250
- .assign_bit_and,
1251
- .assign_bit_or,
1252
- .assign_shl,
1253
- .assign_shl_sat,
1254
- .assign_shr,
1255
- .assign_bit_xor,
1256
- .assign_div,
1257
- .assign_sub,
1258
- .assign_sub_wrap,
1259
- .assign_sub_sat,
1260
- .assign_mod,
1261
- .assign_add,
1262
- .assign_add_wrap,
1263
- .assign_add_sat,
1264
- .assign_mul,
1265
- .assign_mul_wrap,
1266
- .assign_mul_sat,
1267
- => {
1268
- const lhs, const rhs = tree.nodeData(node).node_and_node;
1269
- return becomesMultilineExpr(tree, lhs) or becomesMultilineExpr(tree, rhs);
1270
- },
1271
- .@"break", .@"continue" => {
1272
- const opt_expr = tree.nodeData(node).opt_token_and_opt_node[1];
1273
- return optBecomesMultilineExpr(tree, opt_expr);
1274
- },
1275
- .anyframe_type => return becomesMultilineExpr(tree, tree.nodeData(node).token_and_node[1]),
1276
- .@"errdefer",
1277
- .@"defer",
1278
- .for_range,
1279
- .switch_range,
1280
- .switch_case_one,
1281
- .switch_case_inline_one,
1282
- .switch_case,
1283
- .switch_case_inline,
1284
- .asm_output,
1285
- .asm_input,
1286
- .fn_decl,
1287
- .container_field,
1288
- .container_field_init,
1289
- .container_field_align,
1290
- .root,
1291
- .global_var_decl,
1292
- .local_var_decl,
1293
- .simple_var_decl,
1294
- .aligned_var_decl,
1295
- .test_decl,
1296
- => unreachable,
969
+ const pattern = data[data.len - 1];
970
+ if (splat != 0 and std.mem.indexOfScalar(u8, pattern, '\n') != null) {
971
+ return error.WriteFailed;
1297
972
  }
973
+ n += pattern.len * splat;
974
+
975
+ w.end = 0;
976
+ return n;
1298
977
  }
1299
978
 
1300
- fn isOneLineArrayType(
1301
- tree: Ast,
1302
- array_type: Ast.full.ArrayType,
1303
- rbracket: Ast.TokenIndex,
1304
- ) bool {
1305
- return tree.tokensOnSameLine(array_type.ast.lbracket, rbracket) and
1306
- !becomesMultilineExpr(tree, array_type.ast.elem_count) and
1307
- !optBecomesMultilineExpr(tree, array_type.ast.sentinel);
979
+ fn rendersMultiline(r: *const Render, node: Ast.Node.Index) error{OutOfMemory}!bool {
980
+ var no_nl_buf: [64]u8 = undefined;
981
+ var no_nl_w: Writer = .{
982
+ .vtable = &.{ .drain = drainNoNewline },
983
+ .buffer = &no_nl_buf,
984
+ };
985
+
986
+ if (r.ais.disabled_offset != null) return true;
987
+ var sub_ais: AutoIndentingStream = .init(r.gpa, &no_nl_w, r.ais.indent_delta);
988
+ defer sub_ais.deinit();
989
+ // The following are needed to make sure isLineOverIndented is correct
990
+ sub_ais.indent_count = r.ais.indent_count;
991
+ sub_ais.applied_indent = r.ais.applied_indent;
992
+ sub_ais.current_line_empty = r.ais.current_line_empty;
993
+
994
+ var sub_r: Render = .{
995
+ .gpa = r.gpa,
996
+ .ais = &sub_ais,
997
+ .tree = r.tree,
998
+ .fixups = r.fixups,
999
+ };
1000
+
1001
+ renderExpression(&sub_r, node, .none) catch |e| return switch (e) {
1002
+ error.OutOfMemory => return error.OutOfMemory,
1003
+ error.WriteFailed => return true,
1004
+ };
1005
+ if (sub_ais.disabled_offset != null) return true;
1006
+ if (std.mem.indexOfScalar(u8, no_nl_w.buffered(), '\n') != null) {
1007
+ return true;
1008
+ }
1009
+
1010
+ return false;
1308
1011
  }
1309
1012
 
1310
1013
  fn renderArrayType(
@@ -1315,7 +1018,9 @@ fn renderArrayType(
1315
1018
  const tree = r.tree;
1316
1019
  const ais = r.ais;
1317
1020
  const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
1318
- const one_line = isOneLineArrayType(tree, array_type, rbracket);
1021
+ const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket) and
1022
+ !try rendersMultiline(r, array_type.ast.elem_count) and
1023
+ (if (array_type.ast.sentinel.unwrap()) |s| !try rendersMultiline(r, s) else true);
1319
1024
  const inner_space = if (one_line) Space.none else Space.newline;
1320
1025
  try ais.pushIndent(.normal);
1321
1026
  try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket
@@ -2094,7 +1799,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
2094
1799
  break;
2095
1800
  },
2096
1801
  .keyword_noalias, .keyword_comptime => {
2097
- try renderToken(r, last_param_token, .space);
1802
+ try renderToken(r, last_param_token, .maybe_space);
2098
1803
  last_param_token += 1;
2099
1804
  },
2100
1805
  .identifier => {},
@@ -2145,7 +1850,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
2145
1850
  break;
2146
1851
  },
2147
1852
  .keyword_noalias, .keyword_comptime => {
2148
- try renderToken(r, last_param_token, .space);
1853
+ try renderToken(r, last_param_token, .maybe_space);
2149
1854
  last_param_token += 1;
2150
1855
  },
2151
1856
  .identifier => {},
@@ -2163,7 +1868,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
2163
1868
  {
2164
1869
  try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name
2165
1870
  last_param_token += 1;
2166
- try renderToken(r, last_param_token, .space); // :
1871
+ try renderToken(r, last_param_token, .maybe_space); // :
2167
1872
  last_param_token += 1;
2168
1873
  }
2169
1874
  if (tree.tokenTag(last_param_token) == .keyword_anytype) {
@@ -2504,6 +2209,10 @@ fn renderArrayInit(
2504
2209
  try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none);
2505
2210
 
2506
2211
  buf.clearRetainingCapacity();
2212
+ // The following are needed to make sure isLineOverIndented is not influenced by
2213
+ // the previous element.
2214
+ sub_ais.indent_count = 0;
2215
+ sub_ais.applied_indent = 0;
2507
2216
  }
2508
2217
  }
2509
2218
 
@@ -2749,8 +2458,47 @@ fn renderAsm(
2749
2458
  try renderToken(r, asm_node.ast.asm_token + 1, .none); // lparen
2750
2459
  }
2751
2460
 
2461
+ const render_colons: [3]?Ast.TokenIndex = colons: {
2462
+ var colons: [3]Ast.TokenIndex = undefined;
2463
+ var render: u2 = 0;
2464
+
2465
+ const rparen = asm_node.ast.rparen;
2466
+ filled: {
2467
+ colons[0] = tree.lastToken(asm_node.ast.template) + 1;
2468
+ if (colons[0] == rparen) break :filled;
2469
+
2470
+ if (asm_node.outputs.len != 0) {
2471
+ colons[1] = tree.lastToken(asm_node.outputs[asm_node.outputs.len - 1]) + 1;
2472
+ colons[1] += @intFromBool(tree.tokenTag(colons[1]) == .comma);
2473
+ render = 1;
2474
+ } else {
2475
+ colons[1] = colons[0] + 1;
2476
+ if (hasComment(tree, colons[0], colons[1])) render = 1;
2477
+ }
2478
+ if (colons[1] == rparen) break :filled;
2479
+
2480
+ // Next colon is not checked for here since it cannot present without clobbers
2481
+ if (asm_node.inputs.len != 0) {
2482
+ render = 2;
2483
+ } else {
2484
+ const colon_or_rparen = colons[1] + 1;
2485
+ if (hasComment(tree, colons[1], colon_or_rparen)) render = 2;
2486
+ }
2487
+
2488
+ if (asm_node.ast.clobbers.unwrap()) |clobbers| {
2489
+ colons[2] = tree.firstToken(clobbers) - 1;
2490
+ render = 3;
2491
+ }
2492
+ }
2493
+
2494
+ var opt_colons: [3]?Ast.TokenIndex = @splat(null);
2495
+ for (0..render) |i| opt_colons[i] = colons[i];
2496
+ break :colons opt_colons;
2497
+ };
2498
+
2499
+ try ais.forcePushIndent(.normal);
2500
+
2752
2501
  if (asm_node.ast.items.len == 0) {
2753
- try ais.forcePushIndent(.normal);
2754
2502
  if (asm_node.ast.clobbers.unwrap()) |clobbers| {
2755
2503
  // asm ("foo" ::: clobbers)
2756
2504
  try renderExpression(r, asm_node.ast.template, .space);
@@ -2764,99 +2512,78 @@ fn renderAsm(
2764
2512
  return renderToken(r, asm_node.ast.rparen, space); // rparen
2765
2513
  }
2766
2514
 
2767
- // asm ("foo")
2768
- try renderExpression(r, asm_node.ast.template, .none);
2769
- ais.popIndent();
2770
- return renderToken(r, asm_node.ast.rparen, space); // rparen
2515
+ if (render_colons[0] == null) {
2516
+ // asm ("foo")
2517
+ try renderExpression(r, asm_node.ast.template, .none);
2518
+ ais.popIndent();
2519
+ return renderToken(r, asm_node.ast.rparen, space); // rparen
2520
+ }
2771
2521
  }
2772
2522
 
2773
- try ais.forcePushIndent(.normal);
2774
2523
  try renderExpression(r, asm_node.ast.template, .newline);
2775
2524
  ais.forceLastIndent(); // Might have been dedented by a multiline string literal
2776
2525
  assert(ais.current_line_empty);
2777
2526
 
2527
+ const prev_indent_delta = ais.indent_delta; // May be part of another asm expression
2528
+ // so indent_delta can't be unconditionally used
2778
2529
  ais.setIndentDelta(asm_indent_delta);
2779
- const colon1 = tree.lastToken(asm_node.ast.template) + 1;
2780
2530
 
2781
- const colon2 = if (asm_node.outputs.len == 0) colon2: {
2782
- try renderToken(r, colon1, .newline); // :
2783
- break :colon2 colon1 + 1;
2784
- } else colon2: {
2785
- try renderToken(r, colon1, .space); // :
2531
+ rendered: {
2532
+ if (render_colons[0]) |colon1| {
2533
+ if (asm_node.outputs.len != 0) {
2534
+ try renderToken(r, colon1, .space);
2535
+ try ais.forcePushIndent(.normal);
2786
2536
 
2787
- try ais.forcePushIndent(.normal);
2788
- for (asm_node.outputs, 0..) |asm_output, i| {
2789
- if (i + 1 < asm_node.outputs.len) {
2790
- const next_asm_output = asm_node.outputs[i + 1];
2791
- try renderAsmOutput(r, asm_output, .none);
2537
+ const final = asm_node.outputs.len - 1;
2538
+ for (asm_node.outputs[0..final], 0..) |asm_output, i| {
2539
+ try renderAsmOutput(r, asm_output, .none);
2540
+
2541
+ const next_start = tree.firstToken(asm_node.outputs[i + 1]);
2542
+ try renderToken(r, next_start - 1, .newline); // ,
2543
+ try renderExtraNewlineToken(r, next_start);
2544
+ }
2792
2545
 
2793
- const comma = tree.firstToken(next_asm_output) - 1;
2794
- try renderToken(r, comma, .newline); // ,
2795
- try renderExtraNewlineToken(r, tree.firstToken(next_asm_output));
2796
- } else if (asm_node.inputs.len == 0 and asm_node.ast.clobbers == .none) {
2797
2546
  try ais.pushSpace(.comma);
2798
- try renderAsmOutput(r, asm_output, .comma);
2547
+ try renderAsmOutput(r, asm_node.outputs[final], .comma);
2799
2548
  ais.popSpace();
2800
2549
  ais.popIndent();
2801
- ais.setIndentDelta(indent_delta);
2802
- ais.popIndent();
2803
- return renderToken(r, asm_node.ast.rparen, space); // rparen
2804
2550
  } else {
2805
- try ais.pushSpace(.comma);
2806
- try renderAsmOutput(r, asm_output, .comma);
2807
- ais.popSpace();
2808
- const comma_or_colon = tree.lastToken(asm_output) + 1;
2809
- ais.popIndent();
2810
- break :colon2 switch (tree.tokenTag(comma_or_colon)) {
2811
- .comma => comma_or_colon + 1,
2812
- else => comma_or_colon,
2813
- };
2551
+ try renderToken(r, colon1, .newline);
2814
2552
  }
2815
2553
  } else unreachable;
2816
- };
2817
2554
 
2818
- const colon3 = if (asm_node.inputs.len == 0) colon3: {
2819
- try renderToken(r, colon2, .newline); // :
2820
- break :colon3 colon2 + 1;
2821
- } else colon3: {
2822
- try renderToken(r, colon2, .space); // :
2823
- try ais.forcePushIndent(.normal);
2824
- for (asm_node.inputs, 0..) |asm_input, i| {
2825
- if (i + 1 < asm_node.inputs.len) {
2826
- const next_asm_input = asm_node.inputs[i + 1];
2827
- try renderAsmInput(r, asm_input, .none);
2828
-
2829
- const first_token = tree.firstToken(next_asm_input);
2830
- try renderToken(r, first_token - 1, .newline); // ,
2831
- try renderExtraNewlineToken(r, first_token);
2832
- } else if (asm_node.ast.clobbers == .none) {
2555
+ if (render_colons[1]) |colon2| {
2556
+ if (asm_node.inputs.len != 0) {
2557
+ try renderToken(r, colon2, .space);
2558
+ try ais.forcePushIndent(.normal);
2559
+
2560
+ const final = asm_node.inputs.len - 1;
2561
+ for (asm_node.inputs[0..final], 0..) |asm_input, i| {
2562
+ try renderAsmInput(r, asm_input, .none);
2563
+
2564
+ const next_start = tree.firstToken(asm_node.inputs[i + 1]);
2565
+ try renderToken(r, next_start - 1, .newline); // ,
2566
+ try renderExtraNewlineToken(r, next_start);
2567
+ }
2568
+
2833
2569
  try ais.pushSpace(.comma);
2834
- try renderAsmInput(r, asm_input, .comma);
2570
+ try renderAsmInput(r, asm_node.inputs[final], .comma);
2835
2571
  ais.popSpace();
2836
2572
  ais.popIndent();
2837
- ais.setIndentDelta(indent_delta);
2838
- ais.popIndent();
2839
- return renderToken(r, asm_node.ast.rparen, space); // rparen
2840
2573
  } else {
2841
- try ais.pushSpace(.comma);
2842
- try renderAsmInput(r, asm_input, .comma);
2843
- ais.popSpace();
2844
- const comma_or_colon = tree.lastToken(asm_input) + 1;
2845
- ais.popIndent();
2846
- break :colon3 switch (tree.tokenTag(comma_or_colon)) {
2847
- .comma => comma_or_colon + 1,
2848
- else => comma_or_colon,
2849
- };
2574
+ try renderToken(r, colon2, .newline);
2850
2575
  }
2576
+ } else break :rendered;
2577
+
2578
+ if (render_colons[2]) |colon3| {
2579
+ const clobbers = asm_node.ast.clobbers.unwrap().?;
2580
+ try renderToken(r, colon3, .maybe_space);
2581
+ try renderExpression(r, clobbers, .none);
2582
+ ais.forceLastIndent(); // Might have been dedented by a multiline string literal
2851
2583
  }
2852
- unreachable;
2853
- };
2584
+ }
2854
2585
 
2855
- try renderToken(r, colon3, .maybe_space); // :
2856
- const clobbers = asm_node.ast.clobbers.unwrap().?;
2857
- try renderExpression(r, clobbers, .none);
2858
- ais.forceLastIndent(); // Might have been dedented by a multiline string literal
2859
- ais.setIndentDelta(indent_delta);
2586
+ ais.setIndentDelta(prev_indent_delta);
2860
2587
  ais.popIndent();
2861
2588
  return renderToken(r, asm_node.ast.rparen, space); // rparen
2862
2589
  }
@@ -3324,6 +3051,7 @@ fn renderComments(r: *Render, start: usize, end: usize) Error!bool {
3324
3051
  // Write with the canonical single space.
3325
3052
  try ais.underlying_writer.writeAll("// zig fmt: on\n");
3326
3053
  ais.disabled_offset = null;
3054
+ ais.resetLine();
3327
3055
  } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) {
3328
3056
  // Write with the canonical single space.
3329
3057
  try ais.writeAll("// zig fmt: off\n");