PranitInitial commit439f83c3 months agoHistory
2779 lines · 92.8 KB · Zig
RawDownload
const std = @import("std");const debug_trace = @import("../shared/debug_trace.zig");const text_utils = @import("../shared/text_utils.zig");const types = @import("../shared/types.zig"); const Allocator = std.mem.Allocator; pub const max_preview_lines: usize = 6;pub const max_encoded_path_bytes: usize = 4 * 1024;pub const max_encoded_label_bytes: usize = 4 * 1024;pub const max_encoded_line_bytes: usize = 4 * 1024;pub const max_encoded_preview_bytes: usize = 24 * 1024;pub const max_request_projection_bytes: usize = 32 * 1024; pub const LineOp = enum(u8) { equal, add, remove }; pub const EditKind = enum(u8) { edited, added }; pub const diff_block_start_prefix: []const u8 = "\x1b]9050;";pub const diff_block_end_prefix: []const u8 = "\x1b]9051;";const osc_bell: u8 = 0x07;const trailing_newline_added_marker = "(trailing newline added)";const trailing_newline_removed_marker = "(trailing newline removed)"; pub const DiffEntry = struct {    id: u32,    full: ?FullDiff = null,     pub fn deinit(self: *DiffEntry, alloc: Allocator) void {        if (self.full) |full| full.deinit(alloc);    }}; pub const FullDiff = struct {    content: []u8,    lifecycle_id: types.ToolLifecycleId,     pub fn deinit(self: FullDiff, alloc: Allocator) void {        alloc.free(self.content);        alloc.free(@constCast(self.lifecycle_id.call_id));    }}; pub const DiffEntryPayload = struct {    preview: []u8,    additions: u32 = 0,    deletions: u32 = 0,    full: ?FullDiff = null,}; pub fn freeDiffEntryPayload(alloc: Allocator, payload: DiffEntryPayload) void {    alloc.free(payload.preview);    if (payload.full) |full| full.deinit(alloc);} test "diff entry payload has one canonical preview" {    try std.testing.expect(@hasField(DiffEntryPayload, "preview"));    try std.testing.expect(!@hasField(DiffEntryPayload, "quiet"));    try std.testing.expect(!@hasField(DiffEntryPayload, "normal"));    try std.testing.expect(!@hasField(DiffEntryPayload, "active"));} pub fn wrapWithMarkers(alloc: Allocator, id: u32, content: []const u8) ![]u8 {    var out: std.ArrayList(u8) = .empty;    errdefer out.deinit(alloc);     var id_buf: [16]u8 = undefined;    const id_str = try std.fmt.bufPrint(&id_buf, "{d}", .{id});     try out.appendSlice(alloc, diff_block_start_prefix);    try out.appendSlice(alloc, id_str);    try out.append(alloc, osc_bell);    try out.appendSlice(alloc, content);    try out.appendSlice(alloc, diff_block_end_prefix);    try out.appendSlice(alloc, id_str);    try out.append(alloc, osc_bell);    return out.toOwnedSlice(alloc);} pub fn markedDiffBlockId(bytes: []const u8) ?u32 {    if (!std.mem.startsWith(u8, bytes, diff_block_start_prefix)) return null;    const id_start = diff_block_start_prefix.len;    const bell_pos = std.mem.indexOfScalarPos(u8, bytes, id_start, osc_bell) orelse        return null;    const id_digits = bytes[id_start..bell_pos];    const id = std.fmt.parseInt(u32, id_digits, 10) catch return null;     var end_marker: [32]u8 = undefined;    const end = std.fmt.bufPrint(        &end_marker,        "{s}{s}{c}",        .{ diff_block_end_prefix, id_digits, osc_bell },    ) catch return null;    if (!std.mem.endsWith(u8, bytes, end)) return null;    return id;} pub const DiffLine = struct {    op: LineOp,    old_num: ?u32 = null,    new_num: ?u32 = null,    text: []const u8,}; pub const PreviewOptions = struct {    max_lines: usize = max_preview_lines,    max_indexed_lines: usize = canonical_max_indexed_lines,    max_matrix_cells: usize = canonical_max_matrix_cells,    max_encoded_line_bytes: usize = max_encoded_line_bytes,    max_encoded_preview_bytes: usize = max_encoded_preview_bytes,}; pub const PreviewOp = enum {    context,    addition,    deletion,    elision,    notice,}; pub const PreviewLine = struct {    op: PreviewOp,    old_line: ?u32 = null,    new_line: ?u32 = null,    text: []const u8,}; pub const PreviewValidationError = error{    TooManyLines,    PathTooLong,    InvalidPathMetadata,    LineTooLong,    PreviewTooLarge,    InvalidLineNumbers,}; pub const FileChangePreview = struct {    path: []const u8,    path_basename_start: usize = 0,    path_source_truncated: bool = false,    lines: []const PreviewLine,    additions: usize,    deletions: usize,    truncated: bool,     pub fn validate(self: FileChangePreview) PreviewValidationError!void {        if (self.path.len > max_encoded_path_bytes) return error.PathTooLong;        if (self.path.len == 0 or self.path_basename_start >= self.path.len) {            return error.InvalidPathMetadata;        }        if (self.lines.len > max_preview_lines) return error.TooManyLines;         var encoded_bytes: usize = 0;        var addition_rows: usize = 0;        var deletion_rows: usize = 0;        var elision_rows: usize = 0;        var notice_rows: usize = 0;        for (self.lines) |line| {            if (line.text.len > max_encoded_line_bytes) return error.LineTooLong;            encoded_bytes = std.math.add(usize, encoded_bytes, line.text.len) catch                return error.PreviewTooLarge;            if (encoded_bytes > max_encoded_preview_bytes) return error.PreviewTooLarge;             const valid_numbers = switch (line.op) {                .context => line.old_line != null and line.old_line.? > 0 and                    line.new_line != null and line.new_line.? > 0,                .addition => line.old_line == null and line.new_line != null and line.new_line.? > 0,                .deletion => line.old_line != null and line.old_line.? > 0 and line.new_line == null,                .elision, .notice => line.old_line == null and line.new_line == null,            };            if (!valid_numbers) return error.InvalidLineNumbers;             switch (line.op) {                .context => {},                .addition => addition_rows += 1,                .deletion => deletion_rows += 1,                .elision => elision_rows += 1,                .notice => notice_rows += 1,            }        }         if (elision_rows > 1) return error.InvalidLineNumbers;        if (self.additions == 0 and self.deletions == 0) {            if (self.lines.len != 1 or notice_rows != 1) return error.InvalidLineNumbers;            return;        }        if (notice_rows != 0 or addition_rows + deletion_rows == 0) {            return error.InvalidLineNumbers;        }        if (addition_rows > self.additions or deletion_rows > self.deletions) {            return error.InvalidLineNumbers;        }    }     pub fn eql(a: FileChangePreview, b: FileChangePreview) bool {        if (!std.mem.eql(u8, a.path, b.path)) return false;        if (a.path_basename_start != b.path_basename_start) return false;        if (a.path_source_truncated != b.path_source_truncated) return false;        if (a.lines.len != b.lines.len) return false;        for (a.lines, b.lines) |a_line, b_line| {            if (!previewLineEql(a_line, b_line)) return false;        }        return a.additions == b.additions and            a.deletions == b.deletions and            a.truncated == b.truncated;    }}; fn previewLineEql(a: PreviewLine, b: PreviewLine) bool {    return a.op == b.op and        a.old_line == b.old_line and        a.new_line == b.new_line and        std.mem.eql(u8, a.text, b.text);} pub const ProjectedLine = struct {    op: PreviewOp,    old_line: ?u32 = null,    new_line: ?u32 = null,    text: []u8,}; pub const BoundedDiffProjection = struct {    lines: []ProjectedLine,    additions: usize,    deletions: usize,    truncated: bool,     pub fn deinit(self: BoundedDiffProjection, alloc: Allocator) void {        for (self.lines) |line| alloc.free(line.text);        alloc.free(self.lines);    }}; /// Immutable, borrowed review source for a prepared file mutation.////// Unlike `FileChangePreview`, this never selects a fixed number of changed/// rows. It borrows the already-frozen file contents and materializes only the/// requested viewport.pub const FileReview = struct {    mode: union(enum) {        computed: []DiffLine,        fallback: FallbackReview,    },    additions: usize,    deletions: usize,     pub fn init(        alloc: Allocator,        old_text: []const u8,        new_text: []const u8,    ) FileReviewError!FileReview {        const old_marker = trailingNewlineMarker(old_text, new_text, true);        const new_marker = trailingNewlineMarker(old_text, new_text, false);        const old_line_count = try checkedReviewAdd(            countTextLines(old_text),            @intFromBool(old_marker != null),        );        const new_line_count = try checkedReviewAdd(            countTextLines(new_text),            @intFromBool(new_marker != null),        );        const indexed_lines = try checkedReviewAdd(old_line_count, new_line_count);        const matrix_cells = try checkedReviewMul(            try checkedReviewAdd(old_line_count, 1),            try checkedReviewAdd(new_line_count, 1),        );         if (indexed_lines <= canonical_max_indexed_lines and            matrix_cells <= canonical_max_matrix_cells)        {            const lines = try compute(alloc, old_text, new_text);            var additions: usize = 0;            var deletions: usize = 0;            for (lines) |line| {                switch (line.op) {                    .equal => {},                    .add => additions += 1,                    .remove => deletions += 1,                }            }            return .{                .mode = .{ .computed = lines },                .additions = additions,                .deletions = deletions,            };        }         const fallback = try FallbackReview.init(            alloc,            old_text,            new_text,            old_marker,            new_marker,        );        return .{            .mode = .{ .fallback = fallback },            .additions = fallback.additions,            .deletions = fallback.deletions,        };    }     pub fn deinit(self: *FileReview, alloc: Allocator) void {        switch (self.mode) {            .computed => |lines| alloc.free(lines),            .fallback => |fallback| {                alloc.free(fallback.old_line_starts);                alloc.free(fallback.new_line_starts);            },        }        self.* = undefined;    }     pub fn rowCount(self: FileReview) usize {        const changed = self.additions + self.deletions;        if (changed == 0) return 1;        return switch (self.mode) {            .computed => |source| countComputedReviewRows(source),            .fallback => |source| countFallbackReviewRows(source),        };    }     pub fn viewport(        self: FileReview,        alloc: Allocator,        start_row: usize,        max_rows: usize,    ) Allocator.Error!ReviewViewport {        const total_rows = self.rowCount();        const start = @min(start_row, total_rows);        const count = @min(max_rows, total_rows - start);        const lines = try alloc.alloc(ReviewLine, count);        errdefer alloc.free(lines);         if (self.additions == 0 and self.deletions == 0) {            if (count == 1) {                lines[0] = .{ .op = .notice, .text = "No content changes" };            }            return .{ .lines = lines, .total_rows = total_rows };        }         switch (self.mode) {            .computed => |source| fillComputedReviewViewport(lines, source, start),            .fallback => |fallback| fillFallbackReviewViewport(lines, fallback, start),        }        return .{ .lines = lines, .total_rows = total_rows };    }}; pub const FileReviewError = error{    OutOfMemory,    ArithmeticOverflow,}; pub const ReviewLine = struct {    op: PreviewOp,    old_line: ?u32 = null,    new_line: ?u32 = null,    text: []const u8,    elision_count: usize = 0,}; pub const ReviewViewport = struct {    lines: []ReviewLine,    total_rows: usize,     pub fn deinit(self: ReviewViewport, alloc: Allocator) void {        alloc.free(self.lines);    }}; const FallbackReview = struct {    old_text: []const u8,    new_text: []const u8,    old_line_starts: []usize,    new_line_starts: []usize,    prefix_count: usize,    suffix_count: usize,    old_middle_count: usize,    new_middle_count: usize,    old_marker: ?[]const u8,    new_marker: ?[]const u8,    additions: usize,    deletions: usize,     fn init(        alloc: Allocator,        old_text: []const u8,        new_text: []const u8,        old_marker: ?[]const u8,        new_marker: ?[]const u8,    ) FileReviewError!FallbackReview {        const old_line_starts = try indexLineStarts(alloc, old_text);        errdefer alloc.free(old_line_starts);        const new_line_starts = try indexLineStarts(alloc, new_text);        errdefer alloc.free(new_line_starts);        const old_normal_count = old_line_starts.len;        const new_normal_count = new_line_starts.len;        const comparable_count = @min(old_normal_count, new_normal_count);         var prefix_count: usize = 0;        while (prefix_count < comparable_count) {            const old_line = indexedTextLine(old_text, old_line_starts, prefix_count);            const new_line = indexedTextLine(new_text, new_line_starts, prefix_count);            if (!std.mem.eql(u8, old_line.text, new_line.text)) break;            prefix_count += 1;        }         var suffix_count: usize = 0;        while (prefix_count + suffix_count < comparable_count) {            const old_line = indexedTextLine(                old_text,                old_line_starts,                old_normal_count - suffix_count - 1,            );            const new_line = indexedTextLine(                new_text,                new_line_starts,                new_normal_count - suffix_count - 1,            );            if (!std.mem.eql(u8, old_line.text, new_line.text)) break;            suffix_count += 1;        }         const old_middle_count = old_normal_count - prefix_count - suffix_count;        const new_middle_count = new_normal_count - prefix_count - suffix_count;        return .{            .old_text = old_text,            .new_text = new_text,            .old_line_starts = old_line_starts,            .new_line_starts = new_line_starts,            .prefix_count = prefix_count,            .suffix_count = suffix_count,            .old_middle_count = old_middle_count,            .new_middle_count = new_middle_count,            .old_marker = old_marker,            .new_marker = new_marker,            .additions = try checkedReviewAdd(new_middle_count, @intFromBool(new_marker != null)),            .deletions = try checkedReviewAdd(old_middle_count, @intFromBool(old_marker != null)),        };    }}; fn checkedReviewAdd(a: usize, b: usize) FileReviewError!usize {    return std.math.add(usize, a, b) catch error.ArithmeticOverflow;} fn checkedReviewMul(a: usize, b: usize) FileReviewError!usize {    return std.math.mul(usize, a, b) catch error.ArithmeticOverflow;} const review_context_lines: usize = 5; const ReviewRowCounter = struct {    count: usize = 0,     fn append(self: *@This(), _: ReviewLine) void {        self.count += 1;    }     fn isFull(_: @This()) bool {        return false;    }}; const ReviewViewportWriter = struct {    destination: []ReviewLine,    start_row: usize,    ordinal: usize = 0,    written: usize = 0,     fn append(self: *@This(), line: ReviewLine) void {        appendReviewViewportLine(            self.destination,            &self.ordinal,            &self.written,            self.start_row,            line,        );    }     fn isFull(self: @This()) bool {        return self.written == self.destination.len;    }}; fn countComputedReviewRows(source: []const DiffLine) usize {    var counter = ReviewRowCounter{};    visitComputedReviewRows(source, &counter);    return counter.count;} fn countFallbackReviewRows(source: FallbackReview) usize {    return equalRunRowCount(source.prefix_count) +        source.deletions +        source.additions +        equalRunRowCount(source.suffix_count);} fn equalRunRowCount(count: usize) usize {

Lines 1–500 of 2779.