diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig index eaabcfa936..c11665101c 100644 --- a/lib/compiler/std-docs.zig +++ b/lib/compiler/std-docs.zig @@ -275,10 +275,6 @@ fn buildWasmBinary( ) ![]const u8 { const gpa = context.gpa; - const main_src_path = try std.fs.path.join(arena, &.{ - context.zig_lib_directory, "docs", "wasm", "main.zig", - }); - var argv: std.ArrayListUnmanaged([]const u8) = .{}; try argv.appendSlice(arena, &.{ @@ -298,7 +294,10 @@ fn buildWasmBinary( "--name", "autodoc", "-rdynamic", - main_src_path, + "--dep", + "Walk", + try std.fmt.allocPrint(arena, "-Mroot={s}/docs/wasm/main.zig", .{context.zig_lib_directory}), + try std.fmt.allocPrint(arena, "-MWalk={s}/docs/wasm/Walk.zig", .{context.zig_lib_directory}), "--listen=-", }); diff --git a/lib/docs/wasm/Decl.zig b/lib/docs/wasm/Decl.zig index 0260ce0285..2546355987 100644 --- a/lib/docs/wasm/Decl.zig +++ b/lib/docs/wasm/Decl.zig @@ -1,3 +1,12 @@ +const Decl = @This(); +const std = @import("std"); +const Ast = std.zig.Ast; +const Walk = @import("Walk.zig"); +const gpa = std.heap.wasm_allocator; +const assert = std.debug.assert; +const log = std.log; +const Oom = error{OutOfMemory}; + ast_node: Ast.Node.Index, file: Walk.File.Index, /// The decl whose namespace this is in. @@ -215,12 +224,3 @@ pub fn find(search_string: []const u8) Decl.Index { } return current_decl_index; } - -const Decl = @This(); -const std = @import("std"); -const Ast = std.zig.Ast; -const Walk = @import("Walk.zig"); -const gpa = std.heap.wasm_allocator; -const assert = std.debug.assert; -const log = std.log; -const Oom = error{OutOfMemory}; diff --git a/lib/docs/wasm/Walk.zig b/lib/docs/wasm/Walk.zig index a22da861a8..ae924b8c38 100644 --- a/lib/docs/wasm/Walk.zig +++ b/lib/docs/wasm/Walk.zig @@ -1,4 +1,15 @@ //! Find and annotate identifiers with links to their declarations. + +const Walk = @This(); +const std = @import("std"); +const Ast = std.zig.Ast; +const assert = std.debug.assert; +const log = std.log; +const gpa = std.heap.wasm_allocator; +const Oom = error{OutOfMemory}; + +pub const Decl = @import("Decl.zig"); + pub var files: std.StringArrayHashMapUnmanaged(File) = .{}; pub var decls: std.ArrayListUnmanaged(Decl) = .{}; pub var modules: std.StringArrayHashMapUnmanaged(File.Index) = .{}; @@ -1120,15 +1131,6 @@ pub fn isPrimitiveNonType(name: []const u8) bool { // try w.root(); //} -const Walk = @This(); -const std = @import("std"); -const Ast = std.zig.Ast; -const assert = std.debug.assert; -const Decl = @import("Decl.zig"); -const log = std.log; -const gpa = std.heap.wasm_allocator; -const Oom = error{OutOfMemory}; - fn shrinkToFit(m: anytype) void { m.shrinkAndFree(gpa, m.entries.len); } diff --git a/lib/docs/wasm/html_render.zig b/lib/docs/wasm/html_render.zig new file mode 100644 index 0000000000..cce201049d --- /dev/null +++ b/lib/docs/wasm/html_render.zig @@ -0,0 +1,388 @@ +const std = @import("std"); +const Ast = std.zig.Ast; +const assert = std.debug.assert; + +const Walk = @import("Walk"); +const Decl = Walk.Decl; + +const gpa = std.heap.wasm_allocator; +const Oom = error{OutOfMemory}; + +/// Delete this to find out where URL escaping needs to be added. +pub const missing_feature_url_escape = true; + +pub const RenderSourceOptions = struct { + skip_doc_comments: bool = false, + skip_comments: bool = false, + collapse_whitespace: bool = false, + fn_link: Decl.Index = .none, +}; + +pub fn fileSourceHtml( + file_index: Walk.File.Index, + out: *std.ArrayListUnmanaged(u8), + root_node: Ast.Node.Index, + options: RenderSourceOptions, +) !void { + const ast = file_index.get_ast(); + const file = file_index.get(); + + const g = struct { + var field_access_buffer: std.ArrayListUnmanaged(u8) = .{}; + }; + + const token_tags = ast.tokens.items(.tag); + const token_starts = ast.tokens.items(.start); + const main_tokens = ast.nodes.items(.main_token); + + const start_token = ast.firstToken(root_node); + const end_token = ast.lastToken(root_node) + 1; + + var cursor: usize = token_starts[start_token]; + + var indent: usize = 0; + if (std.mem.lastIndexOf(u8, ast.source[0..cursor], "\n")) |newline_index| { + for (ast.source[newline_index + 1 .. cursor]) |c| { + if (c == ' ') { + indent += 1; + } else { + break; + } + } + } + + for ( + token_tags[start_token..end_token], + token_starts[start_token..end_token], + start_token.., + ) |tag, start, token_index| { + const between = ast.source[cursor..start]; + if (std.mem.trim(u8, between, " \t\r\n").len > 0) { + if (!options.skip_comments) { + try out.appendSlice(gpa, ""); + try appendUnindented(out, between, indent); + try out.appendSlice(gpa, ""); + } + } else if (between.len > 0) { + if (options.collapse_whitespace) { + if (out.items.len > 0 and out.items[out.items.len - 1] != ' ') + try out.append(gpa, ' '); + } else { + try appendUnindented(out, between, indent); + } + } + if (tag == .eof) break; + const slice = ast.tokenSlice(token_index); + cursor = start + slice.len; + switch (tag) { + .eof => unreachable, + + .keyword_addrspace, + .keyword_align, + .keyword_and, + .keyword_asm, + .keyword_async, + .keyword_await, + .keyword_break, + .keyword_catch, + .keyword_comptime, + .keyword_const, + .keyword_continue, + .keyword_defer, + .keyword_else, + .keyword_enum, + .keyword_errdefer, + .keyword_error, + .keyword_export, + .keyword_extern, + .keyword_for, + .keyword_if, + .keyword_inline, + .keyword_noalias, + .keyword_noinline, + .keyword_nosuspend, + .keyword_opaque, + .keyword_or, + .keyword_orelse, + .keyword_packed, + .keyword_anyframe, + .keyword_pub, + .keyword_resume, + .keyword_return, + .keyword_linksection, + .keyword_callconv, + .keyword_struct, + .keyword_suspend, + .keyword_switch, + .keyword_test, + .keyword_threadlocal, + .keyword_try, + .keyword_union, + .keyword_unreachable, + .keyword_usingnamespace, + .keyword_var, + .keyword_volatile, + .keyword_allowzero, + .keyword_while, + .keyword_anytype, + .keyword_fn, + => { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + }, + + .string_literal, + .char_literal, + .multiline_string_literal_line, + => { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + }, + + .builtin => { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + }, + + .doc_comment, + .container_doc_comment, + => { + if (!options.skip_doc_comments) { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + } + }, + + .identifier => i: { + if (options.fn_link != .none) { + const fn_link = options.fn_link.get(); + const fn_token = main_tokens[fn_link.ast_node]; + if (token_index == fn_token + 1) { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + break :i; + } + } + + if (token_index > 0 and token_tags[token_index - 1] == .keyword_fn) { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + break :i; + } + + if (Walk.isPrimitiveNonType(slice)) { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + break :i; + } + + if (std.zig.primitives.isPrimitive(slice)) { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + break :i; + } + + if (file.token_parents.get(token_index)) |field_access_node| { + g.field_access_buffer.clearRetainingCapacity(); + try walkFieldAccesses(file_index, &g.field_access_buffer, field_access_node); + if (g.field_access_buffer.items.len > 0) { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + } else { + try appendEscaped(out, slice); + } + break :i; + } + + { + g.field_access_buffer.clearRetainingCapacity(); + try resolveIdentLink(file_index, &g.field_access_buffer, token_index); + if (g.field_access_buffer.items.len > 0) { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + break :i; + } + } + + try appendEscaped(out, slice); + }, + + .number_literal => { + try out.appendSlice(gpa, ""); + try appendEscaped(out, slice); + try out.appendSlice(gpa, ""); + }, + + .bang, + .pipe, + .pipe_pipe, + .pipe_equal, + .equal, + .equal_equal, + .equal_angle_bracket_right, + .bang_equal, + .l_paren, + .r_paren, + .semicolon, + .percent, + .percent_equal, + .l_brace, + .r_brace, + .l_bracket, + .r_bracket, + .period, + .period_asterisk, + .ellipsis2, + .ellipsis3, + .caret, + .caret_equal, + .plus, + .plus_plus, + .plus_equal, + .plus_percent, + .plus_percent_equal, + .plus_pipe, + .plus_pipe_equal, + .minus, + .minus_equal, + .minus_percent, + .minus_percent_equal, + .minus_pipe, + .minus_pipe_equal, + .asterisk, + .asterisk_equal, + .asterisk_asterisk, + .asterisk_percent, + .asterisk_percent_equal, + .asterisk_pipe, + .asterisk_pipe_equal, + .arrow, + .colon, + .slash, + .slash_equal, + .comma, + .ampersand, + .ampersand_equal, + .question_mark, + .angle_bracket_left, + .angle_bracket_left_equal, + .angle_bracket_angle_bracket_left, + .angle_bracket_angle_bracket_left_equal, + .angle_bracket_angle_bracket_left_pipe, + .angle_bracket_angle_bracket_left_pipe_equal, + .angle_bracket_right, + .angle_bracket_right_equal, + .angle_bracket_angle_bracket_right, + .angle_bracket_angle_bracket_right_equal, + .tilde, + => try appendEscaped(out, slice), + + .invalid, .invalid_periodasterisks => return error.InvalidToken, + } + } +} + +fn appendUnindented(out: *std.ArrayListUnmanaged(u8), s: []const u8, indent: usize) !void { + var it = std.mem.splitScalar(u8, s, '\n'); + var is_first_line = true; + while (it.next()) |line| { + if (is_first_line) { + try appendEscaped(out, line); + is_first_line = false; + } else { + try out.appendSlice(gpa, "\n"); + try appendEscaped(out, unindent(line, indent)); + } + } +} + +pub fn appendEscaped(out: *std.ArrayListUnmanaged(u8), s: []const u8) !void { + for (s) |c| { + try out.ensureUnusedCapacity(gpa, 6); + switch (c) { + '&' => out.appendSliceAssumeCapacity("&"), + '<' => out.appendSliceAssumeCapacity("<"), + '>' => out.appendSliceAssumeCapacity(">"), + '"' => out.appendSliceAssumeCapacity("""), + else => out.appendAssumeCapacity(c), + } + } +} + +fn walkFieldAccesses( + file_index: Walk.File.Index, + out: *std.ArrayListUnmanaged(u8), + node: Ast.Node.Index, +) Oom!void { + const ast = file_index.get_ast(); + const node_tags = ast.nodes.items(.tag); + assert(node_tags[node] == .field_access); + const node_datas = ast.nodes.items(.data); + const main_tokens = ast.nodes.items(.main_token); + const object_node = node_datas[node].lhs; + const dot_token = main_tokens[node]; + const field_ident = dot_token + 1; + switch (node_tags[object_node]) { + .identifier => { + const lhs_ident = main_tokens[object_node]; + try resolveIdentLink(file_index, out, lhs_ident); + }, + .field_access => { + try walkFieldAccesses(file_index, out, object_node); + }, + else => {}, + } + if (out.items.len > 0) { + try out.append(gpa, '.'); + try out.appendSlice(gpa, ast.tokenSlice(field_ident)); + } +} + +fn resolveIdentLink( + file_index: Walk.File.Index, + out: *std.ArrayListUnmanaged(u8), + ident_token: Ast.TokenIndex, +) Oom!void { + const decl_index = file_index.get().lookup_token(ident_token); + if (decl_index == .none) return; + try resolveDeclLink(decl_index, out); +} + +fn unindent(s: []const u8, indent: usize) []const u8 { + var indent_idx: usize = 0; + for (s) |c| { + if (c == ' ' and indent_idx < indent) { + indent_idx += 1; + } else { + break; + } + } + return s[indent_idx..]; +} + +pub fn resolveDeclLink(decl_index: Decl.Index, out: *std.ArrayListUnmanaged(u8)) Oom!void { + const decl = decl_index.get(); + switch (decl.categorize()) { + .alias => |alias_decl| try alias_decl.get().fqn(out), + else => try decl.fqn(out), + } +} diff --git a/lib/docs/wasm/main.zig b/lib/docs/wasm/main.zig index 214f28c24b..55882aaf7d 100644 --- a/lib/docs/wasm/main.zig +++ b/lib/docs/wasm/main.zig @@ -1,15 +1,17 @@ -/// Delete this to find out where URL escaping needs to be added. -const missing_feature_url_escape = true; - -const gpa = std.heap.wasm_allocator; - const std = @import("std"); const log = std.log; const assert = std.debug.assert; const Ast = std.zig.Ast; -const Walk = @import("Walk.zig"); +const Walk = @import("Walk"); const markdown = @import("markdown.zig"); -const Decl = @import("Decl.zig"); +const Decl = Walk.Decl; + +const fileSourceHtml = @import("html_render.zig").fileSourceHtml; +const appendEscaped = @import("html_render.zig").appendEscaped; +const resolveDeclLink = @import("html_render.zig").resolveDeclLink; +const missing_feature_url_escape = @import("html_render.zig").missing_feature_url_escape; + +const gpa = std.heap.wasm_allocator; const js = struct { extern "js" fn log(ptr: [*]const u8, len: usize) void; @@ -439,7 +441,7 @@ fn decl_field_html_fallible( const decl = decl_index.get(); const ast = decl.file.get_ast(); try out.appendSlice(gpa, "
");
- try file_source_html(decl.file, out, field_node, .{});
+ try fileSourceHtml(decl.file, out, field_node, .{});
try out.appendSlice(gpa, "");
const field = ast.fullContainerField(field_node).?;
@@ -478,7 +480,7 @@ fn decl_param_html_fallible(
try out.appendSlice(gpa, "");
try appendEscaped(out, name);
try out.appendSlice(gpa, ": ");
- try file_source_html(decl.file, out, param_node, .{});
+ try fileSourceHtml(decl.file, out, param_node, .{});
try out.appendSlice(gpa, "");
if (ast.tokens.items(.tag)[first_doc_comment] == .doc_comment) {
@@ -506,7 +508,7 @@ export fn decl_fn_proto_html(decl_index: Decl.Index, linkify_fn_name: bool) Stri
};
string_result.clearRetainingCapacity();
- file_source_html(decl.file, &string_result, proto_node, .{
+ fileSourceHtml(decl.file, &string_result, proto_node, .{
.skip_doc_comments = true,
.skip_comments = true,
.collapse_whitespace = true,
@@ -521,7 +523,7 @@ export fn decl_source_html(decl_index: Decl.Index) String {
const decl = decl_index.get();
string_result.clearRetainingCapacity();
- file_source_html(decl.file, &string_result, decl.ast_node, .{}) catch |err| {
+ fileSourceHtml(decl.file, &string_result, decl.ast_node, .{}) catch |err| {
fatal("unable to render source: {s}", .{@errorName(err)});
};
return String.init(string_result.items);
@@ -533,7 +535,7 @@ export fn decl_doctest_html(decl_index: Decl.Index) String {
return String.init("");
string_result.clearRetainingCapacity();
- file_source_html(decl.file, &string_result, doctest_ast_node, .{}) catch |err| {
+ fileSourceHtml(decl.file, &string_result, doctest_ast_node, .{}) catch |err| {
fatal("unable to render source: {s}", .{@errorName(err)});
};
return String.init(string_result.items);
@@ -691,7 +693,7 @@ fn render_docs(
const content = doc.string(data.text.content);
if (resolve_decl_path(r.context, content)) |resolved_decl_index| {
g.link_buffer.clearRetainingCapacity();
- try resolve_decl_link(resolved_decl_index, &g.link_buffer);
+ try resolveDeclLink(resolved_decl_index, &g.link_buffer);
try writer.writeAll("") catch @panic("OOM");
- file_source_html(decl.file, &string_result, var_decl.ast.type_node, .{
+ fileSourceHtml(decl.file, &string_result, var_decl.ast.type_node, .{
.skip_comments = true,
.collapse_whitespace = true,
}) catch |e| {
@@ -902,382 +904,6 @@ export fn namespace_members(parent: Decl.Index, include_private: bool) Slice(Dec
return Slice(Decl.Index).init(g.members.items);
}
-const RenderSourceOptions = struct {
- skip_doc_comments: bool = false,
- skip_comments: bool = false,
- collapse_whitespace: bool = false,
- fn_link: Decl.Index = .none,
-};
-
-fn file_source_html(
- file_index: Walk.File.Index,
- out: *std.ArrayListUnmanaged(u8),
- root_node: Ast.Node.Index,
- options: RenderSourceOptions,
-) !void {
- const ast = file_index.get_ast();
- const file = file_index.get();
-
- const g = struct {
- var field_access_buffer: std.ArrayListUnmanaged(u8) = .{};
- };
-
- const token_tags = ast.tokens.items(.tag);
- const token_starts = ast.tokens.items(.start);
- const main_tokens = ast.nodes.items(.main_token);
-
- const start_token = ast.firstToken(root_node);
- const end_token = ast.lastToken(root_node) + 1;
-
- var cursor: usize = token_starts[start_token];
-
- var indent: usize = 0;
- if (std.mem.lastIndexOf(u8, ast.source[0..cursor], "\n")) |newline_index| {
- for (ast.source[newline_index + 1 .. cursor]) |c| {
- if (c == ' ') {
- indent += 1;
- } else {
- break;
- }
- }
- }
-
- for (
- token_tags[start_token..end_token],
- token_starts[start_token..end_token],
- start_token..,
- ) |tag, start, token_index| {
- const between = ast.source[cursor..start];
- if (std.mem.trim(u8, between, " \t\r\n").len > 0) {
- if (!options.skip_comments) {
- try out.appendSlice(gpa, "");
- try appendUnindented(out, between, indent);
- try out.appendSlice(gpa, "");
- }
- } else if (between.len > 0) {
- if (options.collapse_whitespace) {
- if (out.items.len > 0 and out.items[out.items.len - 1] != ' ')
- try out.append(gpa, ' ');
- } else {
- try appendUnindented(out, between, indent);
- }
- }
- if (tag == .eof) break;
- const slice = ast.tokenSlice(token_index);
- cursor = start + slice.len;
- switch (tag) {
- .eof => unreachable,
-
- .keyword_addrspace,
- .keyword_align,
- .keyword_and,
- .keyword_asm,
- .keyword_async,
- .keyword_await,
- .keyword_break,
- .keyword_catch,
- .keyword_comptime,
- .keyword_const,
- .keyword_continue,
- .keyword_defer,
- .keyword_else,
- .keyword_enum,
- .keyword_errdefer,
- .keyword_error,
- .keyword_export,
- .keyword_extern,
- .keyword_for,
- .keyword_if,
- .keyword_inline,
- .keyword_noalias,
- .keyword_noinline,
- .keyword_nosuspend,
- .keyword_opaque,
- .keyword_or,
- .keyword_orelse,
- .keyword_packed,
- .keyword_anyframe,
- .keyword_pub,
- .keyword_resume,
- .keyword_return,
- .keyword_linksection,
- .keyword_callconv,
- .keyword_struct,
- .keyword_suspend,
- .keyword_switch,
- .keyword_test,
- .keyword_threadlocal,
- .keyword_try,
- .keyword_union,
- .keyword_unreachable,
- .keyword_usingnamespace,
- .keyword_var,
- .keyword_volatile,
- .keyword_allowzero,
- .keyword_while,
- .keyword_anytype,
- .keyword_fn,
- => {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- },
-
- .string_literal,
- .char_literal,
- .multiline_string_literal_line,
- => {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- },
-
- .builtin => {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- },
-
- .doc_comment,
- .container_doc_comment,
- => {
- if (!options.skip_doc_comments) {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- }
- },
-
- .identifier => i: {
- if (options.fn_link != .none) {
- const fn_link = options.fn_link.get();
- const fn_token = main_tokens[fn_link.ast_node];
- if (token_index == fn_token + 1) {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- break :i;
- }
- }
-
- if (token_index > 0 and token_tags[token_index - 1] == .keyword_fn) {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- break :i;
- }
-
- if (Walk.isPrimitiveNonType(slice)) {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- break :i;
- }
-
- if (std.zig.primitives.isPrimitive(slice)) {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- break :i;
- }
-
- if (file.token_parents.get(token_index)) |field_access_node| {
- g.field_access_buffer.clearRetainingCapacity();
- try walk_field_accesses(file_index, &g.field_access_buffer, field_access_node);
- if (g.field_access_buffer.items.len > 0) {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- } else {
- try appendEscaped(out, slice);
- }
- break :i;
- }
-
- {
- g.field_access_buffer.clearRetainingCapacity();
- try resolve_ident_link(file_index, &g.field_access_buffer, token_index);
- if (g.field_access_buffer.items.len > 0) {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- break :i;
- }
- }
-
- try appendEscaped(out, slice);
- },
-
- .number_literal => {
- try out.appendSlice(gpa, "");
- try appendEscaped(out, slice);
- try out.appendSlice(gpa, "");
- },
-
- .bang,
- .pipe,
- .pipe_pipe,
- .pipe_equal,
- .equal,
- .equal_equal,
- .equal_angle_bracket_right,
- .bang_equal,
- .l_paren,
- .r_paren,
- .semicolon,
- .percent,
- .percent_equal,
- .l_brace,
- .r_brace,
- .l_bracket,
- .r_bracket,
- .period,
- .period_asterisk,
- .ellipsis2,
- .ellipsis3,
- .caret,
- .caret_equal,
- .plus,
- .plus_plus,
- .plus_equal,
- .plus_percent,
- .plus_percent_equal,
- .plus_pipe,
- .plus_pipe_equal,
- .minus,
- .minus_equal,
- .minus_percent,
- .minus_percent_equal,
- .minus_pipe,
- .minus_pipe_equal,
- .asterisk,
- .asterisk_equal,
- .asterisk_asterisk,
- .asterisk_percent,
- .asterisk_percent_equal,
- .asterisk_pipe,
- .asterisk_pipe_equal,
- .arrow,
- .colon,
- .slash,
- .slash_equal,
- .comma,
- .ampersand,
- .ampersand_equal,
- .question_mark,
- .angle_bracket_left,
- .angle_bracket_left_equal,
- .angle_bracket_angle_bracket_left,
- .angle_bracket_angle_bracket_left_equal,
- .angle_bracket_angle_bracket_left_pipe,
- .angle_bracket_angle_bracket_left_pipe_equal,
- .angle_bracket_right,
- .angle_bracket_right_equal,
- .angle_bracket_angle_bracket_right,
- .angle_bracket_angle_bracket_right_equal,
- .tilde,
- => try appendEscaped(out, slice),
-
- .invalid, .invalid_periodasterisks => return error.InvalidToken,
- }
- }
-}
-
-fn unindent(s: []const u8, indent: usize) []const u8 {
- var indent_idx: usize = 0;
- for (s) |c| {
- if (c == ' ' and indent_idx < indent) {
- indent_idx += 1;
- } else {
- break;
- }
- }
- return s[indent_idx..];
-}
-
-fn appendUnindented(out: *std.ArrayListUnmanaged(u8), s: []const u8, indent: usize) !void {
- var it = std.mem.splitScalar(u8, s, '\n');
- var is_first_line = true;
- while (it.next()) |line| {
- if (is_first_line) {
- try appendEscaped(out, line);
- is_first_line = false;
- } else {
- try out.appendSlice(gpa, "\n");
- try appendEscaped(out, unindent(line, indent));
- }
- }
-}
-
-fn resolve_ident_link(
- file_index: Walk.File.Index,
- out: *std.ArrayListUnmanaged(u8),
- ident_token: Ast.TokenIndex,
-) Oom!void {
- const decl_index = file_index.get().lookup_token(ident_token);
- if (decl_index == .none) return;
- try resolve_decl_link(decl_index, out);
-}
-
-fn resolve_decl_link(decl_index: Decl.Index, out: *std.ArrayListUnmanaged(u8)) Oom!void {
- const decl = decl_index.get();
- switch (decl.categorize()) {
- .alias => |alias_decl| try alias_decl.get().fqn(out),
- else => try decl.fqn(out),
- }
-}
-
-fn walk_field_accesses(
- file_index: Walk.File.Index,
- out: *std.ArrayListUnmanaged(u8),
- node: Ast.Node.Index,
-) Oom!void {
- const ast = file_index.get_ast();
- const node_tags = ast.nodes.items(.tag);
- assert(node_tags[node] == .field_access);
- const node_datas = ast.nodes.items(.data);
- const main_tokens = ast.nodes.items(.main_token);
- const object_node = node_datas[node].lhs;
- const dot_token = main_tokens[node];
- const field_ident = dot_token + 1;
- switch (node_tags[object_node]) {
- .identifier => {
- const lhs_ident = main_tokens[object_node];
- try resolve_ident_link(file_index, out, lhs_ident);
- },
- .field_access => {
- try walk_field_accesses(file_index, out, object_node);
- },
- else => {},
- }
- if (out.items.len > 0) {
- try out.append(gpa, '.');
- try out.appendSlice(gpa, ast.tokenSlice(field_ident));
- }
-}
-
-fn appendEscaped(out: *std.ArrayListUnmanaged(u8), s: []const u8) !void {
- for (s) |c| {
- try out.ensureUnusedCapacity(gpa, 6);
- switch (c) {
- '&' => out.appendSliceAssumeCapacity("&"),
- '<' => out.appendSliceAssumeCapacity("<"),
- '>' => out.appendSliceAssumeCapacity(">"),
- '"' => out.appendSliceAssumeCapacity("""),
- else => out.appendAssumeCapacity(c),
- }
- }
-}
-
fn count_scalar(haystack: []const u8, needle: u8) usize {
var total: usize = 0;
for (haystack) |elem| {
diff --git a/lib/fuzzer/index.html b/lib/fuzzer/index.html
index c1ef059ad6..dadc2f91d3 100644
--- a/lib/fuzzer/index.html
+++ b/lib/fuzzer/index.html
@@ -2,12 +2,56 @@
-
+