Skip to content

Commit d41ed97

Browse files
committed
add rudimentary support for selections
1 parent 220cd30 commit d41ed97

2 files changed

Lines changed: 114 additions & 0 deletions

File tree

src/libfn/Editor.zig

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,56 @@ pub fn moveSelectionsToStartOfLine(self: *Editor) void {
353353
}
354354
}
355355

356+
pub fn extendSelectionsRight(self: *Editor) void {
357+
for (self.selections.items) |*s| {
358+
s.cursor.col = s.cursor.col +| 1;
359+
360+
const line = self.getLine(s.cursor.row);
361+
362+
if (s.isCursor()) s.anchor.col = @min(s.anchor.col -| 1, line.len);
363+
364+
if (s.cursor.row >= self.lineCount() and s.cursor.col > line.len) {
365+
s.cursor.col = line.len;
366+
} else if (s.cursor.col > line.len) {
367+
s.cursor.row = s.cursor.row +| 1;
368+
s.cursor.col = 0;
369+
}
370+
}
371+
}
372+
373+
pub fn extendSelectionsLeft(self: *Editor) void {
374+
for (self.selections.items) |*s| {
375+
if (s.cursor.col == 0 and s.cursor.row == 0) continue;
376+
377+
if (s.isCursor()) {
378+
const line = self.getLine(s.cursor.row);
379+
s.anchor.col = @min(s.anchor.col + 1, line.len);
380+
}
381+
382+
if (s.cursor.col == 0) {
383+
s.cursor.row -= 1;
384+
const line = self.getLine(s.cursor.row);
385+
s.cursor.col = line.len;
386+
} else {
387+
s.cursor.col -= 1;
388+
}
389+
}
390+
}
391+
392+
pub fn extendSelectionsDown(self: *Editor) void {
393+
for (self.selections.items) |*s| {
394+
if (s.cursor.row >= self.lineCount()) continue;
395+
s.cursor.row += 1;
396+
}
397+
}
398+
399+
pub fn extendSelectionsUp(self: *Editor) void {
400+
for (self.selections.items) |*s| {
401+
if (s.cursor.row == 0) continue;
402+
s.cursor.row -= 1;
403+
}
404+
}
405+
356406
/// Selects the word that comes after each selection's cursor. Behavior varies depending on cursor
357407
/// location for each selection:
358408
///

src/tui/main.zig

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const Mode = enum {
1313
insert,
1414
goto,
1515
maybe_exit_insert,
16+
select,
1617
};
1718

1819
const Event = union(enum) {
@@ -216,6 +217,7 @@ pub fn update(ctx: Vxim.UpdateContext) !Vxim.UpdateResult {
216217
.maybe_exit_insert => {}, // Just keep whatever cursor is currently active.
217218
.normal => ctx.root_win.setCursorShape(.block),
218219
.goto => ctx.root_win.setCursorShape(.block),
220+
.select => ctx.root_win.setCursorShape(.block),
219221
}
220222
}
221223

@@ -241,6 +243,8 @@ fn editor(ctx: Vxim.UpdateContext, container: vaxis.Window) !void {
241243

242244
if (key.matches('g', .{})) state.mode = .goto;
243245

246+
if (key.matches('v', .{})) state.mode = .select;
247+
244248
if (key.matches('h', .{})) state.editor.moveSelectionsLeft();
245249
if (key.matches(vaxis.Key.left, .{})) state.editor.moveSelectionsLeft();
246250
if (key.matches('j', .{})) state.editor.moveSelectionsDown();
@@ -297,6 +301,16 @@ fn editor(ctx: Vxim.UpdateContext, container: vaxis.Window) !void {
297301
if (key.text) |text| try state.editor.insertTextAtCursors(state.gpa, text);
298302
state.mode = .insert;
299303
}
304+
} else if (state.mode == .select) {
305+
if (key.matches('h', .{})) state.editor.extendSelectionsLeft();
306+
if (key.matches('j', .{})) state.editor.extendSelectionsDown();
307+
if (key.matches('k', .{})) state.editor.extendSelectionsUp();
308+
if (key.matches('l', .{})) state.editor.extendSelectionsRight();
309+
310+
if (key.matches('d', .{})) try state.editor.deleteInsideSelections(state.gpa);
311+
312+
if (key.matches('v', .{})) state.mode = .normal;
313+
if (key.matches(vaxis.Key.escape, .{})) state.mode = .normal;
300314
}
301315

302316
if (key.matches('s', .{ .super = true })) try state.editor.saveFile(state.gpa);
@@ -337,6 +351,8 @@ fn editor(ctx: Vxim.UpdateContext, container: vaxis.Window) !void {
337351
else => {},
338352
}
339353

354+
// Draw text.
355+
// FIXME: no need to draw to the end of the file.
340356
for (state.v_scroll..state.editor.lineCount()) |idx| {
341357
const line = state.editor.getLine(idx);
342358

@@ -347,6 +363,54 @@ fn editor(ctx: Vxim.UpdateContext, container: vaxis.Window) !void {
347363
.{ .row_offset = @intCast(idx -| state.v_scroll), .wrap = .none },
348364
);
349365
}
366+
367+
// Draw selections.
368+
for (state.editor.selections.items) |s| {
369+
// If no part of this selection is visible we can skip rendering it.
370+
if ((s.cursor.row < state.v_scroll or s.cursor.row > state.v_scroll +| container.height) and
371+
(s.anchor.row < state.v_scroll or s.anchor.row > state.v_scroll +| container.height))
372+
{
373+
continue;
374+
}
375+
376+
const row_start = s.toRange().before().row -| state.v_scroll;
377+
const line_start = state.editor.getLine(row_start);
378+
const col_start = if (s.toRange().before().row < state.v_scroll)
379+
0
380+
else
381+
@min(s.toRange().before().col -| state.h_scroll, line_start.len -| state.h_scroll);
382+
383+
const row_end = @min(s.toRange().after().row -| state.v_scroll, state.v_scroll +| container.height);
384+
const line_end = state.editor.getLine(row_end);
385+
const col_end = if (s.toRange().after().row > state.v_scroll +| container.height)
386+
line_end.len -| state.h_scroll
387+
else
388+
s.toRange().after().col -| state.h_scroll;
389+
390+
var row_it = row_start;
391+
392+
std.debug.assert(
393+
row_start < row_end or
394+
(row_start == row_end and col_start <= col_end),
395+
);
396+
397+
while (row_it <= row_end) {
398+
const line = state.editor.getLine(row_it);
399+
400+
const start = if (row_it == row_start) col_start else 0;
401+
const end = if (row_it == row_end) col_end else line.len -| state.h_scroll;
402+
403+
for (start..end) |cell_col| {
404+
if (container.readCell(@intCast(cell_col), @intCast(row_it))) |cell| {
405+
var new_cell = cell;
406+
new_cell.style = .{ .reverse = true };
407+
container.writeCell(@intCast(cell_col), @intCast(row_it), new_cell);
408+
}
409+
}
410+
411+
row_it +|= 1;
412+
}
413+
}
350414
}
351415

352416
test "refAllDecls" {

0 commit comments

Comments
 (0)