const std = @import("std"); const builtin = @import("builtin"); const io = std.io; const fs = std.fs; const process = std.process; const ChildProcess = std.ChildProcess; const Progress = std.Progress; const print = std.debug.print; const mem = std.mem; const testing = std.testing; const Allocator = std.mem.Allocator; const Module = @import("../Module.zig"); pub fn genHtml( allocator: Allocator, src: *Module.File, out: anytype, ) !void { try out.writeAll( \\ \\ \\ \\ \\ ); try out.print(" {s} - source view\n", .{src.sub_file_path}); try out.writeAll( \\ \\ \\ \\ \\ ); const source = try src.getSource(allocator); try tokenizeAndPrintRaw(out, source.bytes); try out.writeAll( \\ \\ ); } const start_line = ""; const end_line = "\n"; var line_counter: usize = 1; pub fn tokenizeAndPrintRaw( out: anytype, src: [:0]const u8, ) !void { line_counter = 1; try out.print("
" ++ start_line, .{line_counter});
    var tokenizer = std.zig.Tokenizer.init(src);
    var index: usize = 0;
    var next_tok_is_fn = false;
    while (true) {
        const prev_tok_was_fn = next_tok_is_fn;
        next_tok_is_fn = false;

        const token = tokenizer.next();
        if (mem.indexOf(u8, src[index..token.loc.start], "//")) |comment_start_off| {
            // render one comment
            const comment_start = index + comment_start_off;
            const comment_end_off = mem.indexOf(u8, src[comment_start..token.loc.start], "\n");
            const comment_end = if (comment_end_off) |o| comment_start + o else token.loc.start;

            try writeEscapedLines(out, src[index..comment_start]);
            try out.writeAll("");
            try writeEscaped(out, src[comment_start..comment_end]);
            try out.writeAll("\n");
            index = comment_end;
            tokenizer.index = index;
            continue;
        }

        try writeEscapedLines(out, src[index..token.loc.start]);
        switch (token.tag) {
            .eof => break,

            .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,
            => {
                try out.writeAll("");
                try writeEscaped(out, src[token.loc.start..token.loc.end]);
                try out.writeAll("");
            },

            .keyword_fn => {
                try out.writeAll("");
                try writeEscaped(out, src[token.loc.start..token.loc.end]);
                try out.writeAll("");
                next_tok_is_fn = true;
            },

            .string_literal,
            .char_literal,
            => {
                try out.writeAll("");
                try writeEscaped(out, src[token.loc.start..token.loc.end]);
                try out.writeAll("");
            },

            .multiline_string_literal_line => {
                if (src[token.loc.end - 1] == '\n') {
                    try out.writeAll("");
                    try writeEscaped(out, src[token.loc.start .. token.loc.end - 1]);
                    line_counter += 1;
                    try out.print("" ++ end_line ++ "\n" ++ start_line, .{line_counter});
                } else {
                    try out.writeAll("");
                    try writeEscaped(out, src[token.loc.start..token.loc.end]);
                    try out.writeAll("");
                }
            },

            .builtin => {
                try out.writeAll("");
                try writeEscaped(out, src[token.loc.start..token.loc.end]);
                try out.writeAll("");
            },

            .doc_comment,
            .container_doc_comment,
            => {
                try out.writeAll("");
                try writeEscaped(out, src[token.loc.start..token.loc.end]);
                try out.writeAll("");
            },

            .identifier => {
                const tok_bytes = src[token.loc.start..token.loc.end];
                if (mem.eql(u8, tok_bytes, "undefined") or
                    mem.eql(u8, tok_bytes, "null") or
                    mem.eql(u8, tok_bytes, "true") or
                    mem.eql(u8, tok_bytes, "false"))
                {
                    try out.writeAll("");
                    try writeEscaped(out, tok_bytes);
                    try out.writeAll("");
                } else if (prev_tok_was_fn) {
                    try out.writeAll("");
                    try writeEscaped(out, tok_bytes);
                    try out.writeAll("");
                } else {
                    const is_int = blk: {
                        if (src[token.loc.start] != 'i' and src[token.loc.start] != 'u')
                            break :blk false;
                        var i = token.loc.start + 1;
                        if (i == token.loc.end)
                            break :blk false;
                        while (i != token.loc.end) : (i += 1) {
                            if (src[i] < '0' or src[i] > '9')
                                break :blk false;
                        }
                        break :blk true;
                    };
                    if (is_int or isType(tok_bytes)) {
                        try out.writeAll("");
                        try writeEscaped(out, tok_bytes);
                        try out.writeAll("");
                    } else {
                        try writeEscaped(out, tok_bytes);
                    }
                }
            },

            .number_literal => {
                try out.writeAll("");
                try writeEscaped(out, src[token.loc.start..token.loc.end]);
                try out.writeAll("");
            },

            .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 writeEscaped(out, src[token.loc.start..token.loc.end]),

            .invalid, .invalid_periodasterisks => return error.ParseError,
        }
        index = token.loc.end;
    }
    try out.writeAll(end_line ++ "
"); } fn writeEscapedLines(out: anytype, text: []const u8) !void { for (text) |char| { if (char == '\n') { try out.writeAll(end_line); line_counter += 1; try out.print(start_line, .{line_counter}); } else { try writeEscaped(out, &[_]u8{char}); } } } fn writeEscaped(out: anytype, input: []const u8) !void { for (input) |c| { try switch (c) { '&' => out.writeAll("&"), '<' => out.writeAll("<"), '>' => out.writeAll(">"), '"' => out.writeAll("""), else => out.writeByte(c), }; } } const builtin_types = [_][]const u8{ "f16", "f32", "f64", "f80", "f128", "c_longdouble", "c_short", "c_ushort", "c_int", "c_uint", "c_long", "c_ulong", "c_longlong", "c_ulonglong", "c_char", "anyopaque", "void", "bool", "isize", "usize", "noreturn", "type", "anyerror", "comptime_int", "comptime_float", }; fn isType(name: []const u8) bool { for (builtin_types) |t| { if (mem.eql(u8, t, name)) return true; } return false; }