| ... | @@ -235,7 +235,59 @@ export fn entryPoints() Slice(u32) { | ... | @@ -235,7 +235,59 @@ export fn entryPoints() Slice(u32) { |
| 235 | return Slice(u32).init(entry_points.items); | 235 | return Slice(u32).init(entry_points.items); |
| 236 | } | 236 | } |
| 237 | | 237 | |
| | 238 | /// Index into `coverage_source_locations`. |
| | 239 | const SourceLocationIndex = enum(u32) { |
| | 240 | _, |
| | 241 | |
| | 242 | fn haveCoverage(sli: SourceLocationIndex) bool { |
| | 243 | return @intFromEnum(sli) < coverage_source_locations.items.len; |
| | 244 | } |
| | 245 | |
| | 246 | fn ptr(sli: SourceLocationIndex) *Coverage.SourceLocation { |
| | 247 | return &coverage_source_locations.items[@intFromEnum(sli)]; |
| | 248 | } |
| | 249 | |
| | 250 | fn sourceLocationLinkHtml( |
| | 251 | sli: SourceLocationIndex, |
| | 252 | out: *std.ArrayListUnmanaged(u8), |
| | 253 | ) Allocator.Error!void { |
| | 254 | const sl = sli.ptr(); |
| | 255 | try out.writer(gpa).print("<a href=\"#l{d}\">", .{@intFromEnum(sli)}); |
| | 256 | try sli.appendPath(out); |
| | 257 | try out.writer(gpa).print(":{d}:{d}</a>", .{ sl.line, sl.column }); |
| | 258 | } |
| | 259 | |
| | 260 | fn appendPath(sli: SourceLocationIndex, out: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { |
| | 261 | const sl = sli.ptr(); |
| | 262 | const file = coverage.fileAt(sl.file); |
| | 263 | const file_name = coverage.stringAt(file.basename); |
| | 264 | const dir_name = coverage.stringAt(coverage.directories.keys()[file.directory_index]); |
| | 265 | try html_render.appendEscaped(out, dir_name); |
| | 266 | try out.appendSlice(gpa, "/"); |
| | 267 | try html_render.appendEscaped(out, file_name); |
| | 268 | } |
| | 269 | |
| | 270 | fn toWalkFile(sli: SourceLocationIndex) ?Walk.File.Index { |
| | 271 | var buf: std.ArrayListUnmanaged(u8) = .{}; |
| | 272 | defer buf.deinit(gpa); |
| | 273 | sli.appendPath(&buf) catch @panic("OOM"); |
| | 274 | return @enumFromInt(Walk.files.getIndex(buf.items) orelse return null); |
| | 275 | } |
| | 276 | |
| | 277 | fn fileHtml( |
| | 278 | sli: SourceLocationIndex, |
| | 279 | out: *std.ArrayListUnmanaged(u8), |
| | 280 | ) error{ OutOfMemory, SourceUnavailable }!void { |
| | 281 | const walk_file_index = sli.toWalkFile() orelse return error.SourceUnavailable; |
| | 282 | const root_node = walk_file_index.findRootDecl().get().ast_node; |
| | 283 | html_render.fileSourceHtml(walk_file_index, out, root_node, .{}) catch |err| { |
| | 284 | fatal("unable to render source: {s}", .{@errorName(err)}); |
| | 285 | }; |
| | 286 | } |
| | 287 | }; |
| | 288 | |
| 238 | var coverage = Coverage.init; | 289 | var coverage = Coverage.init; |
| | 290 | /// Index of type `SourceLocationIndex`. |
| 239 | var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .{}; | 291 | var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .{}; |
| 240 | /// Contains the most recent coverage update message, unmodified. | 292 | /// Contains the most recent coverage update message, unmodified. |
| 241 | var recent_coverage_update: std.ArrayListUnmanaged(u8) = .{}; | 293 | var recent_coverage_update: std.ArrayListUnmanaged(u8) = .{}; |
| ... | @@ -263,27 +315,24 @@ fn updateCoverage( | ... | @@ -263,27 +315,24 @@ fn updateCoverage( |
| 263 | try coverage.directories.reIndexContext(gpa, .{ .string_bytes = coverage.string_bytes.items }); | 315 | try coverage.directories.reIndexContext(gpa, .{ .string_bytes = coverage.string_bytes.items }); |
| 264 | } | 316 | } |
| 265 | | 317 | |
| 266 | export fn sourceLocationLinkHtml(index: u32) String { | 318 | export fn sourceLocationLinkHtml(index: SourceLocationIndex) String { |
| 267 | string_result.clearRetainingCapacity(); | 319 | string_result.clearRetainingCapacity(); |
| 268 | sourceLocationLinkHtmlFallible(index, &string_result) catch @panic("OOM"); | 320 | index.sourceLocationLinkHtml(&string_result) catch @panic("OOM"); |
| 269 | return String.init(string_result.items); | 321 | return String.init(string_result.items); |
| 270 | } | 322 | } |
| 271 | | 323 | |
| 272 | fn sourceLocationLinkHtmlFallible(index: u32, out: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { | 324 | /// Returns empty string if coverage metadata is not available for this source location. |
| 273 | const sl = coverage_source_locations.items[index]; | 325 | export fn sourceLocationPath(sli: SourceLocationIndex) String { |
| 274 | const file = coverage.fileAt(sl.file); | 326 | string_result.clearRetainingCapacity(); |
| 275 | const file_name = coverage.stringAt(file.basename); | 327 | if (sli.haveCoverage()) sli.appendPath(&string_result) catch @panic("OOM"); |
| 276 | const dir_name = coverage.stringAt(coverage.directories.keys()[file.directory_index]); | 328 | return String.init(string_result.items); |
| 277 | | 329 | } |
| 278 | out.clearRetainingCapacity(); | 330 | |
| 279 | try out.appendSlice(gpa, "<a href=\"#"); | 331 | export fn sourceLocationFileHtml(sli: SourceLocationIndex) String { |
| 280 | _ = html_render.missing_feature_url_escape; | 332 | string_result.clearRetainingCapacity(); |
| 281 | try out.writer(gpa).print("{s}/{s}:{d}:{d}", .{ | 333 | sli.fileHtml(&string_result) catch |err| switch (err) { |
| 282 | dir_name, file_name, sl.line, sl.column, | 334 | error.OutOfMemory => @panic("OOM"), |
| 283 | }); | 335 | error.SourceUnavailable => {}, |
| 284 | try out.appendSlice(gpa, "\">"); | 336 | }; |
| 285 | try html_render.appendEscaped(out, dir_name); | 337 | return String.init(string_result.items); |
| 286 | try out.appendSlice(gpa, "/"); | | |
| 287 | try html_render.appendEscaped(out, file_name); | | |
| 288 | try out.writer(gpa).print(":{d}:{d}</a>", .{ sl.line, sl.column }); | | |
| 289 | } | 338 | } |