359 lines
11 KiB
Zig
359 lines
11 KiB
Zig
const std = @import("std");
|
|
const Buffer = @import("Buffer");
|
|
|
|
const ArrayList = std.ArrayList;
|
|
const a = std.testing.allocator;
|
|
|
|
fn metrics() Buffer.Metrics {
|
|
return .{
|
|
.ctx = undefined,
|
|
.egc_length = struct {
|
|
fn f(_: Buffer.Metrics, _: []const u8, colcount: *c_int, _: usize) usize {
|
|
colcount.* = 1;
|
|
return 1;
|
|
}
|
|
}.f,
|
|
.egc_chunk_width = struct {
|
|
fn f(_: Buffer.Metrics, chunk_: []const u8, _: usize) usize {
|
|
return chunk_.len;
|
|
}
|
|
}.f,
|
|
.tab_width = 1,
|
|
};
|
|
}
|
|
|
|
fn get_big_doc(eol_mode: *Buffer.EolMode) !*Buffer {
|
|
const BigDocGen = struct {
|
|
line_num: usize = 0,
|
|
lines: usize = 10000,
|
|
|
|
buf: [128]u8 = undefined,
|
|
line_buf: []u8 = "",
|
|
read_count: usize = 0,
|
|
|
|
const Self = @This();
|
|
const Reader = std.io.Reader(*Self, Err, read);
|
|
const Err = error{NoSpaceLeft};
|
|
|
|
fn gen_line(self: *Self) Err!void {
|
|
var stream = std.io.fixedBufferStream(&self.buf);
|
|
const writer = stream.writer();
|
|
try writer.print("this is line {d}\n", .{self.line_num});
|
|
self.line_buf = stream.getWritten();
|
|
self.read_count = 0;
|
|
self.line_num += 1;
|
|
}
|
|
|
|
fn read(self: *Self, buffer: []u8) Err!usize {
|
|
if (self.line_num > self.lines)
|
|
return 0;
|
|
if (self.line_buf.len == 0 or self.line_buf.len - self.read_count == 0)
|
|
try self.gen_line();
|
|
const read_count = self.read_count;
|
|
const bytes_to_read = @min(self.line_buf.len - read_count, buffer.len);
|
|
@memcpy(buffer[0..bytes_to_read], self.line_buf[read_count .. read_count + bytes_to_read]);
|
|
self.read_count += bytes_to_read;
|
|
return bytes_to_read;
|
|
}
|
|
|
|
fn reader(self: *Self) Reader {
|
|
return .{ .context = self };
|
|
}
|
|
};
|
|
var gen: BigDocGen = .{};
|
|
var doc = ArrayList(u8).init(a);
|
|
defer doc.deinit();
|
|
try gen.reader().readAllArrayList(&doc, std.math.maxInt(usize));
|
|
var buf = try Buffer.create(a);
|
|
var fis = std.io.fixedBufferStream(doc.items);
|
|
buf.update(try buf.load(fis.reader(), doc.items.len, eol_mode));
|
|
return buf;
|
|
}
|
|
|
|
test "buffer" {
|
|
const doc: []const u8 =
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
;
|
|
var eol_mode: Buffer.EolMode = .lf;
|
|
const buffer = try Buffer.create(a);
|
|
defer buffer.deinit();
|
|
const root = try buffer.load_from_string(doc, &eol_mode);
|
|
|
|
try std.testing.expect(root.is_balanced());
|
|
buffer.update(root);
|
|
|
|
const result: []const u8 = try buffer.store_to_string(a, eol_mode);
|
|
defer a.free(result);
|
|
try std.testing.expectEqualDeep(result, doc);
|
|
try std.testing.expectEqual(doc.len, result.len);
|
|
try std.testing.expectEqual(doc.len, buffer.root.length());
|
|
}
|
|
|
|
fn get_line(buf: *const Buffer, line: usize) ![]const u8 {
|
|
var result = ArrayList(u8).init(a);
|
|
try buf.root.get_line(line, &result, metrics());
|
|
return result.toOwnedSlice();
|
|
}
|
|
|
|
test "walk_from_line" {
|
|
var eol_mode: Buffer.EolMode = .lf;
|
|
const buffer = try get_big_doc(&eol_mode);
|
|
defer buffer.deinit();
|
|
|
|
const lines = buffer.root.lines();
|
|
try std.testing.expectEqual(lines, 10002);
|
|
|
|
const line0 = try get_line(buffer, 0);
|
|
defer a.free(line0);
|
|
try std.testing.expect(std.mem.eql(u8, line0, "this is line 0"));
|
|
|
|
const line1 = try get_line(buffer, 1);
|
|
defer a.free(line1);
|
|
try std.testing.expect(std.mem.eql(u8, line1, "this is line 1"));
|
|
|
|
const line100 = try get_line(buffer, 100);
|
|
defer a.free(line100);
|
|
try std.testing.expect(std.mem.eql(u8, line100, "this is line 100"));
|
|
|
|
const line9999 = try get_line(buffer, 9999);
|
|
defer a.free(line9999);
|
|
try std.testing.expect(std.mem.eql(u8, line9999, "this is line 9999"));
|
|
}
|
|
|
|
test "line_len" {
|
|
const doc: []const u8 =
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
;
|
|
var eol_mode: Buffer.EolMode = .lf;
|
|
const buffer = try Buffer.create(a);
|
|
defer buffer.deinit();
|
|
buffer.update(try buffer.load_from_string(doc, &eol_mode));
|
|
|
|
try std.testing.expectEqual(try buffer.root.line_width(0, metrics()), 8);
|
|
try std.testing.expectEqual(try buffer.root.line_width(1, metrics()), 5);
|
|
}
|
|
|
|
test "get_byte_pos" {
|
|
const doc: []const u8 =
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
;
|
|
var eol_mode: Buffer.EolMode = .lf;
|
|
const buffer = try Buffer.create(a);
|
|
defer buffer.deinit();
|
|
buffer.update(try buffer.load_from_string(doc, &eol_mode));
|
|
|
|
try std.testing.expectEqual(0, try buffer.root.get_byte_pos(.{ .row = 0, .col = 0 }, metrics(), eol_mode));
|
|
try std.testing.expectEqual(9, try buffer.root.get_byte_pos(.{ .row = 1, .col = 0 }, metrics(), eol_mode));
|
|
try std.testing.expectEqual(11, try buffer.root.get_byte_pos(.{ .row = 1, .col = 2 }, metrics(), eol_mode));
|
|
try std.testing.expectEqual(33, try buffer.root.get_byte_pos(.{ .row = 4, .col = 0 }, metrics(), eol_mode));
|
|
try std.testing.expectEqual(66, try buffer.root.get_byte_pos(.{ .row = 8, .col = 0 }, metrics(), eol_mode));
|
|
try std.testing.expectEqual(97, try buffer.root.get_byte_pos(.{ .row = 11, .col = 2 }, metrics(), eol_mode));
|
|
}
|
|
|
|
test "del_chars" {
|
|
const doc: []const u8 =
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
;
|
|
var eol_mode: Buffer.EolMode = .lf;
|
|
const buffer = try Buffer.create(a);
|
|
defer buffer.deinit();
|
|
buffer.update(try buffer.load_from_string(doc, &eol_mode));
|
|
|
|
buffer.update(try buffer.root.del_chars(3, try buffer.root.line_width(3, metrics()) - 1, 1, buffer.allocator, metrics()));
|
|
const line3 = try get_line(buffer, 3);
|
|
defer a.free(line3);
|
|
try std.testing.expect(std.mem.eql(u8, line3, "us"));
|
|
|
|
buffer.update(try buffer.root.del_chars(3, 0, 7, buffer.allocator, metrics()));
|
|
const line3_1 = try get_line(buffer, 3);
|
|
defer a.free(line3_1);
|
|
try std.testing.expect(std.mem.eql(u8, line3_1, "your"));
|
|
|
|
try std.testing.expect(buffer.root.is_balanced());
|
|
buffer.update(try buffer.root.rebalance(buffer.allocator, buffer.allocator));
|
|
try std.testing.expect(buffer.root.is_balanced());
|
|
|
|
buffer.update(try buffer.root.del_chars(0, try buffer.root.line_width(0, metrics()) - 1, 2, buffer.allocator, metrics()));
|
|
const line0 = try get_line(buffer, 0);
|
|
defer a.free(line0);
|
|
try std.testing.expect(std.mem.eql(u8, line0, "All youropes"));
|
|
}
|
|
|
|
fn check_line(buffer: *const Buffer, line_no: usize, expect: []const u8) !void {
|
|
const line = try get_line(buffer, line_no);
|
|
defer a.free(line);
|
|
try std.testing.expect(std.mem.eql(u8, line, expect));
|
|
}
|
|
|
|
test "del_chars2" {
|
|
const doc: []const u8 =
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
\\All your
|
|
\\ropes
|
|
\\are belong to
|
|
\\us!
|
|
;
|
|
var eol_mode: Buffer.EolMode = .lf;
|
|
const buffer = try Buffer.create(a);
|
|
defer buffer.deinit();
|
|
buffer.update(try buffer.load_from_string(doc, &eol_mode));
|
|
|
|
buffer.update(try buffer.root.del_chars(2, try buffer.root.line_width(2, metrics()) - 3, 6, buffer.allocator, metrics()));
|
|
|
|
try check_line(buffer, 2, "are belong!");
|
|
try check_line(buffer, 3, "All your");
|
|
try check_line(buffer, 4, "ropes");
|
|
}
|
|
|
|
test "insert_chars" {
|
|
const doc: []const u8 =
|
|
\\B
|
|
;
|
|
var eol_mode: Buffer.EolMode = .lf;
|
|
const buffer = try Buffer.create(a);
|
|
defer buffer.deinit();
|
|
buffer.update(try buffer.load_from_string(doc, &eol_mode));
|
|
|
|
const line0 = try get_line(buffer, 0);
|
|
defer a.free(line0);
|
|
try std.testing.expect(std.mem.eql(u8, line0, "B"));
|
|
|
|
_, _, var root = try buffer.root.insert_chars(0, 0, "1", buffer.allocator, metrics());
|
|
buffer.update(root);
|
|
|
|
const line1 = try get_line(buffer, 0);
|
|
defer a.free(line1);
|
|
try std.testing.expect(std.mem.eql(u8, line1, "1B"));
|
|
|
|
_, _, root = try root.insert_chars(0, 1, "2", buffer.allocator, metrics());
|
|
buffer.update(root);
|
|
|
|
const line2 = try get_line(buffer, 0);
|
|
defer a.free(line2);
|
|
try std.testing.expect(std.mem.eql(u8, line2, "12B"));
|
|
|
|
_, _, root = try root.insert_chars(0, 2, "3", buffer.allocator, metrics());
|
|
buffer.update(root);
|
|
|
|
const line3 = try get_line(buffer, 0);
|
|
defer a.free(line3);
|
|
try std.testing.expect(std.mem.eql(u8, line3, "123B"));
|
|
|
|
_, _, root = try root.insert_chars(0, 3, "4", buffer.allocator, metrics());
|
|
buffer.update(root);
|
|
|
|
const line4 = try get_line(buffer, 0);
|
|
defer a.free(line4);
|
|
try std.testing.expect(std.mem.eql(u8, line4, "1234B"));
|
|
|
|
_, _, root = try root.insert_chars(0, 4, "5", buffer.allocator, metrics());
|
|
buffer.update(root);
|
|
|
|
const line5 = try get_line(buffer, 0);
|
|
defer a.free(line5);
|
|
try std.testing.expect(std.mem.eql(u8, line5, "12345B"));
|
|
|
|
_, _, root = try root.insert_chars(0, 5, "6", buffer.allocator, metrics());
|
|
buffer.update(root);
|
|
|
|
const line6 = try get_line(buffer, 0);
|
|
defer a.free(line6);
|
|
try std.testing.expect(std.mem.eql(u8, line6, "123456B"));
|
|
|
|
_, _, root = try root.insert_chars(0, 6, "7", buffer.allocator, metrics());
|
|
buffer.update(root);
|
|
|
|
const line7 = try get_line(buffer, 0);
|
|
defer a.free(line7);
|
|
try std.testing.expect(std.mem.eql(u8, line7, "1234567B"));
|
|
|
|
const line, const col, root = try buffer.root.insert_chars(0, 7, "8\n9", buffer.allocator, metrics());
|
|
buffer.update(root);
|
|
|
|
const line8 = try get_line(buffer, 0);
|
|
defer a.free(line8);
|
|
const line9 = try get_line(buffer, 1);
|
|
defer a.free(line9);
|
|
try std.testing.expect(std.mem.eql(u8, line8, "12345678"));
|
|
try std.testing.expect(std.mem.eql(u8, line9, "9B"));
|
|
try std.testing.expectEqual(line, 1);
|
|
try std.testing.expectEqual(col, 1);
|
|
}
|
|
|
|
test "get_from_pos" {
|
|
var eol_mode: Buffer.EolMode = .lf;
|
|
const buffer = try get_big_doc(&eol_mode);
|
|
defer buffer.deinit();
|
|
|
|
const lines = buffer.root.lines();
|
|
try std.testing.expectEqual(lines, 10002);
|
|
|
|
const line0 = try get_line(buffer, 0);
|
|
defer a.free(line0);
|
|
const line1 = try get_line(buffer, 1);
|
|
defer a.free(line1);
|
|
|
|
var result_buf: [1024]u8 = undefined;
|
|
const result1 = buffer.root.get_from_pos(.{ .row = 0, .col = 0 }, &result_buf, metrics());
|
|
try std.testing.expectEqualDeep(result1[0..line0.len], line0);
|
|
|
|
const result2 = buffer.root.get_from_pos(.{ .row = 1, .col = 5 }, &result_buf, metrics());
|
|
try std.testing.expectEqualDeep(result2[0 .. line1.len - 5], line1[5..]);
|
|
|
|
_, _, const root = try buffer.root.insert_chars(1, 3, " ", buffer.allocator, metrics());
|
|
buffer.update(root);
|
|
|
|
const result3 = buffer.root.get_from_pos(.{ .row = 1, .col = 5 }, &result_buf, metrics());
|
|
try std.testing.expectEqualDeep(result3[0 .. line1.len - 4], line1[4..]);
|
|
}
|