unmanage diff.zig (#680)

This commit is contained in:
Techatrix 2022-09-28 18:13:55 +02:00 committed by GitHub
parent a70beba6d2
commit 8eeae02865
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 34 deletions

View File

@ -1993,7 +1993,7 @@ fn formattingHandler(server: *Server, writer: anytype, id: types.RequestId, req:
.Exited => |code| if (code == 0) { .Exited => |code| if (code == 0) {
if (std.mem.eql(u8, handle.document.text, stdout_bytes)) return try respondGeneric(writer, id, null_result_response); if (std.mem.eql(u8, handle.document.text, stdout_bytes)) return try respondGeneric(writer, id, null_result_response);
var edits = diff.edits(server.allocator, handle.document.text, stdout_bytes) catch { var edits = diff.edits(server.arena.allocator(), handle.document.text, stdout_bytes) catch {
const range = offsets.locToRange(handle.document.text, .{ .start = 0, .end = handle.document.text.len }, server.offset_encoding); const range = offsets.locToRange(handle.document.text, .{ .start = 0, .end = handle.document.text.len }, server.offset_encoding);
// If there was an error trying to diff the text, return the formatted response // If there was an error trying to diff the text, return the formatted response
// as the new text for the entire range of the document // as the new text for the entire range of the document
@ -2009,18 +2009,11 @@ fn formattingHandler(server: *Server, writer: anytype, id: types.RequestId, req:
}, },
}); });
}; };
defer {
for (edits.items) |item| item.newText.deinit();
edits.deinit();
}
// Convert from `[]diff.Edit` to `[]types.TextEdit` // Convert from `[]diff.Edit` to `[]types.TextEdit`
var text_edits = try std var text_edits = try std.ArrayListUnmanaged(types.TextEdit).initCapacity(server.arena.allocator(), edits.items.len);
.ArrayList(types.TextEdit)
.initCapacity(server.allocator, edits.items.len);
defer text_edits.deinit();
for (edits.items) |edit| { for (edits.items) |edit| {
try text_edits.append(.{ text_edits.appendAssumeCapacity(.{
.range = edit.range, .range = edit.range,
.newText = edit.newText.items, .newText = edit.newText.items,
}); });

View File

@ -1,11 +1,13 @@
const std = @import("std"); const std = @import("std");
const types = @import("types.zig"); const types = @import("types.zig");
pub const Error = error{ OutOfMemory, InvalidRange };
// This is essentially the same as `types.TextEdit`, but we use an // This is essentially the same as `types.TextEdit`, but we use an
// ArrayList(u8) here to be able to clean up the memory later on // ArrayList(u8) here to be able to clean up the memory later on
pub const Edit = struct { pub const Edit = struct {
range: types.Range, range: types.Range,
newText: std.ArrayList(u8), newText: std.ArrayListUnmanaged(u8),
}; };
// Whether the `Change` is an addition, deletion, or no change from the // Whether the `Change` is an addition, deletion, or no change from the
@ -25,7 +27,7 @@ pub fn edits(
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
a: []const u8, a: []const u8,
b: []const u8, b: []const u8,
) !std.ArrayList(Edit) { ) Error!std.ArrayListUnmanaged(Edit) {
// Given the input strings A and B, we skip over the first N characters // Given the input strings A and B, we skip over the first N characters
// where A[0..N] == B[0..N]. We want to trim the start (and end) of the // where A[0..N] == B[0..N]. We want to trim the start (and end) of the
// strings that have the same text. This decreases the size of the LCS // strings that have the same text. This decreases the size of the LCS
@ -122,7 +124,7 @@ pub const Array2D = struct {
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
rows: usize, rows: usize,
cols: usize, cols: usize,
) !Self { ) error{OutOfMemory}!Self {
const data = try allocator.alloc(usize, rows * cols); const data = try allocator.alloc(usize, rows * cols);
return Self{ return Self{
@ -183,11 +185,11 @@ pub fn get_changes(
a_trim: []const u8, a_trim: []const u8,
b_trim: []const u8, b_trim: []const u8,
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
) !std.ArrayList(Edit) { ) Error!std.ArrayListUnmanaged(Edit) {
// First we get a list of changes between strings at the character level: // First we get a list of changes between strings at the character level:
// "addition", "deletion", and "no change" for each character // "addition", "deletion", and "no change" for each character
var changes = try std.ArrayList(Change).initCapacity(allocator, a_trim.len); var changes = try std.ArrayListUnmanaged(Change).initCapacity(allocator, a_trim.len);
defer changes.deinit(); defer changes.deinit(allocator);
try recur_changes( try recur_changes(
lcs, lcs,
&changes, &changes,
@ -195,13 +197,15 @@ pub fn get_changes(
b_trim, b_trim,
@intCast(i64, a_trim.len), @intCast(i64, a_trim.len),
@intCast(i64, b_trim.len), @intCast(i64, b_trim.len),
allocator,
); );
// We want to group runs of deletions and additions, and separate them by // We want to group runs of deletions and additions, and separate them by
// runs of `.Nothing` changes. This will allow us to calculate the // runs of `.Nothing` changes. This will allow us to calculate the
// `TextEdit` ranges // `TextEdit` ranges
var groups = std.ArrayList([]Change).init(allocator); var groups = std.ArrayListUnmanaged([]Change){};
defer groups.deinit(); defer groups.deinit(allocator);
var active_change: ?[]Change = null; var active_change: ?[]Change = null;
for (changes.items) |ch, i| { for (changes.items) |ch, i| {
switch (ch.operation) { switch (ch.operation) {
@ -213,7 +217,7 @@ pub fn get_changes(
.Nothing => { .Nothing => {
if (active_change) |*ac| { if (active_change) |*ac| {
ac.* = ac.*[0..(i - (changes.items.len - ac.*.len))]; ac.* = ac.*[0..(i - (changes.items.len - ac.*.len))];
try groups.append(ac.*); try groups.append(allocator, ac.*);
active_change = null; active_change = null;
} }
}, },
@ -221,7 +225,7 @@ pub fn get_changes(
} }
if (active_change) |*ac| { if (active_change) |*ac| {
ac.* = ac.*[0..(changes.items.len - (changes.items.len - ac.*.len))]; ac.* = ac.*[0..(changes.items.len - (changes.items.len - ac.*.len))];
try groups.append(ac.*); try groups.append(allocator, ac.*);
} }
// The LCS algorithm works "in reverse", so we're putting everything back // The LCS algorithm works "in reverse", so we're putting everything back
@ -230,17 +234,17 @@ pub fn get_changes(
std.mem.reverse([]Change, groups.items); std.mem.reverse([]Change, groups.items);
for (groups.items) |group| std.mem.reverse(Change, group); for (groups.items) |group| std.mem.reverse(Change, group);
var edit_results = std.ArrayList(Edit).init(allocator); var edit_results = std.ArrayListUnmanaged(Edit){};
errdefer edit_results.deinit(); errdefer edit_results.deinit(allocator);
// Convert our grouped changes into `Edit`s // Convert our grouped changes into `Edit`s
for (groups.items) |group| { for (groups.items) |group| {
var range_start = group[0].pos; var range_start = group[0].pos;
var range_len: usize = 0; var range_len: usize = 0;
var newText = std.ArrayList(u8).init(allocator); var newText = std.ArrayListUnmanaged(u8){};
for (group) |ch| { for (group) |ch| {
switch (ch.operation) { switch (ch.operation) {
.Addition => try newText.append(ch.value.?), .Addition => try newText.append(allocator, ch.value.?),
.Deletion => range_len += 1, .Deletion => range_len += 1,
else => {}, else => {},
} }
@ -251,7 +255,7 @@ pub fn get_changes(
a_trim_offset + range_start + range_len, a_trim_offset + range_start + range_len,
); );
a_lines.reset(); a_lines.reset();
try edit_results.append(Edit{ try edit_results.append(allocator, Edit{
.range = range, .range = range,
.newText = newText, .newText = newText,
}); });
@ -262,12 +266,13 @@ pub fn get_changes(
fn recur_changes( fn recur_changes(
lcs: *Array2D, lcs: *Array2D,
changes: *std.ArrayList(Change), changes: *std.ArrayListUnmanaged(Change),
a: []const u8, a: []const u8,
b: []const u8, b: []const u8,
i: i64, i: i64,
j: i64, j: i64,
) anyerror!void { allocator: std.mem.Allocator,
) error{OutOfMemory}!void {
// This function recursively works backwards through the LCS table in // This function recursively works backwards through the LCS table in
// order to figure out what kind of changes took place to transform `a` // order to figure out what kind of changes took place to transform `a`
// into `b` // into `b`
@ -276,26 +281,26 @@ fn recur_changes(
const jj = @intCast(usize, j); const jj = @intCast(usize, j);
if (i > 0 and j > 0 and a[ii - 1] == b[jj - 1]) { if (i > 0 and j > 0 and a[ii - 1] == b[jj - 1]) {
try changes.append(.{ try changes.append(allocator, .{
.operation = .Nothing, .operation = .Nothing,
.pos = ii - 1, .pos = ii - 1,
.value = null, .value = null,
}); });
try recur_changes(lcs, changes, a, b, i - 1, j - 1); try recur_changes(lcs, changes, a, b, i - 1, j - 1, allocator);
} else if (j > 0 and (i == 0 or lcs.get(ii, jj - 1).* >= lcs.get(ii - 1, jj).*)) { } else if (j > 0 and (i == 0 or lcs.get(ii, jj - 1).* >= lcs.get(ii - 1, jj).*)) {
try changes.append(.{ try changes.append(allocator, .{
.operation = .Addition, .operation = .Addition,
.pos = ii, .pos = ii,
.value = b[jj - 1], .value = b[jj - 1],
}); });
try recur_changes(lcs, changes, a, b, i, j - 1); try recur_changes(lcs, changes, a, b, i, j - 1, allocator);
} else if (i > 0 and (j == 0 or lcs.get(ii, jj - 1).* < lcs.get(ii - 1, jj).*)) { } else if (i > 0 and (j == 0 or lcs.get(ii, jj - 1).* < lcs.get(ii - 1, jj).*)) {
try changes.append(.{ try changes.append(allocator, .{
.operation = .Deletion, .operation = .Deletion,
.pos = ii - 1, .pos = ii - 1,
.value = a[ii - 1], .value = a[ii - 1],
}); });
try recur_changes(lcs, changes, a, b, i - 1, j); try recur_changes(lcs, changes, a, b, i - 1, j, allocator);
} }
} }
@ -305,7 +310,7 @@ fn char_pos_to_range(
lines: *std.mem.SplitIterator(u8), lines: *std.mem.SplitIterator(u8),
start: usize, start: usize,
end: usize, end: usize,
) !types.Range { ) Error!types.Range {
var char_pos: usize = 0; var char_pos: usize = 0;
var line_pos: usize = 0; var line_pos: usize = 0;
var result_start_pos: ?types.Position = null; var result_start_pos: ?types.Position = null;