refactor: add Cursor.from_pos and Seleciton.from_range

And use them to clean-up all root.pos_to_width call sites.
This commit is contained in:
CJ van den Berg 2025-11-25 14:37:05 +01:00
parent be41027d75
commit 4a0150d68f
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
3 changed files with 80 additions and 68 deletions

View file

@ -42,6 +42,13 @@ fn follow_target(self: *Self, root: Buffer.Root, metrics: Metrics) void {
self.col = @min(self.target, root.line_width(self.row, metrics) catch 0); self.col = @min(self.target, root.line_width(self.row, metrics) catch 0);
} }
pub fn from_pos(self: Self, root: Buffer.Root, metrics: Buffer.Metrics) Self {
return .{
.row = self.row,
.col = root.pos_to_width(self.row, self.col, metrics) catch root.line_width(self.row, metrics) catch 0,
};
}
fn move_right_no_target(self: *Self, root: Buffer.Root, metrics: Metrics) !void { fn move_right_no_target(self: *Self, root: Buffer.Root, metrics: Metrics) !void {
const lines = root.lines(); const lines = root.lines();
if (lines <= self.row) return error.Stop; if (lines <= self.row) return error.Stop;

View file

@ -32,6 +32,17 @@ pub fn from_pos(sel: Self, root: Buffer.Root, metrics: Buffer.Metrics) Self {
}; };
} }
pub fn from_range(range: anytype, root: Buffer.Root, metrics: Buffer.Metrics) Self {
return from_pos(from_range_raw(range), root, metrics);
}
pub fn from_range_raw(range: anytype) Self {
return .{
.begin = .{ .row = range.start_point.row, .col = range.start_point.column },
.end = .{ .row = range.end_point.row, .col = range.end_point.column },
};
}
pub fn line_from_cursor(cursor: Cursor, root: Buffer.Root, mtrx: Buffer.Metrics) Self { pub fn line_from_cursor(cursor: Cursor, root: Buffer.Root, mtrx: Buffer.Metrics) Self {
var begin = cursor; var begin = cursor;
var end = cursor; var end = cursor;

View file

@ -84,8 +84,8 @@ pub const Match = struct {
return .{ .begin = self.begin, .end = self.end }; return .{ .begin = self.begin, .end = self.end };
} }
pub fn from_pos(self: Self, root: Buffer.Root, metrics: Buffer.Metrics) error{NotFound}!Self { pub fn from_pos(self: Self, root: Buffer.Root, metrics: Buffer.Metrics) Self {
return from_selection(try self.to_selection().from_pos(root, metrics)); return from_selection(self.to_selection().from_pos(root, metrics));
} }
fn nudge_insert(self: *Self, nudge: Selection) void { fn nudge_insert(self: *Self, nudge: Selection) void {
@ -1442,7 +1442,7 @@ pub const Editor = struct {
pos_cache: PosToWidthCache, pos_cache: PosToWidthCache,
last_begin: Cursor = Cursor.invalid(), last_begin: Cursor = Cursor.invalid(),
fn cb(ctx: *@This(), range: syntax.Range, scope: []const u8, id: u32, idx: usize, _: *const syntax.Node) error{Stop}!void { fn cb(ctx: *@This(), range: syntax.Range, scope: []const u8, id: u32, idx: usize, _: *const syntax.Node) error{Stop}!void {
const sel_ = ctx.pos_cache.range_to_selection(range, ctx.root, ctx.self.metrics) orelse return; const sel_ = ctx.pos_cache.range_to_selection(range, ctx.root, ctx.self.metrics);
if (idx > 0) return; if (idx > 0) return;
if (sel_.begin.eql(ctx.last_begin)) return; if (sel_.begin.eql(ctx.last_begin)) return;
@ -5420,13 +5420,14 @@ pub const Editor = struct {
self.need_render(); self.need_render();
} }
fn add_match_internal(self: *Self, begin_line_: usize, begin_pos_: usize, end_line_: usize, end_pos_: usize) void { fn add_match_internal(self: *Self, begin_line: usize, begin_pos: usize, end_line: usize, end_pos: usize) void {
const root = self.buf_root() catch return; const root = self.buf_root() catch return;
const begin_line = begin_line_ - 1; const begin: Cursor = .{ .row = begin_line - 1, .col = begin_pos };
const end_line = end_line_ - 1; const end: Cursor = .{ .row = end_line - 1, .col = end_pos };
const begin_pos = root.pos_to_width(begin_line, begin_pos_, self.metrics) catch return; var match: Match = .{
const end_pos = root.pos_to_width(end_line, end_pos_, self.metrics) catch return; .begin = begin.from_pos(root, self.metrics),
var match: Match = .{ .begin = .{ .row = begin_line, .col = begin_pos }, .end = .{ .row = end_line, .col = end_pos } }; .end = end.from_pos(root, self.metrics),
};
if (match.end.eql(self.get_primary().cursor)) if (match.end.eql(self.get_primary().cursor))
match.has_selection = true; match.has_selection = true;
(self.matches.addOne(self.allocator) catch return).* = match; (self.matches.addOne(self.allocator) catch return).* = match;
@ -5631,72 +5632,73 @@ pub const Editor = struct {
pub const goto_line_vim_meta: Meta = .{ .arguments = &.{.integer} }; pub const goto_line_vim_meta: Meta = .{ .arguments = &.{.integer} };
pub fn goto_column(self: *Self, ctx: Context) Result { pub fn goto_column(self: *Self, ctx: Context) Result {
var column: usize = 0;
if (!try ctx.args.match(.{tp.extract(&column)}))
return error.InvalidGotoColumnArgument;
const root = self.buf_root() catch return; const root = self.buf_root() catch return;
const primary = self.get_primary(); const primary = self.get_primary();
column = if (column < 1) 0 else column - 1; var dest = primary.cursor;
column = try root.pos_to_width(primary.cursor.row, column, self.metrics); if (!try ctx.args.match(.{tp.extract(&dest.col)}))
try primary.cursor.move_to(root, primary.cursor.row, column, self.metrics); return error.InvalidGotoColumnArgument;
dest.col -|= 1;
dest = dest.from_pos(root, self.metrics);
try primary.cursor.move_to(root, dest.row, dest.col, self.metrics);
self.clamp(); self.clamp();
} }
pub const goto_column_meta: Meta = .{ .arguments = &.{.integer} }; pub const goto_column_meta: Meta = .{ .arguments = &.{.integer} };
pub fn goto_line_and_column(self: *Self, ctx: Context) Result { pub fn goto_line_and_column(self: *Self, ctx: Context) Result {
try self.send_editor_jump_source(); try self.send_editor_jump_source();
var line: usize = 0; var dest: Cursor = .{};
var column: usize = 0;
var have_sel: bool = false; var have_sel: bool = false;
var sel: Selection = .{}; var sel: Selection = .{};
var pos_type: PosType = .column; var pos_type: PosType = .column;
if (try ctx.args.match(.{ if (try ctx.args.match(.{
tp.extract(&line), tp.extract(&dest.row),
tp.extract(&column), tp.extract(&dest.col),
})) { })) {
// self.logger.print("goto: l:{d} c:{d}", .{ line, column }); // self.logger.print("goto: l:{d} c:{d}", .{ dest.row, dest.col });
} else if (try ctx.args.match(.{ } else if (try ctx.args.match(.{
tp.extract(&line), tp.extract(&dest.row),
tp.extract(&column), tp.extract(&dest.col),
tp.extract(&pos_type), tp.extract(&pos_type),
})) { })) {
// self.logger.print("goto: l:{d} c:{d}", .{ line, column }); // self.logger.print("goto: l:{d} c:{d}", .{ dest.row, dest.col });
} else if (try ctx.args.match(.{ } else if (try ctx.args.match(.{
tp.extract(&line), tp.extract(&dest.row),
tp.extract(&column), tp.extract(&dest.col),
tp.extract(&sel.begin.row), tp.extract(&sel.begin.row),
tp.extract(&sel.begin.col), tp.extract(&sel.begin.col),
tp.extract(&sel.end.row), tp.extract(&sel.end.row),
tp.extract(&sel.end.col), tp.extract(&sel.end.col),
})) { })) {
// self.logger.print("goto: l:{d} c:{d} {any}", .{ line, column, sel }); // self.logger.print("goto: l:{d} c:{d} {any}", .{ dest.row, dest.col, sel });
have_sel = true; have_sel = true;
} else if (try ctx.args.match(.{ } else if (try ctx.args.match(.{
tp.extract(&line), tp.extract(&dest.row),
tp.extract(&column), tp.extract(&dest.col),
tp.extract(&sel.begin.row), tp.extract(&sel.begin.row),
tp.extract(&sel.begin.col), tp.extract(&sel.begin.col),
tp.extract(&sel.end.row), tp.extract(&sel.end.row),
tp.extract(&sel.end.col), tp.extract(&sel.end.col),
tp.extract(&pos_type), tp.extract(&pos_type),
})) { })) {
// self.logger.print("goto: l:{d} c:{d} {any} {}", .{ line, column, sel, pos_type }); // self.logger.print("goto: l:{d} c:{d} {any} {}", .{ dest.row, dest.col, sel, pos_type });
have_sel = true; have_sel = true;
} else return error.InvalidGotoLineAndColumnArgument; } else return error.InvalidGotoLineAndColumnArgument;
self.cancel_all_selections(); self.cancel_all_selections();
const root = self.buf_root() catch return; const root = self.buf_root() catch return;
if (pos_type == .byte) { if (pos_type == .byte) {
column = root.pos_to_width(line - 1, column - 1, self.metrics) catch return; dest.row -|= 1;
column += 1; dest.col -|= 1;
dest = dest.from_pos(root, self.metrics);
dest.col += 1;
if (have_sel) if (have_sel)
sel = sel.from_pos(root, self.metrics) catch return; sel = sel.from_pos(root, self.metrics);
// self.logger.print("goto_byte_pos: l:{d} c:{d} {any} {}", .{ line, column, sel, pos_type }); // self.logger.print("goto_byte_pos: l:{d} c:{d} {any} {}", .{ dest.row, dest.col, sel, pos_type });
} }
const primary = self.get_primary(); const primary = self.get_primary();
try primary.cursor.move_to( try primary.cursor.move_to(
root, root,
@intCast(if (line < 1) 0 else line - 1), dest.row -| 1,
@intCast(if (column < 1) 0 else column - 1), dest.col -| 1,
self.metrics, self.metrics,
); );
if (have_sel) primary.selection = sel; if (have_sel) primary.selection = sel;
@ -5918,23 +5920,10 @@ pub const Editor = struct {
pub fn add_hover_highlight(self: *Self, match_: Match) void { pub fn add_hover_highlight(self: *Self, match_: Match) void {
const root = self.buf_root() catch return; const root = self.buf_root() catch return;
const match: Match = .{ const match = match_.from_pos(root, self.metrics);
.begin = .{
.row = match_.begin.row,
.col = root.pos_to_width(match_.begin.row, match_.begin.col, self.metrics) catch return,
},
.end = .{
.row = match_.end.row,
.col = root.pos_to_width(match_.end.row, match_.end.col, self.metrics) catch return,
},
};
switch (self.matches.items.len) { switch (self.matches.items.len) {
0 => { 0 => (self.matches.addOne(self.allocator) catch return).* = match,
(self.matches.addOne(self.allocator) catch return).* = match; 1 => self.matches.items[0] = match,
},
1 => {
self.matches.items[0] = match;
},
else => {}, else => {},
} }
self.need_render(); self.need_render();
@ -5954,14 +5943,11 @@ pub const Editor = struct {
self.cancel_all_matches(); self.cancel_all_matches();
} }
const root = self.buf_root() catch return; const root = self.buf_root() catch return;
const begin_row = match_.begin.row - @min(match_.begin.row, 1); var match = match_;
const begin_col = root.pos_to_width(begin_row, match_.begin.col, self.metrics) catch return; match.begin.row -|= 1;
const end_row = match_.end.row - @min(match_.end.row, 1); match.end.row -|= 1;
const end_col = root.pos_to_width(end_row, match_.end.col, self.metrics) catch return; match = match.from_pos(root, self.metrics);
(self.matches.addOne(self.allocator) catch return).* = .{ (self.matches.addOne(self.allocator) catch return).* = match;
.begin = .{ .row = begin_row, .col = begin_col },
.end = .{ .row = end_row, .col = end_col },
};
self.need_render(); self.need_render();
} }
@ -6696,18 +6682,26 @@ pub const PosToWidthCache = struct {
self.cache.deinit(self.allocator); self.cache.deinit(self.allocator);
} }
pub fn range_to_selection(self: *Self, range: syntax.Range, root: Buffer.Root, metrics: Buffer.Metrics) ?Selection { pub fn range_to_selection(self: *Self, range: syntax.Range, root: Buffer.Root, metrics: Buffer.Metrics) Selection {
const start = range.start_point; var sel = Selection.from_range_raw(range);
const end = range.end_point; if (root != self.cached_root or self.cached_line != sel.begin.row) {
if (root != self.cached_root or self.cached_line != start.row) {
self.cache.clearRetainingCapacity(); self.cache.clearRetainingCapacity();
self.cached_line = start.row; self.cached_line = sel.begin.row;
self.cached_root = root; self.cached_root = root;
root.get_line_width_map(self.cached_line, &self.cache, self.allocator, metrics) catch return null; root.get_line_width_map(self.cached_line, &self.cache, self.allocator, metrics) catch return sel;
} }
const start_col = if (start.column < self.cache.items.len) self.cache.items[start.column] else start.column;
const end_col = if (end.row == start.row and end.column < self.cache.items.len) self.cache.items[end.column] else root.pos_to_width(end.row, end.column, metrics) catch end.column; sel.begin.col = if (sel.begin.col < self.cache.items.len)
return .{ .begin = .{ .row = start.row, .col = start_col }, .end = .{ .row = end.row, .col = end_col } }; self.cache.items[sel.begin.col]
else
sel.begin.col;
sel.end.col = if (sel.end.row == sel.end.row and sel.end.col < self.cache.items.len)
self.cache.items[sel.end.col]
else
root.pos_to_width(sel.end.row, sel.end.col, metrics) catch sel.end.col;
return sel;
} }
}; };