| author | |
| committer | |
| log | 53e971226dcc2fd0cb53e3442399b8724700b3e5 |
| tree | 2d23cad4a85858f3a45234ea431be2eb3b746f2c |
| parent | 51a4861a081eea5d9bfd590c63f4a4506305f941 |
4 files changed, 444 insertions(+), 442 deletions(-)
lib/docs/main.js+2-2| ... | ... | @@ -51,7 +51,7 @@ var zigAnalysis; |
| 51 | 51 | const domHdrName = document.getElementById("hdrName"); |
| 52 | 52 | const domHelpModal = document.getElementById("helpModal"); |
| 53 | 53 | const domSearchPlaceholder = document.getElementById("searchPlaceholder"); |
| 54 | const sourceFileUrlTemplate = "src-viewer/{{file}}#L{{line}}" | |
| 54 | const sourceFileUrlTemplate = "src/{{file}}#L{{line}}" | |
| 55 | 55 | const domLangRefLink = document.getElementById("langRefLink"); |
| 56 | 56 | |
| 57 | 57 | let lineCounter = 1; |
| ... | ... | @@ -989,7 +989,7 @@ var zigAnalysis; |
| 989 | 989 | "switch(" + |
| 990 | 990 | cond + |
| 991 | 991 | ") {" + |
| 992 | '<a href="/src-viewer/' + | |
| 992 | '<a href="/src/' + | |
| 993 | 993 | file_name + |
| 994 | 994 | "#L" + |
| 995 | 995 | line + |
src/Autodoc.zig+18-15| ... | ... | @@ -9,7 +9,7 @@ const Package = @import("Package.zig"); |
| 9 | 9 | const Zir = @import("Zir.zig"); |
| 10 | 10 | const Ref = Zir.Inst.Ref; |
| 11 | 11 | const log = std.log.scoped(.autodoc); |
| 12 | const Docgen = @import("Docgen.zig"); | |
| 12 | const Docgen = @import("autodoc/render_source.zig"); | |
| 13 | 13 | |
| 14 | 14 | module: *Module, |
| 15 | 15 | doc_location: Compilation.EmitLoc, |
| ... | ... | @@ -243,6 +243,7 @@ pub fn generateZirData(self: *Autodoc) !void { |
| 243 | 243 | try d.handle.openDir(self.doc_location.basename, .{}) |
| 244 | 244 | else |
| 245 | 245 | try self.module.zig_cache_artifact_directory.handle.openDir(self.doc_location.basename, .{}); |
| 246 | ||
| 246 | 247 | { |
| 247 | 248 | const data_js_f = try output_dir.createFile("data.js", .{}); |
| 248 | 249 | defer data_js_f.close(); |
| ... | ... | @@ -267,25 +268,27 @@ pub fn generateZirData(self: *Autodoc) !void { |
| 267 | 268 | try buffer.flush(); |
| 268 | 269 | } |
| 269 | 270 | |
| 270 | output_dir.makeDir("src-viewer") catch |e| switch (e) { | |
| 271 | error.PathAlreadyExists => {}, | |
| 272 | else => |err| return err, | |
| 273 | }; | |
| 274 | const html_dir = try output_dir.openDir("src-viewer", .{}); | |
| 271 | { | |
| 272 | output_dir.makeDir("src") catch |e| switch (e) { | |
| 273 | error.PathAlreadyExists => {}, | |
| 274 | else => |err| return err, | |
| 275 | }; | |
| 276 | const html_dir = try output_dir.openDir("src", .{}); | |
| 275 | 277 | |
| 276 | var files_iterator = self.files.iterator(); | |
| 278 | var files_iterator = self.files.iterator(); | |
| 277 | 279 | |
| 278 | while (files_iterator.next()) |entry| { | |
| 279 | const new_html_path = entry.key_ptr.*.sub_file_path; | |
| 280 | while (files_iterator.next()) |entry| { | |
| 281 | const new_html_path = entry.key_ptr.*.sub_file_path; | |
| 280 | 282 | |
| 281 | const html_file = try createFromPath(html_dir, new_html_path); | |
| 282 | defer html_file.close(); | |
| 283 | var buffer = std.io.bufferedWriter(html_file.writer()); | |
| 283 | const html_file = try createFromPath(html_dir, new_html_path); | |
| 284 | defer html_file.close(); | |
| 285 | var buffer = std.io.bufferedWriter(html_file.writer()); | |
| 284 | 286 | |
| 285 | const out = buffer.writer(); | |
| 287 | const out = buffer.writer(); | |
| 286 | 288 | |
| 287 | try Docgen.genHtml(self.module.gpa, entry.key_ptr.*, out); | |
| 288 | try buffer.flush(); | |
| 289 | try Docgen.genHtml(self.module.gpa, entry.key_ptr.*, out); | |
| 290 | try buffer.flush(); | |
| 291 | } | |
| 289 | 292 | } |
| 290 | 293 | |
| 291 | 294 | // copy main.js, index.html |
src/Docgen.zig deleted-425| ... | ... | @@ -1,425 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const io = std.io; | |
| 4 | const fs = std.fs; | |
| 5 | const process = std.process; | |
| 6 | const ChildProcess = std.ChildProcess; | |
| 7 | const Progress = std.Progress; | |
| 8 | const print = std.debug.print; | |
| 9 | const mem = std.mem; | |
| 10 | const testing = std.testing; | |
| 11 | const Allocator = std.mem.Allocator; | |
| 12 | const Module = @import("Module.zig"); | |
| 13 | ||
| 14 | pub fn genHtml( | |
| 15 | allocator: Allocator, | |
| 16 | src: *Module.File, | |
| 17 | out: anytype, | |
| 18 | ) !void { | |
| 19 | try out.writeAll( | |
| 20 | \\<!doctype html> | |
| 21 | \\<html lang="en"> | |
| 22 | \\<head> | |
| 23 | \\ <meta charset="utf-8"> | |
| 24 | \\ <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| 25 | ); | |
| 26 | try out.print(" <title>{s} - source view</title>\n", .{src.sub_file_path}); | |
| 27 | try out.writeAll( | |
| 28 | \\ <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg=="/> | |
| 29 | \\ <style> | |
| 30 | \\ body{ | |
| 31 | \\ font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif; | |
| 32 | \\ margin: 0; | |
| 33 | \\ line-height: 1.5; | |
| 34 | \\ } | |
| 35 | \\ | |
| 36 | \\ @media screen and (min-width: 1025px) { | |
| 37 | \\ pre > code { | |
| 38 | \\ display: block; | |
| 39 | \\ overflow: auto; | |
| 40 | \\ line-height: normal; | |
| 41 | \\ margin: 0em; | |
| 42 | \\ } | |
| 43 | \\ .tok-kw { | |
| 44 | \\ color: #333; | |
| 45 | \\ font-weight: bold; | |
| 46 | \\ } | |
| 47 | \\ .tok-str { | |
| 48 | \\ color: #d14; | |
| 49 | \\ } | |
| 50 | \\ .tok-builtin { | |
| 51 | \\ color: #005C7A; | |
| 52 | \\ } | |
| 53 | \\ .tok-comment { | |
| 54 | \\ color: #545454; | |
| 55 | \\ font-style: italic; | |
| 56 | \\ } | |
| 57 | \\ .tok-fn { | |
| 58 | \\ color: #900; | |
| 59 | \\ font-weight: bold; | |
| 60 | \\ } | |
| 61 | \\ .tok-null { | |
| 62 | \\ color: #005C5C; | |
| 63 | \\ } | |
| 64 | \\ .tok-number { | |
| 65 | \\ color: #005C5C; | |
| 66 | \\ } | |
| 67 | \\ .tok-type { | |
| 68 | \\ color: #458; | |
| 69 | \\ font-weight: bold; | |
| 70 | \\ } | |
| 71 | \\ pre { | |
| 72 | \\ counter-reset: line; | |
| 73 | \\ } | |
| 74 | \\ pre .line:before { | |
| 75 | \\ counter-increment: line; | |
| 76 | \\ content: counter(line); | |
| 77 | \\ display: inline-block; | |
| 78 | \\ padding-right: 1em; | |
| 79 | \\ width: 2em; | |
| 80 | \\ text-align: right; | |
| 81 | \\ color: #999; | |
| 82 | \\ } | |
| 83 | \\ | |
| 84 | \\ @media (prefers-color-scheme: dark) { | |
| 85 | \\ body{ | |
| 86 | \\ background:#222; | |
| 87 | \\ color: #ccc; | |
| 88 | \\ } | |
| 89 | \\ pre > code { | |
| 90 | \\ color: #ccc; | |
| 91 | \\ background: #222; | |
| 92 | \\ border: unset; | |
| 93 | \\ } | |
| 94 | \\ .tok-kw { | |
| 95 | \\ color: #eee; | |
| 96 | \\ } | |
| 97 | \\ .tok-str { | |
| 98 | \\ color: #2e5; | |
| 99 | \\ } | |
| 100 | \\ .tok-builtin { | |
| 101 | \\ color: #ff894c; | |
| 102 | \\ } | |
| 103 | \\ .tok-comment { | |
| 104 | \\ color: #aa7; | |
| 105 | \\ } | |
| 106 | \\ .tok-fn { | |
| 107 | \\ color: #B1A0F8; | |
| 108 | \\ } | |
| 109 | \\ .tok-null { | |
| 110 | \\ color: #ff8080; | |
| 111 | \\ } | |
| 112 | \\ .tok-number { | |
| 113 | \\ color: #ff8080; | |
| 114 | \\ } | |
| 115 | \\ .tok-type { | |
| 116 | \\ color: #68f; | |
| 117 | \\ } | |
| 118 | \\ } | |
| 119 | \\ </style> | |
| 120 | \\</head> | |
| 121 | \\<body> | |
| 122 | \\ | |
| 123 | ); | |
| 124 | ||
| 125 | const source = try src.getSource(allocator); | |
| 126 | try tokenizeAndPrintRaw(allocator, out, source.bytes); | |
| 127 | try out.writeAll( | |
| 128 | \\</body> | |
| 129 | \\</html> | |
| 130 | ); | |
| 131 | } | |
| 132 | ||
| 133 | const start_line = "<span class=\"line\" id=\"L{d}\">"; | |
| 134 | const end_line = "</span>\n"; | |
| 135 | ||
| 136 | var line_counter: usize = 1; | |
| 137 | ||
| 138 | pub fn tokenizeAndPrintRaw( | |
| 139 | allocator: Allocator, | |
| 140 | out: anytype, | |
| 141 | raw_src: [:0]const u8, | |
| 142 | ) !void { | |
| 143 | const src = try allocator.dupeZ(u8, raw_src); | |
| 144 | defer allocator.free(src); | |
| 145 | ||
| 146 | line_counter = 1; | |
| 147 | ||
| 148 | try out.print("<pre><code>" ++ start_line, .{line_counter}); | |
| 149 | var tokenizer = std.zig.Tokenizer.init(src); | |
| 150 | var index: usize = 0; | |
| 151 | var next_tok_is_fn = false; | |
| 152 | while (true) { | |
| 153 | const prev_tok_was_fn = next_tok_is_fn; | |
| 154 | next_tok_is_fn = false; | |
| 155 | ||
| 156 | const token = tokenizer.next(); | |
| 157 | if (mem.indexOf(u8, src[index..token.loc.start], "//")) |comment_start_off| { | |
| 158 | // render one comment | |
| 159 | const comment_start = index + comment_start_off; | |
| 160 | const comment_end_off = mem.indexOf(u8, src[comment_start..token.loc.start], "\n"); | |
| 161 | const comment_end = if (comment_end_off) |o| comment_start + o else token.loc.start; | |
| 162 | ||
| 163 | try writeEscapedLines(out, src[index..comment_start]); | |
| 164 | try out.writeAll("<span class=\"tok-comment\">"); | |
| 165 | try writeEscaped(out, src[comment_start..comment_end]); | |
| 166 | try out.writeAll("</span>\n"); | |
| 167 | index = comment_end; | |
| 168 | tokenizer.index = index; | |
| 169 | continue; | |
| 170 | } | |
| 171 | ||
| 172 | try writeEscapedLines(out, src[index..token.loc.start]); | |
| 173 | switch (token.tag) { | |
| 174 | .eof => break, | |
| 175 | ||
| 176 | .keyword_addrspace, | |
| 177 | .keyword_align, | |
| 178 | .keyword_and, | |
| 179 | .keyword_asm, | |
| 180 | .keyword_async, | |
| 181 | .keyword_await, | |
| 182 | .keyword_break, | |
| 183 | .keyword_catch, | |
| 184 | .keyword_comptime, | |
| 185 | .keyword_const, | |
| 186 | .keyword_continue, | |
| 187 | .keyword_defer, | |
| 188 | .keyword_else, | |
| 189 | .keyword_enum, | |
| 190 | .keyword_errdefer, | |
| 191 | .keyword_error, | |
| 192 | .keyword_export, | |
| 193 | .keyword_extern, | |
| 194 | .keyword_for, | |
| 195 | .keyword_if, | |
| 196 | .keyword_inline, | |
| 197 | .keyword_noalias, | |
| 198 | .keyword_noinline, | |
| 199 | .keyword_nosuspend, | |
| 200 | .keyword_opaque, | |
| 201 | .keyword_or, | |
| 202 | .keyword_orelse, | |
| 203 | .keyword_packed, | |
| 204 | .keyword_anyframe, | |
| 205 | .keyword_pub, | |
| 206 | .keyword_resume, | |
| 207 | .keyword_return, | |
| 208 | .keyword_linksection, | |
| 209 | .keyword_callconv, | |
| 210 | .keyword_struct, | |
| 211 | .keyword_suspend, | |
| 212 | .keyword_switch, | |
| 213 | .keyword_test, | |
| 214 | .keyword_threadlocal, | |
| 215 | .keyword_try, | |
| 216 | .keyword_union, | |
| 217 | .keyword_unreachable, | |
| 218 | .keyword_usingnamespace, | |
| 219 | .keyword_var, | |
| 220 | .keyword_volatile, | |
| 221 | .keyword_allowzero, | |
| 222 | .keyword_while, | |
| 223 | .keyword_anytype, | |
| 224 | => { | |
| 225 | try out.writeAll("<span class=\"tok-kw\">"); | |
| 226 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 227 | try out.writeAll("</span>"); | |
| 228 | }, | |
| 229 | ||
| 230 | .keyword_fn => { | |
| 231 | try out.writeAll("<span class=\"tok-kw\">"); | |
| 232 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 233 | try out.writeAll("</span>"); | |
| 234 | next_tok_is_fn = true; | |
| 235 | }, | |
| 236 | ||
| 237 | .string_literal, | |
| 238 | .char_literal, | |
| 239 | => { | |
| 240 | try out.writeAll("<span class=\"tok-str\">"); | |
| 241 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 242 | try out.writeAll("</span>"); | |
| 243 | }, | |
| 244 | ||
| 245 | .multiline_string_literal_line => { | |
| 246 | if (src[token.loc.end - 1] == '\n') { | |
| 247 | try out.writeAll("<span class=\"tok-str\">"); | |
| 248 | try writeEscaped(out, src[token.loc.start .. token.loc.end - 1]); | |
| 249 | line_counter += 1; | |
| 250 | try out.print("</span>" ++ end_line ++ "\n" ++ start_line, .{line_counter}); | |
| 251 | } else { | |
| 252 | try out.writeAll("<span class=\"tok-str\">"); | |
| 253 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 254 | try out.writeAll("</span>"); | |
| 255 | } | |
| 256 | }, | |
| 257 | ||
| 258 | .builtin => { | |
| 259 | try out.writeAll("<span class=\"tok-builtin\">"); | |
| 260 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 261 | try out.writeAll("</span>"); | |
| 262 | }, | |
| 263 | ||
| 264 | .doc_comment, | |
| 265 | .container_doc_comment, | |
| 266 | => { | |
| 267 | try out.writeAll("<span class=\"tok-comment\">"); | |
| 268 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 269 | try out.writeAll("</span>"); | |
| 270 | }, | |
| 271 | ||
| 272 | .identifier => { | |
| 273 | const tok_bytes = src[token.loc.start..token.loc.end]; | |
| 274 | if (mem.eql(u8, tok_bytes, "undefined") or | |
| 275 | mem.eql(u8, tok_bytes, "null") or | |
| 276 | mem.eql(u8, tok_bytes, "true") or | |
| 277 | mem.eql(u8, tok_bytes, "false")) | |
| 278 | { | |
| 279 | try out.writeAll("<span class=\"tok-null\">"); | |
| 280 | try writeEscaped(out, tok_bytes); | |
| 281 | try out.writeAll("</span>"); | |
| 282 | } else if (prev_tok_was_fn) { | |
| 283 | try out.writeAll("<span class=\"tok-fn\">"); | |
| 284 | try writeEscaped(out, tok_bytes); | |
| 285 | try out.writeAll("</span>"); | |
| 286 | } else { | |
| 287 | const is_int = blk: { | |
| 288 | if (src[token.loc.start] != 'i' and src[token.loc.start] != 'u') | |
| 289 | break :blk false; | |
| 290 | var i = token.loc.start + 1; | |
| 291 | if (i == token.loc.end) | |
| 292 | break :blk false; | |
| 293 | while (i != token.loc.end) : (i += 1) { | |
| 294 | if (src[i] < '0' or src[i] > '9') | |
| 295 | break :blk false; | |
| 296 | } | |
| 297 | break :blk true; | |
| 298 | }; | |
| 299 | if (is_int or isType(tok_bytes)) { | |
| 300 | try out.writeAll("<span class=\"tok-type\">"); | |
| 301 | try writeEscaped(out, tok_bytes); | |
| 302 | try out.writeAll("</span>"); | |
| 303 | } else { | |
| 304 | try writeEscaped(out, tok_bytes); | |
| 305 | } | |
| 306 | } | |
| 307 | }, | |
| 308 | ||
| 309 | .integer_literal, | |
| 310 | .float_literal, | |
| 311 | => { | |
| 312 | try out.writeAll("<span class=\"tok-number\">"); | |
| 313 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 314 | try out.writeAll("</span>"); | |
| 315 | }, | |
| 316 | ||
| 317 | .bang, | |
| 318 | .pipe, | |
| 319 | .pipe_pipe, | |
| 320 | .pipe_equal, | |
| 321 | .equal, | |
| 322 | .equal_equal, | |
| 323 | .equal_angle_bracket_right, | |
| 324 | .bang_equal, | |
| 325 | .l_paren, | |
| 326 | .r_paren, | |
| 327 | .semicolon, | |
| 328 | .percent, | |
| 329 | .percent_equal, | |
| 330 | .l_brace, | |
| 331 | .r_brace, | |
| 332 | .l_bracket, | |
| 333 | .r_bracket, | |
| 334 | .period, | |
| 335 | .period_asterisk, | |
| 336 | .ellipsis2, | |
| 337 | .ellipsis3, | |
| 338 | .caret, | |
| 339 | .caret_equal, | |
| 340 | .plus, | |
| 341 | .plus_plus, | |
| 342 | .plus_equal, | |
| 343 | .plus_percent, | |
| 344 | .plus_percent_equal, | |
| 345 | .plus_pipe, | |
| 346 | .plus_pipe_equal, | |
| 347 | .minus, | |
| 348 | .minus_equal, | |
| 349 | .minus_percent, | |
| 350 | .minus_percent_equal, | |
| 351 | .minus_pipe, | |
| 352 | .minus_pipe_equal, | |
| 353 | .asterisk, | |
| 354 | .asterisk_equal, | |
| 355 | .asterisk_asterisk, | |
| 356 | .asterisk_percent, | |
| 357 | .asterisk_percent_equal, | |
| 358 | .asterisk_pipe, | |
| 359 | .asterisk_pipe_equal, | |
| 360 | .arrow, | |
| 361 | .colon, | |
| 362 | .slash, | |
| 363 | .slash_equal, | |
| 364 | .comma, | |
| 365 | .ampersand, | |
| 366 | .ampersand_equal, | |
| 367 | .question_mark, | |
| 368 | .angle_bracket_left, | |
| 369 | .angle_bracket_left_equal, | |
| 370 | .angle_bracket_angle_bracket_left, | |
| 371 | .angle_bracket_angle_bracket_left_equal, | |
| 372 | .angle_bracket_angle_bracket_left_pipe, | |
| 373 | .angle_bracket_angle_bracket_left_pipe_equal, | |
| 374 | .angle_bracket_right, | |
| 375 | .angle_bracket_right_equal, | |
| 376 | .angle_bracket_angle_bracket_right, | |
| 377 | .angle_bracket_angle_bracket_right_equal, | |
| 378 | .tilde, | |
| 379 | => try writeEscaped(out, src[token.loc.start..token.loc.end]), | |
| 380 | ||
| 381 | .invalid, .invalid_periodasterisks => return error.ParseError, | |
| 382 | } | |
| 383 | index = token.loc.end; | |
| 384 | } | |
| 385 | try out.writeAll(end_line ++ "</code></pre>"); | |
| 386 | } | |
| 387 | ||
| 388 | fn writeEscapedLines(out: anytype, text: []const u8) !void { | |
| 389 | for (text) |char| { | |
| 390 | if (char == '\n') { | |
| 391 | try out.writeAll(end_line); | |
| 392 | line_counter += 1; | |
| 393 | try out.print(start_line, .{line_counter}); | |
| 394 | } else { | |
| 395 | try writeEscaped(out, &[_]u8{char}); | |
| 396 | } | |
| 397 | } | |
| 398 | } | |
| 399 | ||
| 400 | fn writeEscaped(out: anytype, input: []const u8) !void { | |
| 401 | for (input) |c| { | |
| 402 | try switch (c) { | |
| 403 | '&' => out.writeAll("&amp;"), | |
| 404 | '<' => out.writeAll("&lt;"), | |
| 405 | '>' => out.writeAll("&gt;"), | |
| 406 | '"' => out.writeAll("&quot;"), | |
| 407 | else => out.writeByte(c), | |
| 408 | }; | |
| 409 | } | |
| 410 | } | |
| 411 | ||
| 412 | const builtin_types = [_][]const u8{ | |
| 413 | "f16", "f32", "f64", "f128", "c_longdouble", "c_short", | |
| 414 | "c_ushort", "c_int", "c_uint", "c_long", "c_ulong", "c_longlong", | |
| 415 | "c_ulonglong", "c_char", "anyopaque", "void", "bool", "isize", | |
| 416 | "usize", "noreturn", "type", "anyerror", "comptime_int", "comptime_float", | |
| 417 | }; | |
| 418 | ||
| 419 | fn isType(name: []const u8) bool { | |
| 420 | for (builtin_types) |t| { | |
| 421 | if (mem.eql(u8, t, name)) | |
| 422 | return true; | |
| 423 | } | |
| 424 | return false; | |
| 425 | } |
src/autodoc/render_source.zig created+424| ... | ... | @@ -0,0 +1,424 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const io = std.io; | |
| 4 | const fs = std.fs; | |
| 5 | const process = std.process; | |
| 6 | const ChildProcess = std.ChildProcess; | |
| 7 | const Progress = std.Progress; | |
| 8 | const print = std.debug.print; | |
| 9 | const mem = std.mem; | |
| 10 | const testing = std.testing; | |
| 11 | const Allocator = std.mem.Allocator; | |
| 12 | const Module = @import("../Module.zig"); | |
| 13 | ||
| 14 | pub fn genHtml( | |
| 15 | allocator: Allocator, | |
| 16 | src: *Module.File, | |
| 17 | out: anytype, | |
| 18 | ) !void { | |
| 19 | try out.writeAll( | |
| 20 | \\<!doctype html> | |
| 21 | \\<html lang="en"> | |
| 22 | \\<head> | |
| 23 | \\ <meta charset="utf-8"> | |
| 24 | \\ <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| 25 | ); | |
| 26 | try out.print(" <title>{s} - source view</title>\n", .{src.sub_file_path}); | |
| 27 | try out.writeAll( | |
| 28 | \\ <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg=="/> | |
| 29 | \\ <style> | |
| 30 | \\ body{ | |
| 31 | \\ font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif; | |
| 32 | \\ margin: 0; | |
| 33 | \\ line-height: 1.5; | |
| 34 | \\ } | |
| 35 | \\ | |
| 36 | \\ pre > code { | |
| 37 | \\ display: block; | |
| 38 | \\ overflow: auto; | |
| 39 | \\ line-height: normal; | |
| 40 | \\ margin: 0em; | |
| 41 | \\ } | |
| 42 | \\ .tok-kw { | |
| 43 | \\ color: #333; | |
| 44 | \\ font-weight: bold; | |
| 45 | \\ } | |
| 46 | \\ .tok-str { | |
| 47 | \\ color: #d14; | |
| 48 | \\ } | |
| 49 | \\ .tok-builtin { | |
| 50 | \\ color: #005C7A; | |
| 51 | \\ } | |
| 52 | \\ .tok-comment { | |
| 53 | \\ color: #545454; | |
| 54 | \\ font-style: italic; | |
| 55 | \\ } | |
| 56 | \\ .tok-fn { | |
| 57 | \\ color: #900; | |
| 58 | \\ font-weight: bold; | |
| 59 | \\ } | |
| 60 | \\ .tok-null { | |
| 61 | \\ color: #005C5C; | |
| 62 | \\ } | |
| 63 | \\ .tok-number { | |
| 64 | \\ color: #005C5C; | |
| 65 | \\ } | |
| 66 | \\ .tok-type { | |
| 67 | \\ color: #458; | |
| 68 | \\ font-weight: bold; | |
| 69 | \\ } | |
| 70 | \\ pre { | |
| 71 | \\ counter-reset: line; | |
| 72 | \\ } | |
| 73 | \\ pre .line:before { | |
| 74 | \\ counter-increment: line; | |
| 75 | \\ content: counter(line); | |
| 76 | \\ display: inline-block; | |
| 77 | \\ padding-right: 1em; | |
| 78 | \\ width: 2em; | |
| 79 | \\ text-align: right; | |
| 80 | \\ color: #999; | |
| 81 | \\ } | |
| 82 | \\ | |
| 83 | \\ @media (prefers-color-scheme: dark) { | |
| 84 | \\ body{ | |
| 85 | \\ background:#222; | |
| 86 | \\ color: #ccc; | |
| 87 | \\ } | |
| 88 | \\ pre > code { | |
| 89 | \\ color: #ccc; | |
| 90 | \\ background: #222; | |
| 91 | \\ border: unset; | |
| 92 | \\ } | |
| 93 | \\ .tok-kw { | |
| 94 | \\ color: #eee; | |
| 95 | \\ } | |
| 96 | \\ .tok-str { | |
| 97 | \\ color: #2e5; | |
| 98 | \\ } | |
| 99 | \\ .tok-builtin { | |
| 100 | \\ color: #ff894c; | |
| 101 | \\ } | |
| 102 | \\ .tok-comment { | |
| 103 | \\ color: #aa7; | |
| 104 | \\ } | |
| 105 | \\ .tok-fn { | |
| 106 | \\ color: #B1A0F8; | |
| 107 | \\ } | |
| 108 | \\ .tok-null { | |
| 109 | \\ color: #ff8080; | |
| 110 | \\ } | |
| 111 | \\ .tok-number { | |
| 112 | \\ color: #ff8080; | |
| 113 | \\ } | |
| 114 | \\ .tok-type { | |
| 115 | \\ color: #68f; | |
| 116 | \\ } | |
| 117 | \\ } | |
| 118 | \\ </style> | |
| 119 | \\</head> | |
| 120 | \\<body> | |
| 121 | \\ | |
| 122 | ); | |
| 123 | ||
| 124 | const source = try src.getSource(allocator); | |
| 125 | try tokenizeAndPrintRaw(allocator, out, source.bytes); | |
| 126 | try out.writeAll( | |
| 127 | \\</body> | |
| 128 | \\</html> | |
| 129 | ); | |
| 130 | } | |
| 131 | ||
| 132 | const start_line = "<span class=\"line\" id=\"L{d}\">"; | |
| 133 | const end_line = "</span>\n"; | |
| 134 | ||
| 135 | var line_counter: usize = 1; | |
| 136 | ||
| 137 | pub fn tokenizeAndPrintRaw( | |
| 138 | allocator: Allocator, | |
| 139 | out: anytype, | |
| 140 | raw_src: [:0]const u8, | |
| 141 | ) !void { | |
| 142 | const src = try allocator.dupeZ(u8, raw_src); | |
| 143 | defer allocator.free(src); | |
| 144 | ||
| 145 | line_counter = 1; | |
| 146 | ||
| 147 | try out.print("<pre><code>" ++ start_line, .{line_counter}); | |
| 148 | var tokenizer = std.zig.Tokenizer.init(src); | |
| 149 | var index: usize = 0; | |
| 150 | var next_tok_is_fn = false; | |
| 151 | while (true) { | |
| 152 | const prev_tok_was_fn = next_tok_is_fn; | |
| 153 | next_tok_is_fn = false; | |
| 154 | ||
| 155 | const token = tokenizer.next(); | |
| 156 | if (mem.indexOf(u8, src[index..token.loc.start], "//")) |comment_start_off| { | |
| 157 | // render one comment | |
| 158 | const comment_start = index + comment_start_off; | |
| 159 | const comment_end_off = mem.indexOf(u8, src[comment_start..token.loc.start], "\n"); | |
| 160 | const comment_end = if (comment_end_off) |o| comment_start + o else token.loc.start; | |
| 161 | ||
| 162 | try writeEscapedLines(out, src[index..comment_start]); | |
| 163 | try out.writeAll("<span class=\"tok-comment\">"); | |
| 164 | try writeEscaped(out, src[comment_start..comment_end]); | |
| 165 | try out.writeAll("</span>\n"); | |
| 166 | index = comment_end; | |
| 167 | tokenizer.index = index; | |
| 168 | continue; | |
| 169 | } | |
| 170 | ||
| 171 | try writeEscapedLines(out, src[index..token.loc.start]); | |
| 172 | switch (token.tag) { | |
| 173 | .eof => break, | |
| 174 | ||
| 175 | .keyword_addrspace, | |
| 176 | .keyword_align, | |
| 177 | .keyword_and, | |
| 178 | .keyword_asm, | |
| 179 | .keyword_async, | |
| 180 | .keyword_await, | |
| 181 | .keyword_break, | |
| 182 | .keyword_catch, | |
| 183 | .keyword_comptime, | |
| 184 | .keyword_const, | |
| 185 | .keyword_continue, | |
| 186 | .keyword_defer, | |
| 187 | .keyword_else, | |
| 188 | .keyword_enum, | |
| 189 | .keyword_errdefer, | |
| 190 | .keyword_error, | |
| 191 | .keyword_export, | |
| 192 | .keyword_extern, | |
| 193 | .keyword_for, | |
| 194 | .keyword_if, | |
| 195 | .keyword_inline, | |
| 196 | .keyword_noalias, | |
| 197 | .keyword_noinline, | |
| 198 | .keyword_nosuspend, | |
| 199 | .keyword_opaque, | |
| 200 | .keyword_or, | |
| 201 | .keyword_orelse, | |
| 202 | .keyword_packed, | |
| 203 | .keyword_anyframe, | |
| 204 | .keyword_pub, | |
| 205 | .keyword_resume, | |
| 206 | .keyword_return, | |
| 207 | .keyword_linksection, | |
| 208 | .keyword_callconv, | |
| 209 | .keyword_struct, | |
| 210 | .keyword_suspend, | |
| 211 | .keyword_switch, | |
| 212 | .keyword_test, | |
| 213 | .keyword_threadlocal, | |
| 214 | .keyword_try, | |
| 215 | .keyword_union, | |
| 216 | .keyword_unreachable, | |
| 217 | .keyword_usingnamespace, | |
| 218 | .keyword_var, | |
| 219 | .keyword_volatile, | |
| 220 | .keyword_allowzero, | |
| 221 | .keyword_while, | |
| 222 | .keyword_anytype, | |
| 223 | => { | |
| 224 | try out.writeAll("<span class=\"tok-kw\">"); | |
| 225 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 226 | try out.writeAll("</span>"); | |
| 227 | }, | |
| 228 | ||
| 229 | .keyword_fn => { | |
| 230 | try out.writeAll("<span class=\"tok-kw\">"); | |
| 231 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 232 | try out.writeAll("</span>"); | |
| 233 | next_tok_is_fn = true; | |
| 234 | }, | |
| 235 | ||
| 236 | .string_literal, | |
| 237 | .char_literal, | |
| 238 | => { | |
| 239 | try out.writeAll("<span class=\"tok-str\">"); | |
| 240 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 241 | try out.writeAll("</span>"); | |
| 242 | }, | |
| 243 | ||
| 244 | .multiline_string_literal_line => { | |
| 245 | if (src[token.loc.end - 1] == '\n') { | |
| 246 | try out.writeAll("<span class=\"tok-str\">"); | |
| 247 | try writeEscaped(out, src[token.loc.start .. token.loc.end - 1]); | |
| 248 | line_counter += 1; | |
| 249 | try out.print("</span>" ++ end_line ++ "\n" ++ start_line, .{line_counter}); | |
| 250 | } else { | |
| 251 | try out.writeAll("<span class=\"tok-str\">"); | |
| 252 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 253 | try out.writeAll("</span>"); | |
| 254 | } | |
| 255 | }, | |
| 256 | ||
| 257 | .builtin => { | |
| 258 | try out.writeAll("<span class=\"tok-builtin\">"); | |
| 259 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 260 | try out.writeAll("</span>"); | |
| 261 | }, | |
| 262 | ||
| 263 | .doc_comment, | |
| 264 | .container_doc_comment, | |
| 265 | => { | |
| 266 | try out.writeAll("<span class=\"tok-comment\">"); | |
| 267 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 268 | try out.writeAll("</span>"); | |
| 269 | }, | |
| 270 | ||
| 271 | .identifier => { | |
| 272 | const tok_bytes = src[token.loc.start..token.loc.end]; | |
| 273 | if (mem.eql(u8, tok_bytes, "undefined") or | |
| 274 | mem.eql(u8, tok_bytes, "null") or | |
| 275 | mem.eql(u8, tok_bytes, "true") or | |
| 276 | mem.eql(u8, tok_bytes, "false")) | |
| 277 | { | |
| 278 | try out.writeAll("<span class=\"tok-null\">"); | |
| 279 | try writeEscaped(out, tok_bytes); | |
| 280 | try out.writeAll("</span>"); | |
| 281 | } else if (prev_tok_was_fn) { | |
| 282 | try out.writeAll("<span class=\"tok-fn\">"); | |
| 283 | try writeEscaped(out, tok_bytes); | |
| 284 | try out.writeAll("</span>"); | |
| 285 | } else { | |
| 286 | const is_int = blk: { | |
| 287 | if (src[token.loc.start] != 'i' and src[token.loc.start] != 'u') | |
| 288 | break :blk false; | |
| 289 | var i = token.loc.start + 1; | |
| 290 | if (i == token.loc.end) | |
| 291 | break :blk false; | |
| 292 | while (i != token.loc.end) : (i += 1) { | |
| 293 | if (src[i] < '0' or src[i] > '9') | |
| 294 | break :blk false; | |
| 295 | } | |
| 296 | break :blk true; | |
| 297 | }; | |
| 298 | if (is_int or isType(tok_bytes)) { | |
| 299 | try out.writeAll("<span class=\"tok-type\">"); | |
| 300 | try writeEscaped(out, tok_bytes); | |
| 301 | try out.writeAll("</span>"); | |
| 302 | } else { | |
| 303 | try writeEscaped(out, tok_bytes); | |
| 304 | } | |
| 305 | } | |
| 306 | }, | |
| 307 | ||
| 308 | .integer_literal, | |
| 309 | .float_literal, | |
| 310 | => { | |
| 311 | try out.writeAll("<span class=\"tok-number\">"); | |
| 312 | try writeEscaped(out, src[token.loc.start..token.loc.end]); | |
| 313 | try out.writeAll("</span>"); | |
| 314 | }, | |
| 315 | ||
| 316 | .bang, | |
| 317 | .pipe, | |
| 318 | .pipe_pipe, | |
| 319 | .pipe_equal, | |
| 320 | .equal, | |
| 321 | .equal_equal, | |
| 322 | .equal_angle_bracket_right, | |
| 323 | .bang_equal, | |
| 324 | .l_paren, | |
| 325 | .r_paren, | |
| 326 | .semicolon, | |
| 327 | .percent, | |
| 328 | .percent_equal, | |
| 329 | .l_brace, | |
| 330 | .r_brace, | |
| 331 | .l_bracket, | |
| 332 | .r_bracket, | |
| 333 | .period, | |
| 334 | .period_asterisk, | |
| 335 | .ellipsis2, | |
| 336 | .ellipsis3, | |
| 337 | .caret, | |
| 338 | .caret_equal, | |
| 339 | .plus, | |
| 340 | .plus_plus, | |
| 341 | .plus_equal, | |
| 342 | .plus_percent, | |
| 343 | .plus_percent_equal, | |
| 344 | .plus_pipe, | |
| 345 | .plus_pipe_equal, | |
| 346 | .minus, | |
| 347 | .minus_equal, | |
| 348 | .minus_percent, | |
| 349 | .minus_percent_equal, | |
| 350 | .minus_pipe, | |
| 351 | .minus_pipe_equal, | |
| 352 | .asterisk, | |
| 353 | .asterisk_equal, | |
| 354 | .asterisk_asterisk, | |
| 355 | .asterisk_percent, | |
| 356 | .asterisk_percent_equal, | |
| 357 | .asterisk_pipe, | |
| 358 | .asterisk_pipe_equal, | |
| 359 | .arrow, | |
| 360 | .colon, | |
| 361 | .slash, | |
| 362 | .slash_equal, | |
| 363 | .comma, | |
| 364 | .ampersand, | |
| 365 | .ampersand_equal, | |
| 366 | .question_mark, | |
| 367 | .angle_bracket_left, | |
| 368 | .angle_bracket_left_equal, | |
| 369 | .angle_bracket_angle_bracket_left, | |
| 370 | .angle_bracket_angle_bracket_left_equal, | |
| 371 | .angle_bracket_angle_bracket_left_pipe, | |
| 372 | .angle_bracket_angle_bracket_left_pipe_equal, | |
| 373 | .angle_bracket_right, | |
| 374 | .angle_bracket_right_equal, | |
| 375 | .angle_bracket_angle_bracket_right, | |
| 376 | .angle_bracket_angle_bracket_right_equal, | |
| 377 | .tilde, | |
| 378 | => try writeEscaped(out, src[token.loc.start..token.loc.end]), | |
| 379 | ||
| 380 | .invalid, .invalid_periodasterisks => return error.ParseError, | |
| 381 | } | |
| 382 | index = token.loc.end; | |
| 383 | } | |
| 384 | try out.writeAll(end_line ++ "</code></pre>"); | |
| 385 | } | |
| 386 | ||
| 387 | fn writeEscapedLines(out: anytype, text: []const u8) !void { | |
| 388 | for (text) |char| { | |
| 389 | if (char == '\n') { | |
| 390 | try out.writeAll(end_line); | |
| 391 | line_counter += 1; | |
| 392 | try out.print(start_line, .{line_counter}); | |
| 393 | } else { | |
| 394 | try writeEscaped(out, &[_]u8{char}); | |
| 395 | } | |
| 396 | } | |
| 397 | } | |
| 398 | ||
| 399 | fn writeEscaped(out: anytype, input: []const u8) !void { | |
| 400 | for (input) |c| { | |
| 401 | try switch (c) { | |
| 402 | '&' => out.writeAll("&amp;"), | |
| 403 | '<' => out.writeAll("&lt;"), | |
| 404 | '>' => out.writeAll("&gt;"), | |
| 405 | '"' => out.writeAll("&quot;"), | |
| 406 | else => out.writeByte(c), | |
| 407 | }; | |
| 408 | } | |
| 409 | } | |
| 410 | ||
| 411 | const builtin_types = [_][]const u8{ | |
| 412 | "f16", "f32", "f64", "f128", "c_longdouble", "c_short", | |
| 413 | "c_ushort", "c_int", "c_uint", "c_long", "c_ulong", "c_longlong", | |
| 414 | "c_ulonglong", "c_char", "anyopaque", "void", "bool", "isize", | |
| 415 | "usize", "noreturn", "type", "anyerror", "comptime_int", "comptime_float", | |
| 416 | }; | |
| 417 | ||
| 418 | fn isType(name: []const u8) bool { | |
| 419 | for (builtin_types) |t| { | |
| 420 | if (mem.eql(u8, t, name)) | |
| 421 | return true; | |
| 422 | } | |
| 423 | return false; | |
| 424 | } |