output: pull in changes from lib/std/zig/render.zig at 68d2f68ed

master
alex 1 year ago
parent 96e101607e
commit 95b6490a31

@ -40,7 +40,12 @@ or directory exists, zdoc searches across the whole zig std lib.
zdoc outputs results in a basic colored format unless `NO_COLOR`
env variable is set or `--nocolor` flag is seen on command line.
---
### supported versions
zdoc `master` branch requires zig v0.11. however, zdoc can be built with zig v0.10
from the `0.10` branch.
### contributing
to contribute, create a pull request or send a patch with
[git send-mail](https://git-scm.com/docs/git-send-email) to alex-dot-cloudware.io.

@ -32,7 +32,8 @@ pub fn search(alloc: std.mem.Allocator, ais: *output.Ais, source: [:0]const u8,
if (insert_newline) {
try output.renderExtraNewline(ais, tree, decl);
}
try output.renderPubMember(alloc, ais, tree, decl, .newline);
const is_tuple = isTuple(tree, decl);
try output.renderPubMember(alloc, ais, tree, decl, is_tuple, .newline);
insert_newline = true;
}
}
@ -95,8 +96,46 @@ pub fn isPublic(tree: Ast, decl: Ast.Node.Index) bool {
return false;
}
/// reports whether the given node is a tuple-like.
fn isTuple(tree: Ast, node: Ast.Node.Index) bool {
const node_tags = tree.nodes.items(.tag);
const container = blk: {
switch (node_tags[node]) {
.container_decl,
.container_decl_trailing,
=> break :blk tree.containerDecl(node),
.container_decl_two,
.container_decl_two_trailing,
=> {
var buffer: [2]Ast.Node.Index = undefined;
break :blk tree.containerDeclTwo(&buffer, node);
},
.container_decl_arg,
.container_decl_arg_trailing,
=> break :blk tree.containerDeclArg(node),
else => return false,
}
};
const tag = tree.tokens.items(.tag)[container.ast.main_token];
if (tag != .keyword_struct) {
return false;
}
for (container.ast.members) |m| {
const tuple_like = switch (node_tags[m]) {
.container_field_init => tree.containerFieldInit(m).ast.tuple_like,
.container_field_align => tree.containerFieldAlign(m).ast.tuple_like,
.container_field => tree.containerField(m).ast.tuple_like,
else => continue,
};
if (!tuple_like) {
return false;
}
}
return true;
}
/// reports whether the given name matches decl identifier, case-insensitive.
pub fn identifierMatch(tree: Ast, decl: Ast.Node.Index, q: Query) bool {
fn identifierMatch(tree: Ast, decl: Ast.Node.Index, q: Query) bool {
return switch (q) {
.none => false,
.all => true,
@ -254,3 +293,37 @@ test "no identifier sub match" {
}
}
}
test "isTupleLikeDecl" {
const alloc = std.testing.allocator;
const print = std.debug.print;
const src =
\\const Foo = struct{ i32, u32 };
\\const Bar = struct{ a: i32, b: u32 };
\\const baz: u32 = 1;
\\var quix1 = .{"quix"};
\\var quix2: []const u8 = "quix";
;
const tt = [_]struct { is_tuple: bool }{
.{ .is_tuple = true }, // Foo
.{ .is_tuple = false }, // Bar
.{ .is_tuple = false }, // baz
.{ .is_tuple = false }, // quix1
.{ .is_tuple = false }, // quix2
};
var tree = try std.zig.parse(alloc, src);
defer tree.deinit(alloc);
for (tree.rootDecls()) |root_node, i| {
const tc = tt[i];
const decl = tree.simpleVarDecl(root_node);
const res = isTuple(tree, decl.ast.init_node);
if (res != tc.is_tuple) {
const id = identifier(tree, root_node);
print("{d}: isTupleLikeDecl({?s}): {any}; want {any}\n", .{ i, id, res, tc.is_tuple });
return error.IsTupleLikeDecl;
}
}
}

@ -9,9 +9,10 @@ const std = @import("std");
const analyze = @import("analyze.zig");
const mem = std.mem;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const Ast = std.zig.Ast;
const primitives = std.zig.primitives;
const log = std.log.scoped(.render);
@ -34,7 +35,14 @@ pub fn renderTopLevelDocComments(ais: *Ais, tree: Ast) !void {
/// renderPubMember prints the given declaration using ais.
/// it outputs anything only if the declaration is public.
pub fn renderPubMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, space: Space) !void {
pub fn renderPubMember(
gpa: Allocator,
ais: *Ais,
tree: Ast,
decl: Ast.Node.Index,
is_tuple: bool,
space: Space,
) !void {
const token_tags = tree.tokens.items(.tag);
const main_tokens = tree.nodes.items(.main_token);
const datas = tree.nodes.items(.data);
@ -89,15 +97,15 @@ pub fn renderPubMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Inde
},
.container_field_init => {
try renderDocComments(ais, tree, tree.firstToken(decl));
try renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space);
try renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), is_tuple, space);
},
.container_field_align => {
try renderDocComments(ais, tree, tree.firstToken(decl));
try renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space);
try renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), is_tuple, space);
},
.container_field => {
try renderDocComments(ais, tree, tree.firstToken(decl));
try renderContainerField(gpa, ais, tree, tree.containerField(decl), space);
try renderContainerField(gpa, ais, tree, tree.containerField(decl), is_tuple, space);
},
else => |tag| {
log.err("renderPubMember for {} unimplemented", .{tag});
@ -121,19 +129,9 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
const node_tags = tree.nodes.items(.tag);
const datas = tree.nodes.items(.data);
switch (node_tags[node]) {
// TODO remove this c_void -> anyopaque rewrite after the 0.10.0 release.
// Also get rid of renderSpace() as it will no longer be necessary.
.identifier => {
const token_index = main_tokens[node];
const lexeme = tokenSliceForRender(tree, token_index);
if (mem.eql(u8, lexeme, "c_void")) {
try ais.writer().writeAll("anyopaque");
} else {
try ais.writer().writeAll(lexeme);
}
return renderSpace(ais, tree, token_index, lexeme.len, space);
return renderIdentifier(ais, tree, token_index, space, .preserve_when_shadowing);
},
.number_literal,
@ -163,7 +161,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
.error_value => {
try renderToken(ais, tree, main_tokens[node], .none);
try renderToken(ais, tree, main_tokens[node] + 1, .none);
return renderToken(ais, tree, main_tokens[node] + 2, space);
return renderIdentifier(ais, tree, main_tokens[node] + 2, space, .eagerly_unquote);
},
.block_two,
@ -185,26 +183,6 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
return renderBlock(gpa, ais, tree, node, statements, space);
},
//.@"errdefer" => {
// const defer_token = main_tokens[node];
// const payload_token = datas[node].lhs;
// const expr = datas[node].rhs;
// try renderToken(ais, tree, defer_token, .space);
// if (payload_token != 0) {
// try renderToken(ais, tree, payload_token - 1, .none); // |
// try renderToken(ais, tree, payload_token, .none); // identifier
// try renderToken(ais, tree, payload_token + 1, .space); // |
// }
// return renderExpression(gpa, ais, tree, expr, space);
//},
//.@"defer" => {
// const defer_token = main_tokens[node];
// const expr = datas[node].rhs;
// try renderToken(ais, tree, defer_token, .space);
// return renderExpression(gpa, ais, tree, expr, space);
//},
.@"comptime", .@"nosuspend" => {
const comptime_token = main_tokens[node];
const block = datas[node].lhs;
@ -231,7 +209,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
// if (token_tags[fallback_first - 1] == .pipe) {
// try renderToken(ais, tree, main_token, .space); // catch keyword
// try renderToken(ais, tree, main_token + 1, .none); // pipe
// try renderToken(ais, tree, main_token + 2, .none); // payload identifier
// try renderIdentifier(ais, tree, main_token + 2, .none, .preserve_when_shadowing); // payload identifier
// try renderToken(ais, tree, main_token + 3, after_op_space); // pipe
// } else {
// assert(token_tags[fallback_first - 1] == .keyword_catch);
@ -257,7 +235,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
ais.pushIndentOneShot();
}
try renderToken(ais, tree, main_token, .none);
try renderToken(ais, tree, main_token, .none); // .
// This check ensures that zag() is indented in the following example:
// const x = foo
@ -268,7 +246,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
ais.pushIndentOneShot();
}
return renderToken(ais, tree, field_access.rhs, space);
return renderIdentifier(ais, tree, field_access.rhs, space, .eagerly_unquote); // field
},
.error_union,
@ -451,11 +429,11 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
} else if (label_token != 0 and target == 0) {
try renderToken(ais, tree, main_token, .space); // break keyword
try renderToken(ais, tree, label_token - 1, .none); // colon
try renderToken(ais, tree, label_token, space); // identifier
try renderIdentifier(ais, tree, label_token, space, .eagerly_unquote); // identifier
} else if (label_token != 0 and target != 0) {
try renderToken(ais, tree, main_token, .space); // break keyword
try renderToken(ais, tree, label_token - 1, .none); // colon
try renderToken(ais, tree, label_token, .space); // identifier
try renderIdentifier(ais, tree, label_token, .space, .eagerly_unquote); // identifier
try renderExpression(gpa, ais, tree, target, space);
}
},
@ -466,7 +444,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
// if (label != 0) {
// try renderToken(ais, tree, main_token, .space); // continue
// try renderToken(ais, tree, label - 1, .none); // :
// return renderToken(ais, tree, label, space); // label
// return renderIdentifier(ais, tree, label, space, .eagerly_unquote); // label
// } else {
// return renderToken(ais, tree, main_token, space); // continue
// }
@ -527,7 +505,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
// There is exactly one member and no trailing comma or
// comments, so render without surrounding spaces: `error{Foo}`
try renderToken(ais, tree, lbrace, .none);
try renderToken(ais, tree, lbrace + 1, .none); // identifier
try renderIdentifier(ais, tree, lbrace + 1, .none, .eagerly_unquote); // identifier
return renderToken(ais, tree, rbrace, space);
} else if (token_tags[rbrace - 1] == .comma) {
// There is a trailing comma so render each member on a new line.
@ -538,7 +516,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i);
switch (token_tags[i]) {
.doc_comment => try renderToken(ais, tree, i, .newline),
.identifier => try renderToken(ais, tree, i, .comma),
.identifier => try renderIdentifier(ais, tree, i, .comma, .eagerly_unquote),
.comma => {},
else => unreachable,
}
@ -552,7 +530,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
while (i < rbrace) : (i += 1) {
switch (token_tags[i]) {
.doc_comment => unreachable, // TODO
.identifier => try renderToken(ais, tree, i, .comma_space),
.identifier => try renderIdentifier(ais, tree, i, .comma_space, .eagerly_unquote),
.comma => {},
else => unreachable,
}
@ -634,12 +612,9 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
.if_simple => return renderIf(gpa, ais, tree, tree.ifSimple(node), space),
.@"if" => return renderIf(gpa, ais, tree, tree.ifFull(node), space),
//.asm_simple => return renderAsm(gpa, ais, tree, tree.asmSimple(node), space),
//.@"asm" => return renderAsm(gpa, ais, tree, tree.asmFull(node), space),
.enum_literal => {
try renderToken(ais, tree, main_tokens[node] - 1, .none); // .
return renderToken(ais, tree, main_tokens[node], space); // name
return renderIdentifier(ais, tree, main_tokens[node], space, .eagerly_unquote); // name
},
//.fn_decl => unreachable,
@ -845,7 +820,7 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec
else
Space.none;
ais.colorIdentifier();
try renderToken(ais, tree, var_decl.ast.mut_token + 1, name_space); // name
try renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name
ais.colorReset();
if (var_decl.ast.type_node != 0) {
@ -946,7 +921,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
const token_tags = tree.tokens.items(.tag);
if (while_node.label_token) |label| {
try renderToken(ais, tree, label, .none); // label
try renderIdentifier(ais, tree, label, .none, .eagerly_unquote); // label
try renderToken(ais, tree, label + 1, .space); // :
}
@ -971,11 +946,11 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
break :blk payload_token;
}
};
try renderToken(ais, tree, ident, .none); // identifier
try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier
const pipe = blk: {
if (token_tags[ident + 1] == .comma) {
try renderToken(ais, tree, ident + 1, .space); // ,
try renderToken(ais, tree, ident + 2, .none); // index
try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // index
break :blk ident + 3;
} else {
break :blk ident + 1;
@ -1018,7 +993,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
if (while_node.error_token) |error_token| {
try renderToken(ais, tree, while_node.else_token, .space); // else
try renderToken(ais, tree, error_token - 1, .none); // |
try renderToken(ais, tree, error_token, .none); // identifier
try renderIdentifier(ais, tree, error_token, .none, .preserve_when_shadowing); // identifier
last_else_token = error_token + 1; // |
}
@ -1047,22 +1022,40 @@ fn renderContainerField(
gpa: Allocator,
ais: *Ais,
tree: Ast,
field: Ast.full.ContainerField,
field_param: Ast.full.ContainerField,
is_tuple: bool,
space: Space,
) !void {
var field = field_param;
if (!is_tuple) field.convertToNonTupleLike(tree.nodes);
if (field.comptime_token) |t| {
try renderToken(ais, tree, t, .space); // comptime
}
if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
if (field.ast.align_expr != 0) {
ais.colorIdentifier();
try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name
ais.colorReset();
const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
const align_kw = lparen_token - 1;
const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
try renderToken(ais, tree, align_kw, .none); // align
try renderToken(ais, tree, lparen_token, .none); // (
try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
return renderToken(ais, tree, rparen_token, .space); // )
}
ais.colorIdentifier();
defer ais.colorReset();
return renderTokenComma(ais, tree, field.ast.name_token, space); // name
return renderIdentifierComma(ais, tree, field.ast.main_token, space, .eagerly_unquote); // name
}
if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
ais.colorIdentifier();
try renderToken(ais, tree, field.ast.name_token, .none); // name
ais.colorReset();
try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
if (!field.ast.tuple_like) {
ais.colorIdentifier();
try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name
ais.colorReset();
try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
}
if (field.ast.align_expr != 0) {
try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
@ -1078,16 +1071,27 @@ fn renderContainerField(
}
if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
ais.colorIdentifier();
try renderToken(ais, tree, field.ast.name_token, .space); // name
try renderIdentifier(ais, tree, field.ast.main_token, .space, .eagerly_unquote); // name
ais.colorReset();
try renderToken(ais, tree, field.ast.name_token + 1, .space); // =
if (field.ast.align_expr != 0) {
const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
const align_kw = lparen_token - 1;
const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
try renderToken(ais, tree, align_kw, .none); // align
try renderToken(ais, tree, lparen_token, .none); // (
try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
try renderToken(ais, tree, rparen_token, .space); // )
}
try renderToken(ais, tree, field.ast.main_token + 1, .space); // =
return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
}
ais.colorIdentifier();
try renderToken(ais, tree, field.ast.name_token, .none); // name
ais.colorReset();
try renderToken(ais, tree, field.ast.name_token + 1, .space); // :
if (!field.ast.tuple_like) {
ais.colorIdentifier();
try renderIdentifier(ais, tree, field.ast.main_token, .none, .eagerly_unquote); // name
ais.colorReset();
try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
}
try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
if (field.ast.align_expr != 0) {
@ -1186,7 +1190,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
const lparen = if (token_tags[after_fn_token] == .identifier) blk: {
try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn
ais.colorIdentifier();
try renderToken(ais, tree, after_fn_token, .none); // name
try renderIdentifier(ais, tree, after_fn_token, .none, .preserve_when_shadowing); // name
ais.colorReset();
break :blk after_fn_token + 1;
} else blk: {
@ -1276,7 +1280,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
if (token_tags[last_param_token] == .identifier and
token_tags[last_param_token + 1] == .colon)
{
try renderToken(ais, tree, last_param_token, .none); // name
try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name
last_param_token += 1;
try renderToken(ais, tree, last_param_token, .space); // :
last_param_token += 1;
@ -1325,7 +1329,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
if (token_tags[last_param_token] == .identifier and
token_tags[last_param_token + 1] == .colon)
{
try renderToken(ais, tree, last_param_token, .none); // name
try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name
last_param_token += 1;
try renderToken(ais, tree, last_param_token, .space); // :
last_param_token += 1;
@ -1438,7 +1442,7 @@ fn renderSwitchCase(
else
Space.space;
const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space;
try renderToken(ais, tree, switch_case.ast.arrow_token, after_arrow_space);
try renderToken(ais, tree, switch_case.ast.arrow_token, after_arrow_space); // =>
if (switch_case.payload_token) |payload_token| {
try renderToken(ais, tree, payload_token - 1, .none); // pipe
@ -1446,10 +1450,10 @@ fn renderSwitchCase(
if (token_tags[payload_token] == .asterisk) {
try renderToken(ais, tree, payload_token, .none); // asterisk
}
try renderToken(ais, tree, ident, .none); // identifier
try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier
if (token_tags[ident + 1] == .comma) {
try renderToken(ais, tree, ident + 1, .space); // ,
try renderToken(ais, tree, ident + 2, .none); // identifier
try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // identifier
try renderToken(ais, tree, ident + 3, pre_target_space); // pipe
} else {
try renderToken(ais, tree, ident + 1, pre_target_space); // pipe
@ -1474,8 +1478,9 @@ fn renderBlock(
if (token_tags[lbrace - 1] == .colon and
token_tags[lbrace - 2] == .identifier)
{
try renderToken(ais, tree, lbrace - 2, .none);
try renderToken(ais, tree, lbrace - 1, .space);
try renderIdentifier(ais, tree, lbrace - 2, .none, .eagerly_unquote); // identifier
try renderToken(ais, tree, lbrace - 1, .space); // :
}
ais.pushIndentNextLine();
@ -1528,7 +1533,7 @@ fn renderStructInit(
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 renderIdentifier(ais, tree, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name
try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // =
try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma);
@ -1536,7 +1541,7 @@ fn renderStructInit(
const init_token = tree.firstToken(field_init);
try renderExtraNewlineToken(ais, tree, init_token - 3);
try renderToken(ais, tree, init_token - 3, .none); // .
try renderToken(ais, tree, init_token - 2, .space); // name
try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name
try renderToken(ais, tree, init_token - 1, .space); // =
try renderExpression(gpa, ais, tree, field_init, .comma);
}
@ -1549,7 +1554,7 @@ fn renderStructInit(
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 renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name
try renderToken(ais, tree, init_token - 1, .space); // =
try renderExpression(gpa, ais, tree, field_init, .comma_space);
}
@ -1792,6 +1797,20 @@ fn renderContainerDecl(
try renderToken(ais, tree, layout_token, .space);
}
var is_tuple = token_tags[container_decl.ast.main_token] == .keyword_struct;
if (is_tuple) for (container_decl.ast.members) |member| {
const tuple_like = switch (node_tags[member]) {
.container_field_init => tree.containerFieldInit(member).ast.tuple_like,
.container_field_align => tree.containerFieldAlign(member).ast.tuple_like,
.container_field => tree.containerField(member).ast.tuple_like,
else => continue,
};
if (!tuple_like) {
is_tuple = false;
break;
}
};
var lbrace: Ast.TokenIndex = undefined;
if (container_decl.ast.enum_token) |enum_token| {
try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
@ -1858,7 +1877,7 @@ fn renderContainerDecl(
// Print all the declarations on the same line.
try renderToken(ais, tree, lbrace, .space); // lbrace
for (container_decl.ast.members) |member| {
try renderPubMember(gpa, ais, tree, member, .space);
try renderPubMember(gpa, ais, tree, member, is_tuple, .space);
}
return renderToken(ais, tree, rbrace, space); // rbrace
}
@ -1876,9 +1895,9 @@ fn renderContainerDecl(
.container_field_init,
.container_field_align,
.container_field,
=> try renderPubMember(gpa, ais, tree, member, .comma),
=> try renderPubMember(gpa, ais, tree, member, is_tuple, .comma),
else => try renderPubMember(gpa, ais, tree, member, .newline),
else => try renderPubMember(gpa, ais, tree, member, is_tuple, .newline),
}
}
ais.popIndent();
@ -2044,6 +2063,19 @@ fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) !
}
}
/// Render an identifier, and the comma that follows it, if it is present in the source.
/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
fn renderIdentifierComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space, quote: QuoteBehavior) !void {
const token_tags = tree.tokens.items(.tag);
const maybe_comma = token + 1;
if (token_tags[maybe_comma] == .comma and space != .comma) {
try renderIdentifier(ais, tree, token, .none, quote);
return renderToken(ais, tree, maybe_comma, space);
} else {
return renderIdentifier(ais, tree, token, space, quote);
}
}
const Space = enum {
/// Output the token lexeme only.
none,
@ -2111,6 +2143,158 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us
}
}
const QuoteBehavior = enum {
preserve_when_shadowing,
eagerly_unquote,
};
fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) !void {
const token_tags = tree.tokens.items(.tag);
assert(token_tags[token_index] == .identifier);
const lexeme = tokenSliceForRender(tree, token_index);
if (lexeme[0] != '@') {
return renderToken(ais, tree, token_index, space);
}
assert(lexeme.len >= 3);
assert(lexeme[0] == '@');
assert(lexeme[1] == '\"');
assert(lexeme[lexeme.len - 1] == '\"');
const contents = lexeme[2 .. lexeme.len - 1]; // inside the @"" quotation
// Empty name can't be unquoted.
if (contents.len == 0) {
return renderQuotedIdentifier(ais, tree, token_index, space, false);
}
// Special case for _ which would incorrectly be rejected by isValidId below.
if (contents.len == 1 and contents[0] == '_') switch (quote) {
.eagerly_unquote => return renderQuotedIdentifier(ais, tree, token_index, space, true),
.preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false),
};
// Scan the entire name for characters that would (after un-escaping) be illegal in a symbol,
// i.e. contents don't match: [A-Za-z_][A-Za-z0-9_]*
var contents_i: usize = 0;
while (contents_i < contents.len) {
switch (contents[contents_i]) {
'0'...'9' => if (contents_i == 0) return renderQuotedIdentifier(ais, tree, token_index, space, false),
'A'...'Z', 'a'...'z', '_' => {},
'\\' => {
var esc_offset = contents_i;
const res = std.zig.string_literal.parseEscapeSequence(contents, &esc_offset);
switch (res) {
.success => |char| switch (char) {
'0'...'9' => if (contents_i == 0) return renderQuotedIdentifier(ais, tree, token_index, space, false),
'A'...'Z', 'a'...'z', '_' => {},
else => return renderQuotedIdentifier(ais, tree, token_index, space, false),
},
.failure => return renderQuotedIdentifier(ais, tree, token_index, space, false),
}
contents_i += esc_offset;
continue;
},
else => return renderQuotedIdentifier(ais, tree, token_index, space, false),
}
contents_i += 1;
}
// Read enough of the name (while un-escaping) to determine if it's a keyword or primitive.
// If it's too long to fit in this buffer, we know it's neither and quoting is unnecessary.
// If we read the whole thing, we have to do further checks.
const longest_keyword_or_primitive_len = comptime blk: {
var longest = 0;
for (primitives.names.kvs) |kv| {
if (kv.key.len > longest) longest = kv.key.len;
}
for (std.zig.Token.keywords.kvs) |kv| {
if (kv.key.len > longest) longest = kv.key.len;
}
break :blk longest;
};
var buf: [longest_keyword_or_primitive_len]u8 = undefined;
contents_i = 0;
var buf_i: usize = 0;
while (contents_i < contents.len and buf_i < longest_keyword_or_primitive_len) {
if (contents[contents_i] == '\\') {
const res = std.zig.string_literal.parseEscapeSequence(contents, &contents_i).success;
buf[buf_i] = @intCast(u8, res);
buf_i += 1;
} else {
buf[buf_i] = contents[contents_i];
contents_i += 1;
buf_i += 1;
}
}
// We read the whole thing, so it could be a keyword or primitive.
if (contents_i == contents.len) {
if (!std.zig.isValidId(buf[0..buf_i])) {
return renderQuotedIdentifier(ais, tree, token_index, space, false);
}
if (primitives.isPrimitive(buf[0..buf_i])) switch (quote) {
.eagerly_unquote => return renderQuotedIdentifier(ais, tree, token_index, space, true),
.preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false),
};
}
try renderQuotedIdentifier(ais, tree, token_index, space, true);
}
// Renders a @"" quoted identifier, normalizing escapes.
// Unnecessary escapes are un-escaped, and \u escapes are normalized to \x when they fit.
// If unquote is true, the @"" is removed and the result is a bare symbol whose validity is asserted.
fn renderQuotedIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space, comptime unquote: bool) !void {
const token_tags = tree.tokens.items(.tag);
assert(token_tags[token_index] == .identifier);
const lexeme = tokenSliceForRender(tree, token_index);
assert(lexeme.len >= 3 and lexeme[0] == '@');
if (!unquote) try ais.writer().writeAll("@\"");
const contents = lexeme[2 .. lexeme.len - 1];
try renderIdentifierContents(ais.writer(), contents);
if (!unquote) try ais.writer().writeByte('\"');
try renderSpace(ais, tree, token_index, lexeme.len, space);
}
fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void {
var pos: usize = 0;
while (pos < bytes.len) {
const byte = bytes[pos];
switch (byte) {
'\\' => {
const old_pos = pos;
const res = std.zig.string_literal.parseEscapeSequence(bytes, &pos);
const escape_sequence = bytes[old_pos..pos];
switch (res) {
.success => |codepoint| {
if (codepoint <= 0x7f) {
const buf = [1]u8{@intCast(u8, codepoint)};
try std.fmt.format(writer, "{}", .{std.zig.fmtEscapes(&buf)});
} else {
try writer.writeAll(escape_sequence);
}
},
.failure => {
try writer.writeAll(escape_sequence);
},
}
},
0x00...('\\' - 1), ('\\' + 1)...0x7f => {
const buf = [1]u8{@intCast(u8, byte)};
try std.fmt.format(writer, "{}", .{std.zig.fmtEscapes(&buf)});
pos += 1;
},
0x80...0xff => {
try writer.writeByte(byte);
pos += 1;
},
}
}
}
/// Returns true if there exists a line comment between any of the tokens from
/// `start_token` to `end_token`. This is used to determine if e.g. a
/// fn_proto should be wrapped and have a trailing comma inserted even if
@ -2155,7 +2339,7 @@ fn renderComments(ais: *Ais, tree: Ast, start: usize, end: usize) anyerror!bool
const newline = if (newline_index) |i| comment_start + i else null;
const untrimmed_comment = tree.source[comment_start .. newline orelse tree.source.len];
const trimmed_comment = mem.trimRight(u8, untrimmed_comment, &std.ascii.spaces);
const trimmed_comment = mem.trimRight(u8, untrimmed_comment, &std.ascii.whitespace);
// Don't leave any whitespace at the start of the file
if (index != 0) {
@ -2176,7 +2360,7 @@ fn renderComments(ais: *Ais, tree: Ast, start: usize, end: usize) anyerror!bool
index = 1 + (newline orelse end - 1);
const comment_content = mem.trimLeft(u8, trimmed_comment["//".len..], &std.ascii.spaces);
const comment_content = mem.trimLeft(u8, trimmed_comment["//".len..], &std.ascii.whitespace);
if (ais.disabled_offset != null and mem.eql(u8, comment_content, "zig fmt: on")) {
// Write the source for which formatting was disabled directly
// to the underlying writer, fixing up invaild whitespace.
@ -2223,7 +2407,7 @@ fn renderExtraNewlineToken(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex) an
// non-whitespace character is encountered or two newlines have been found.
var i = token_start - 1;
var newlines: u2 = 0;
while (std.ascii.isSpace(tree.source[i])) : (i -= 1) {
while (std.ascii.isWhitespace(tree.source[i])) : (i -= 1) {
if (tree.source[i] == '\n') newlines += 1;
if (newlines == 2) return ais.insertNewline();
if (i == prev_token_end) break;
@ -2474,15 +2658,15 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
search_file: ?[]const u8 = null,
fn colorIdentifier(self: *Self) void {
self.ttyconf.setColor(self.underlying_writer, .Bold);
self.ttyconf.setColor(self.underlying_writer, .Bold) catch {};
}
fn colorComments(self: *Self) void {
self.ttyconf.setColor(self.underlying_writer, .Dim);
self.ttyconf.setColor(self.underlying_writer, .Dim) catch {};
}
fn colorReset(self: *Self) void {
self.ttyconf.setColor(self.underlying_writer, .Reset);
self.ttyconf.setColor(self.underlying_writer, .Reset) catch {};
}
pub fn writer(self: *Self) Writer {
@ -2648,7 +2832,7 @@ pub const TypeErasedWriter = struct {
const WriteError = anyerror;
const VTable = struct {
writeFn: std.meta.FnPtr(fn (usize, []const u8) WriteError!usize),
writeFn: *const fn (usize, []const u8) WriteError!usize,
};
/// pointer is expected to be of an std.io.Writer type.

Loading…
Cancel
Save