| ... | ... | @@ -235,7 +235,59 @@ export fn entryPoints() Slice(u32) { |
| 235 | 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 | 289 | var coverage = Coverage.init; |
| 290 | /// Index of type `SourceLocationIndex`. |
| 239 | 291 | var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .{}; |
| 240 | 292 | /// Contains the most recent coverage update message, unmodified. |
| 241 | 293 | var recent_coverage_update: std.ArrayListUnmanaged(u8) = .{}; |
| ... | ... | @@ -263,27 +315,24 @@ fn updateCoverage( |
| 263 | 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 | 319 | string_result.clearRetainingCapacity(); |
| 268 | | sourceLocationLinkHtmlFallible(index, &string_result) catch @panic("OOM"); |
| 320 | index.sourceLocationLinkHtml(&string_result) catch @panic("OOM"); |
| 269 | 321 | return String.init(string_result.items); |
| 270 | 322 | } |
| 271 | 323 | |
| 272 | | fn sourceLocationLinkHtmlFallible(index: u32, out: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { |
| 273 | | const sl = coverage_source_locations.items[index]; |
| 274 | | const file = coverage.fileAt(sl.file); |
| 275 | | const file_name = coverage.stringAt(file.basename); |
| 276 | | const dir_name = coverage.stringAt(coverage.directories.keys()[file.directory_index]); |
| 277 | | |
| 278 | | out.clearRetainingCapacity(); |
| 279 | | try out.appendSlice(gpa, "<a href=\"#"); |
| 280 | | _ = html_render.missing_feature_url_escape; |
| 281 | | try out.writer(gpa).print("{s}/{s}:{d}:{d}", .{ |
| 282 | | dir_name, file_name, sl.line, sl.column, |
| 283 | | }); |
| 284 | | try out.appendSlice(gpa, "\">"); |
| 285 | | try html_render.appendEscaped(out, dir_name); |
| 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 }); |
| 324 | /// Returns empty string if coverage metadata is not available for this source location. |
| 325 | export fn sourceLocationPath(sli: SourceLocationIndex) String { |
| 326 | string_result.clearRetainingCapacity(); |
| 327 | if (sli.haveCoverage()) sli.appendPath(&string_result) catch @panic("OOM"); |
| 328 | return String.init(string_result.items); |
| 329 | } |
| 330 | |
| 331 | export fn sourceLocationFileHtml(sli: SourceLocationIndex) String { |
| 332 | string_result.clearRetainingCapacity(); |
| 333 | sli.fileHtml(&string_result) catch |err| switch (err) { |
| 334 | error.OutOfMemory => @panic("OOM"), |
| 335 | error.SourceUnavailable => {}, |
| 336 | }; |
| 337 | return String.init(string_result.items); |
| 289 | 338 | } |