Compare commits

...

8 commits

4 changed files with 138 additions and 183 deletions

View file

@ -333,21 +333,21 @@ const Node = union(enum) {
};
}
fn collect(self: *const Node, l: *ArrayList(*const Node)) !void {
fn collect(self: *const Node, allocator: Allocator, l: *ArrayList(*const Node)) !void {
switch (self.*) {
.node => |*node| {
try node.left.collect(l);
try node.right.collect(l);
try node.left.collect(allocator, l);
try node.right.collect(allocator, l);
},
.leaf => (try l.addOne()).* = self,
.leaf => (try l.addOne(allocator)).* = self,
}
}
fn collect_leaves(self: *const Node, allocator: Allocator) ![]*const Node {
var leaves = ArrayList(*const Node).init(allocator);
try leaves.ensureTotalCapacity(self.lines());
try self.collect(&leaves);
return leaves.toOwnedSlice();
var leaves: ArrayList(*const Node) = .empty;
try leaves.ensureTotalCapacity(allocator, self.lines());
try self.collect(allocator, &leaves);
return leaves.toOwnedSlice(allocator);
}
fn walk_const(self: *const Node, f: Walker.F, ctx: *anyopaque, metrics: Metrics) Walker {
@ -751,12 +751,12 @@ const Node = union(enum) {
return Node.new(allocator, try merge_in_place(leaves[0..mid], allocator), try merge_in_place(leaves[mid..], allocator));
}
pub fn get_line(self: *const Node, line: usize, result: *ArrayList(u8), metrics: Metrics) !void {
pub fn get_line(self: *const Node, line: usize, result: *std.Io.Writer, metrics: Metrics) !void {
const Ctx = struct {
line: *ArrayList(u8),
line: *std.Io.Writer,
fn walker(ctx_: *anyopaque, leaf: *const Leaf, _: Metrics) Walker {
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
ctx.line.appendSlice(leaf.buf) catch |e| return .{ .err = e };
ctx.line.writeAll(leaf.buf) catch |e| return .{ .err = e };
return if (!leaf.eol) Walker.keep_walking else Walker.stop;
}
};
@ -1039,53 +1039,34 @@ const Node = union(enum) {
};
}
pub fn get_byte_pos(self: *const Node, pos_: Cursor, metrics_: Metrics, eol_mode: EolMode) !usize {
const Ctx = struct {
pub fn get_byte_pos(self: *const Node, pos_: Cursor, metrics: Metrics, eol_mode: EolMode) !usize {
const ctx_ = struct {
pos: usize = 0,
line: usize = 0,
abs_col: usize = 0,
pos: Cursor,
byte_pos: usize = 0,
metrics: Metrics,
const Ctx = @This();
const Writer = std.io.Writer(*Ctx, error{Stop}, write);
fn write(ctx: *Ctx, bytes: []const u8) error{Stop}!usize {
if (ctx.line >= ctx.pos.row) {
return ctx.get_col_bytes(bytes, bytes.len);
} else for (bytes, 1..) |char, i| {
ctx.byte_pos += 1;
if (char == '\n') {
col: usize = 0,
target_line: usize,
target_col: usize,
eol_mode: EolMode,
fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Metrics) Walker {
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
if (ctx.line == ctx.target_line and ctx.col == ctx.target_col) return Walker.stop;
if (egc[0] == '\n') {
ctx.pos += switch (ctx.eol_mode) {
.lf => 1,
.crlf => 2,
};
ctx.line += 1;
if (ctx.line >= ctx.pos.row)
return ctx.get_col_bytes(bytes[i..], bytes.len);
ctx.col = 0;
} else {
ctx.pos += egc.len;
ctx.col += wcwidth;
}
}
return bytes.len;
}
fn get_col_bytes(ctx: *Ctx, bytes: []const u8, result: usize) error{Stop}!usize {
var buf: []const u8 = bytes;
while (buf.len > 0) {
if (ctx.abs_col >= ctx.pos.col) return error.Stop;
if (buf[0] == '\n') return error.Stop;
var cols: c_int = undefined;
const egc_bytes = ctx.metrics.egc_length(ctx.metrics, buf, &cols, ctx.abs_col);
ctx.abs_col += @intCast(cols);
ctx.byte_pos += egc_bytes;
buf = buf[egc_bytes..];
}
return result;
}
fn writer(ctx: *Ctx) Writer {
return .{ .context = ctx };
return Walker.keep_walking;
}
};
var ctx: Ctx = .{
.pos = pos_,
.metrics = metrics_,
};
self.store(ctx.writer(), eol_mode) catch |e| switch (e) {
error.Stop => return ctx.byte_pos,
};
return error.NotFound;
var ctx: ctx_ = .{ .target_line = pos_.row, .target_col = pos_.col, .eol_mode = eol_mode };
self.walk_egc_forward(0, ctx_.walker, &ctx, metrics) catch {};
return ctx.pos;
}
pub fn debug_render_chunks(self: *const Node, allocator: std.mem.Allocator, line: usize, metrics_: Metrics) ![]const u8 {
@ -1119,6 +1100,49 @@ const Node = union(enum) {
break :blk l.toOwnedSlice();
} else error.NotFound;
}
pub fn write_range(
self: *const Node,
sel: Selection,
writer: *std.Io.Writer,
wcwidth_: ?*usize,
metrics: Metrics,
) std.Io.Writer.Error!void {
const Ctx = struct {
col: usize = 0,
sel: Selection,
writer: *std.Io.Writer,
wcwidth: usize = 0,
fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Metrics) Walker {
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
if (ctx.col < ctx.sel.begin.col) {
ctx.col += wcwidth;
return Walker.keep_walking;
}
_ = ctx.writer.write(egc) catch |e| return Walker{ .err = e };
ctx.wcwidth += wcwidth;
if (egc[0] == '\n') {
ctx.col = 0;
ctx.sel.begin.col = 0;
ctx.sel.begin.row += 1;
} else {
ctx.col += wcwidth;
ctx.sel.begin.col += wcwidth;
}
return if (ctx.sel.begin.eql(ctx.sel.end))
Walker.stop
else
Walker.keep_walking;
}
};
var ctx: Ctx = .{ .sel = sel, .writer = writer };
ctx.sel.normalize();
if (sel.begin.eql(sel.end))
return;
self.walk_egc_forward(sel.begin.row, Ctx.walker, &ctx, metrics) catch return error.WriteFailed;
if (wcwidth_) |p| p.* = ctx.wcwidth;
}
};
pub fn create(allocator: Allocator) error{OutOfMemory}!*Self {
@ -1346,8 +1370,8 @@ pub fn refresh_from_file(self: *Self) LoadFromFileError!void {
}
pub fn store_to_string(self: *const Self, allocator: Allocator, eol_mode: EolMode) ![]u8 {
var s = try ArrayList(u8).initCapacity(allocator, self.root.weights_sum().len);
try self.root.store(s.writer(), eol_mode);
var s: std.Io.Writer.Allocating = try .initCapacity(allocator, self.root.weights_sum().len);
try self.root.store(&s.writer, eol_mode);
return s.toOwnedSlice();
}

View file

@ -804,7 +804,7 @@ test "parse" {
const actual: []const KeyEvent = parsed;
try expectEqual(expected.len, actual.len);
for (expected, 0..) |expected_event, i| {
try std.testing.expectFmt(expected_event, "{}", .{actual[i]});
try std.testing.expectFmt(expected_event, "{f}", .{actual[i]});
}
}
}

View file

@ -820,10 +820,10 @@ pub const Editor = struct {
fn update_buf_and_eol_mode(self: *Self, root: Buffer.Root, eol_mode: Buffer.EolMode, utf8_sanitized: bool) !void {
const b = self.buffer orelse return error.Stop;
var sfa = std.heap.stackFallback(512, self.allocator);
const allocator = sfa.get();
const sfa_allocator = sfa.get();
if (!self.pause_undo) {
const meta = try self.store_undo_meta(allocator);
defer allocator.free(meta);
const meta = try self.store_undo_meta(sfa_allocator);
defer sfa_allocator.free(meta);
try b.store_undo(meta);
}
b.update(root);
@ -850,9 +850,9 @@ pub const Editor = struct {
try self.send_editor_jump_source();
self.cancel_all_matches();
var sfa = std.heap.stackFallback(512, self.allocator);
const allocator = sfa.get();
const redo_metadata = try self.store_current_undo_meta(allocator);
defer allocator.free(redo_metadata);
const sfa_allocator = sfa.get();
const redo_metadata = try self.store_current_undo_meta(sfa_allocator);
defer sfa_allocator.free(redo_metadata);
const meta = b_mut.undo(redo_metadata) catch |e| switch (e) {
error.Stop => {
self.logger.print("nothing to undo", .{});
@ -893,9 +893,9 @@ pub const Editor = struct {
self.pause_undo = false;
const b = self.buffer orelse return;
var sfa = std.heap.stackFallback(512, self.allocator);
const allocator = sfa.get();
const meta = try self.store_undo_meta(allocator);
defer allocator.free(meta);
const sfa_allocator = sfa.get();
const meta = try self.store_undo_meta(sfa_allocator);
defer sfa_allocator.free(meta);
const root = self.buf_root() catch return;
if (self.pause_undo_root) |paused_root| b.update(paused_root);
try b.store_undo(meta);
@ -947,49 +947,6 @@ pub const Editor = struct {
return ctx.col;
}
fn write_range(
self: *const Self,
root: Buffer.Root,
sel: Selection,
writer: *std.Io.Writer,
wcwidth_: ?*usize,
) std.Io.Writer.Error!void {
const Ctx = struct {
col: usize = 0,
sel: Selection,
writer: *std.Io.Writer,
wcwidth: usize = 0,
fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Buffer.Metrics) Buffer.Walker {
const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_)));
if (ctx.col < ctx.sel.begin.col) {
ctx.col += wcwidth;
return Buffer.Walker.keep_walking;
}
_ = ctx.writer.write(egc) catch |e| return Buffer.Walker{ .err = e };
ctx.wcwidth += wcwidth;
if (egc[0] == '\n') {
ctx.col = 0;
ctx.sel.begin.col = 0;
ctx.sel.begin.row += 1;
} else {
ctx.col += wcwidth;
ctx.sel.begin.col += wcwidth;
}
return if (ctx.sel.begin.eql(ctx.sel.end))
Buffer.Walker.stop
else
Buffer.Walker.keep_walking;
}
};
var ctx: Ctx = .{ .sel = sel, .writer = writer };
ctx.sel.normalize();
if (sel.begin.eql(sel.end))
return;
root.walk_egc_forward(sel.begin.row, Ctx.walker, &ctx, self.metrics) catch return error.WriteFailed;
if (wcwidth_) |p| p.* = ctx.wcwidth;
}
pub fn update(self: *Self) void {
self.update_scroll();
self.update_event() catch {};
@ -3617,8 +3574,9 @@ pub const Editor = struct {
const saved = cursel.*;
const sel = cursel.expand_selection_to_line(root, self.metrics) catch return error.Stop;
var sfa = std.heap.stackFallback(4096, self.allocator);
const cut_text = copy_selection(root, sel.*, sfa.get(), self.metrics) catch return error.Stop;
defer allocator.free(cut_text);
const sfa_allocator = sfa.get();
const cut_text = copy_selection(root, sel.*, sfa_allocator, self.metrics) catch return error.Stop;
defer sfa_allocator.free(cut_text);
root = try self.delete_selection(root, cursel, allocator);
try cursel.cursor.move_up(root, self.metrics);
root = self.insert(root, cursel, cut_text, allocator) catch return error.Stop;
@ -3644,8 +3602,9 @@ pub const Editor = struct {
const saved = cursel.*;
const sel = cursel.expand_selection_to_line(root, self.metrics) catch return error.Stop;
var sfa = std.heap.stackFallback(4096, self.allocator);
const cut_text = copy_selection(root, sel.*, sfa.get(), self.metrics) catch return error.Stop;
defer allocator.free(cut_text);
const sfa_allocator = sfa.get();
const cut_text = copy_selection(root, sel.*, sfa_allocator, self.metrics) catch return error.Stop;
defer sfa_allocator.free(cut_text);
root = try self.delete_selection(root, cursel, allocator);
try cursel.cursor.move_down(root, self.metrics);
root = self.insert(root, cursel, cut_text, allocator) catch return error.Stop;
@ -3671,8 +3630,9 @@ pub const Editor = struct {
const sel: Selection = if (cursel.selection) |sel_| sel_ else Selection.line_from_cursor(cursel.cursor, root, self.metrics);
cursel.disable_selection(root, self.metrics);
var sfa = std.heap.stackFallback(4096, self.allocator);
const text = copy_selection(root, sel, sfa.get(), self.metrics) catch return error.Stop;
defer allocator.free(text);
const sfa_allocator = sfa.get();
const text = copy_selection(root, sel, sfa_allocator, self.metrics) catch return error.Stop;
defer sfa_allocator.free(text);
cursel.cursor = sel.begin;
root = self.insert(root, cursel, text, allocator) catch return error.Stop;
cursel.selection = .{ .begin = sel.begin, .end = sel.end };
@ -3693,8 +3653,9 @@ pub const Editor = struct {
const sel: Selection = if (cursel.selection) |sel_| sel_ else Selection.line_from_cursor(cursel.cursor, root, self.metrics);
cursel.disable_selection(root, self.metrics);
var sfa = std.heap.stackFallback(4096, self.allocator);
const text = copy_selection(root, sel, sfa.get(), self.metrics) catch return error.Stop;
defer allocator.free(text);
const sfa_allocator = sfa.get();
const text = copy_selection(root, sel, sfa_allocator, self.metrics) catch return error.Stop;
defer sfa_allocator.free(text);
cursel.cursor = sel.end;
root = self.insert(root, cursel, text, allocator) catch return error.Stop;
cursel.selection = .{ .begin = sel.end, .end = cursel.cursor };
@ -3714,11 +3675,12 @@ pub const Editor = struct {
const saved = cursel.*;
const sel = cursel.expand_selection_to_line(root, self.metrics) catch return error.Stop;
var sfa = std.heap.stackFallback(4096, self.allocator);
const alloc = sfa.get();
const text = copy_selection(root, sel.*, alloc, self.metrics) catch return error.Stop;
defer allocator.free(text);
const sfa_allocator = sfa.get();
const text = copy_selection(root, sel.*, sfa_allocator, self.metrics) catch return error.Stop;
defer sfa_allocator.free(text);
root = try self.delete_selection(root, cursel, allocator);
const new_text = text_manip.toggle_prefix_in_text(self.prefix, text, alloc) catch return error.Stop;
const new_text = text_manip.toggle_prefix_in_text(self.prefix, text, sfa_allocator) catch return error.Stop;
defer sfa_allocator.free(new_text);
root = self.insert(root, cursel, new_text, allocator) catch return error.Stop;
cursel.* = saved;
cursel.cursor.clamp_to_buffer(root, self.metrics);
@ -4514,8 +4476,8 @@ pub const Editor = struct {
const row = cursel.cursor.row;
const leading_ws = @min(find_first_non_ws(root, row, self.metrics), cursel.cursor.col);
var sfa = std.heap.stackFallback(512, self.allocator);
const allocator = sfa.get();
var stream: std.Io.Writer.Allocating = .init(allocator);
const sfa_allocator = sfa.get();
var stream: std.Io.Writer.Allocating = .init(sfa_allocator);
defer stream.deinit();
const writer = &stream.writer;
_ = try writer.write("\n");
@ -4591,8 +4553,8 @@ pub const Editor = struct {
const row = cursel.cursor.row;
try move_cursor_left(root, &cursel.cursor, self.metrics);
var sfa = std.heap.stackFallback(512, self.allocator);
const allocator = sfa.get();
var stream: std.Io.Writer.Allocating = .init(allocator);
const sfa_allocator = sfa.get();
var stream: std.Io.Writer.Allocating = .init(sfa_allocator);
defer stream.deinit();
try self.generate_leading_ws(&stream.writer, leading_ws);
if (stream.written().len > 0)
@ -4624,8 +4586,8 @@ pub const Editor = struct {
const row = cursel.cursor.row;
try move_cursor_end(root, &cursel.cursor, self.metrics);
var sfa = std.heap.stackFallback(512, self.allocator);
const allocator = sfa.get();
var stream: std.Io.Writer.Allocating = .init(allocator);
const sfa_allocator = sfa.get();
var stream: std.Io.Writer.Allocating = .init(sfa_allocator);
defer stream.deinit();
try stream.writer.writeAll("\n");
try self.generate_leading_ws(&stream.writer, leading_ws);
@ -5840,7 +5802,7 @@ pub const Editor = struct {
}
var sp_buf: [tp.subprocess.max_chunk_size]u8 = undefined;
var writer = sp.writer(&sp_buf);
try self.write_range(state.before_root, sel, &writer.interface, null);
try state.before_root.write_range(sel, &writer.interface, null, self.metrics);
try writer.interface.flush();
self.logger.print("filter: sent", .{});
state.work_root = try state.work_root.delete_range(sel, buf_a_, null, self.metrics);
@ -5930,10 +5892,11 @@ pub const Editor = struct {
break :ret sel;
};
var sfa = std.heap.stackFallback(4096, self.allocator);
const cut_text = copy_selection(root, sel.*, sfa.get(), self.metrics) catch return error.Stop;
defer allocator.free(cut_text);
const ucased = Buffer.unicode.get_letter_casing().toUpperStr(allocator, cut_text) catch return error.Stop;
defer allocator.free(ucased);
const sfa_allocator = sfa.get();
const cut_text = copy_selection(root, sel.*, sfa_allocator, self.metrics) catch return error.Stop;
defer sfa_allocator.free(cut_text);
const ucased = Buffer.unicode.get_letter_casing().toUpperStr(sfa_allocator, cut_text) catch return error.Stop;
defer sfa_allocator.free(ucased);
root = try self.delete_selection(root, cursel, allocator);
root = self.insert(root, cursel, ucased, allocator) catch return error.Stop;
cursel.* = saved;
@ -5958,10 +5921,11 @@ pub const Editor = struct {
break :ret sel;
};
var sfa = std.heap.stackFallback(4096, self.allocator);
const cut_text = copy_selection(root, sel.*, sfa.get(), self.metrics) catch return error.Stop;
defer allocator.free(cut_text);
const ucased = Buffer.unicode.get_letter_casing().toLowerStr(allocator, cut_text) catch return error.Stop;
defer allocator.free(ucased);
const sfa_allocator = sfa.get();
const cut_text = copy_selection(root, sel.*, sfa_allocator, self.metrics) catch return error.Stop;
defer sfa_allocator.free(cut_text);
const ucased = Buffer.unicode.get_letter_casing().toLowerStr(sfa_allocator, cut_text) catch return error.Stop;
defer sfa_allocator.free(ucased);
root = try self.delete_selection(root, cursel, allocator);
root = self.insert(root, cursel, ucased, allocator) catch return error.Stop;
cursel.* = saved;
@ -5987,7 +5951,7 @@ pub const Editor = struct {
};
var range: std.Io.Writer.Allocating = .init(self.allocator);
defer range.deinit();
self.write_range(root, sel.*, &range.writer, null) catch return error.Stop;
root.write_range(sel.*, &range.writer, null, self.metrics) catch return error.Stop;
const bytes = range.written();
const letter_casing = Buffer.unicode.get_letter_casing();

View file

@ -28,51 +28,18 @@ fn metrics() Buffer.Metrics {
}
fn get_big_doc(eol_mode: *Buffer.EolMode) !*Buffer {
const BigDocGen = struct {
line_num: usize = 0,
lines: usize = 10000,
const nl_lines = 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);
var doc: std.Io.Writer.Allocating = .init(a);
defer doc.deinit();
try gen.reader().readAllArrayList(&doc, std.math.maxInt(usize));
for (0..nl_lines) |line_num| {
try doc.writer.print("this is line {d}\n", .{line_num});
}
var buf = try Buffer.create(a);
var fis = std.io.fixedBufferStream(doc.items);
var sanitized: bool = false;
buf.update(try buf.load(fis.reader(), doc.items.len, eol_mode, &sanitized));
buf.update(try buf.load_from_string(doc.written(), eol_mode, &sanitized));
return buf;
}
@ -108,8 +75,8 @@ test "buffer" {
}
fn get_line(buf: *const Buffer, line: usize) ![]const u8 {
var result = ArrayList(u8).init(a);
try buf.root.get_line(line, &result, metrics());
var result: std.Io.Writer.Allocating = .init(a);
try buf.root.get_line(line, &result.writer, metrics());
return result.toOwnedSlice();
}
@ -119,7 +86,7 @@ test "walk_from_line" {
defer buffer.deinit();
const lines = buffer.root.lines();
try std.testing.expectEqual(lines, 10002);
try std.testing.expectEqual(lines, 10001);
const line0 = try get_line(buffer, 0);
defer a.free(line0);
@ -135,7 +102,7 @@ test "walk_from_line" {
const line9999 = try get_line(buffer, 9999);
defer a.free(line9999);
try std.testing.expect(std.mem.eql(u8, line9999, "this is line 9999"));
try std.testing.expectEqualDeep("this is line 9999", line9999);
}
test "line_len" {
@ -394,7 +361,7 @@ test "get_from_pos" {
defer buffer.deinit();
const lines = buffer.root.lines();
try std.testing.expectEqual(lines, 10002);
try std.testing.expectEqual(lines, 10001);
const line0 = try get_line(buffer, 0);
defer a.free(line0);