summaryrefslogtreecommitdiff
path: root/src/normal.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/normal.zig')
-rw-r--r--src/normal.zig464
1 files changed, 233 insertions, 231 deletions
diff --git a/src/normal.zig b/src/normal.zig
index 6647dcca..192fd933 100644
--- a/src/normal.zig
+++ b/src/normal.zig
@@ -12,6 +12,7 @@ const edit = @import("edit.zig");
const config = @import("config.zig");
const pdf_enabled = panes.Pdf.enabled;
const Pane = panes.Pane;
+const Text = panes.Text;
const Key = pardes.Key;
const hit = pardes.hit;
const sel_slot = pardes.sel_slot;
@@ -66,11 +67,11 @@ pub fn multiOnce(p: *Pardes) bool {
}
fn startSelRegex(p: *Pardes, pane: *Pane, split: bool) void {
- const text = edit.flatSurface(p, pane) catch return;
- const got = pane.ranges(text, 0, &pane.sel_snap);
+ const text = edit.flatSurface(p, &pane.body) catch return;
+ const got = pane.body.ranges(text, 0, &pane.sel_snap);
pane.nsel_snap = @intCast(got.n);
pane.sel_snap_pri = @intCast(got.pri);
- pane.sel_snap_expl = (pane.vsel.active and pane.vsel.explicit) or pane.msel.active;
+ pane.sel_snap_expl = (pane.body.vsel.active and pane.body.vsel.explicit) or pane.body.msel.active;
p.startPrompt(pane, .{ .search = if (split) config.split_marker else config.select_marker });
}
@@ -89,9 +90,9 @@ pub fn selRegexArmed(pane: *Pane) ?struct { pat: []const u8, split: bool } {
}
pub fn applySelRegex(p: *Pardes, pane: *Pane, pat: []const u8, split: bool) void {
- const text = edit.flatSurface(p, pane) catch return;
+ const text = edit.flatSurface(p, &pane.body) catch return;
const snap = pane.sel_snap[0..pane.nsel_snap];
- var out: [Pane.max_selections]modal.Selection = undefined;
+ var out: [Text.max_selections]modal.Selection = undefined;
var m: usize = 0;
if (pat.len > 0) if (mvzr.compile(pat)) |re| {
var hay_all = text;
@@ -109,7 +110,7 @@ pub fn applySelRegex(p: *Pardes, pane: *Pane, pat: []const u8, split: bool) void
const hay = hay_all[from..to];
var at: usize = 0;
var piece = from; // split: where the next piece begins
- while (at < hay.len and m < Pane.max_selections) {
+ while (at < hay.len and m < Text.max_selections) {
const hit_at = re.matchPos(at, hay) orelse break;
if (split) {
out[m] = .{ .anchor = piece, .head = from + hit_at.start };
@@ -122,7 +123,7 @@ pub fn applySelRegex(p: *Pardes, pane: *Pane, pat: []const u8, split: bool) void
// an empty match would otherwise never advance
at = if (hit_at.end > hit_at.start) hit_at.end else hit_at.end + 1;
}
- if (split and piece < to and m < Pane.max_selections) {
+ if (split and piece < to and m < Text.max_selections) {
out[m] = .{ .anchor = piece, .head = to };
m += 1;
}
@@ -135,37 +136,37 @@ pub fn applySelRegex(p: *Pardes, pane: *Pane, pat: []const u8, split: bool) void
r.anchor = @min(r.anchor, text.len);
r.head = @min(r.head, text.len);
}
- return pane.setRanges(text, snap, &.{}, pane.sel_snap_pri, pane.sel_snap_expl);
+ return pane.body.setRanges(text, snap, &.{}, pane.sel_snap_pri, pane.sel_snap_expl);
}
- pane.setRanges(text, out[0..m], &.{}, 0, true); // helix keeps primary 0 (its own TODO)
+ pane.body.setRanges(text, out[0..m], &.{}, 0, true); // helix keeps primary 0 (its own TODO)
}
const KeyState = struct {
- mode: Pane.Mode,
+ mode: Text.Mode,
select: bool,
normal: modal.Normal.State,
find_op: u8,
find_ch: u21,
- append_at: @FieldType(Pane, "append_at"),
+ append_at: @FieldType(Text, "append_at"),
- fn of(pane: *Pane) KeyState {
+ fn of(t: *Text) KeyState {
return .{
- .mode = pane.mode,
- .select = pane.select,
- .normal = pane.normal,
- .find_op = pane.find_op,
- .find_ch = pane.find_ch,
- .append_at = pane.append_at,
+ .mode = t.mode,
+ .select = t.select,
+ .normal = t.normal,
+ .find_op = t.find_op,
+ .find_ch = t.find_ch,
+ .append_at = t.append_at,
};
}
- fn into(s: KeyState, pane: *Pane) void {
- pane.mode = s.mode;
- pane.select = s.select;
- pane.normal = s.normal;
- pane.find_op = s.find_op;
- pane.find_ch = s.find_ch;
- pane.append_at = s.append_at;
+ fn into(s: KeyState, t: *Text) void {
+ t.mode = s.mode;
+ t.select = s.select;
+ t.normal = s.normal;
+ t.find_op = s.find_op;
+ t.find_ch = s.find_ch;
+ t.append_at = s.append_at;
}
};
@@ -175,25 +176,25 @@ const Replay = union(enum) {
insert: Key,
};
-pub fn replaySels(p: *Pardes, pane: *Pane, what: Replay) void {
+pub fn replaySels(p: *Pardes, t: *Text, what: Replay) void {
const id = p.active;
- const serial = pane.serial;
- // the whole selection in pane coordinates, document order. pane.sels
+ const serial = t.pane().serial;
+ // the whole selection in text coordinates, document order. t.sels
// is already ordered, so this only slots the primary into place.
- var list: [Pane.max_selections]Pane.SelRange = undefined;
+ var list: [Text.max_selections]Text.SelRange = undefined;
var n: usize = 0;
var pri: usize = 0;
- const prim: Pane.SelRange = .{
- .row = pane.cur_row,
- .col = pane.cur_col,
- .arow = if (pane.vsel.active) pane.vsel.row else pane.cur_row,
- .acol = if (pane.vsel.active) pane.vsel.col else pane.cur_col,
- .sticky = pane.sticky_col,
+ const prim: Text.SelRange = .{
+ .row = t.cur_row,
+ .col = t.cur_col,
+ .arow = if (t.vsel.active) t.vsel.row else t.cur_row,
+ .acol = if (t.vsel.active) t.vsel.col else t.cur_col,
+ .sticky = t.sticky_col,
};
- const pr = Pane.selStart(prim);
+ const pr = Text.selStart(prim);
var placed = false;
- for (pane.sels[0..pane.nsel]) |s| {
- const sr = Pane.selStart(s);
+ for (t.sels[0..t.nsel]) |s| {
+ const sr = Text.selStart(s);
if (!placed and (pr.row < sr.row or (pr.row == sr.row and pr.col <= sr.col))) {
pri = n;
list[n] = prim;
@@ -209,13 +210,13 @@ pub fn replaySels(p: *Pardes, pane: *Pane, what: Replay) void {
n += 1;
}
- const saved = KeyState.of(pane);
- const explicit = pane.vsel.explicit;
+ const saved = KeyState.of(t);
+ const explicit = t.vsel.explicit;
var after = saved;
var after_expl = explicit;
// each pass's result: cursor and anchor cells as distances from the
// end of the surface text, plus the range's own j/k goal column
- var res: [Pane.max_selections]struct { cur: usize, anc: usize, sticky: i32 } = undefined;
+ var res: [Text.max_selections]struct { cur: usize, anc: usize, sticky: i32 } = undefined;
p.multi_on = true;
p.multi_stop = false;
var passes: usize = 0;
@@ -224,98 +225,99 @@ pub fn replaySels(p: *Pardes, pane: *Pane, what: Replay) void {
i -= 1;
p.multi_first = passes == 0;
passes += 1;
- saved.into(pane);
- pane.nsel = 0; // the handler underneath sees ONE selection
- pane.cur_row = list[i].row;
- pane.cur_col = list[i].col;
- pane.vsel = .{
- .active = list[i].arow != list[i].row or list[i].acol != list[i].col or pane.select,
+ saved.into(t);
+ t.nsel = 0; // the handler underneath sees ONE selection
+ t.cur_row = list[i].row;
+ t.cur_col = list[i].col;
+ t.vsel = .{
+ .active = list[i].arow != list[i].row or list[i].acol != list[i].col or t.select,
.row = list[i].arow,
.col = list[i].acol,
.explicit = explicit,
};
- pane.msel.active = false;
- pane.sticky_col = list[i].sticky;
+ t.msel.active = false;
+ t.sticky_col = list[i].sticky;
switch (what) {
- .normal => |normal_action| executeNormalAction(p, pane, normal_action),
- .insert => |insert_key| edit.insertKey(p, pane, insert_key),
+ .normal => |normal_action| executeNormalAction(p, t, normal_action),
+ .insert => |insert_key| edit.insertKey(p, t, insert_key),
}
if (p.multi_stop) break;
if (i == pri) {
- after = KeyState.of(pane);
- after_expl = pane.vsel.explicit;
+ after = KeyState.of(t);
+ after_expl = t.vsel.explicit;
}
- const text = edit.flatSurface(p, pane) catch {
+ const text = edit.flatSurface(p, t) catch {
p.multi_stop = true;
break;
};
- const co = modal.offsetAt(text, .{ .row = @intCast(@max(0, pane.cur_row)), .col = @intCast(@max(0, pane.cur_col)) });
- const ao = if (pane.vsel.active)
- modal.offsetAt(text, .{ .row = @intCast(@max(0, pane.vsel.row)), .col = @intCast(@max(0, pane.vsel.col)) })
+ const co = modal.offsetAt(text, .{ .row = @intCast(@max(0, t.cur_row)), .col = @intCast(@max(0, t.cur_col)) });
+ const ao = if (t.vsel.active)
+ modal.offsetAt(text, .{ .row = @intCast(@max(0, t.vsel.row)), .col = @intCast(@max(0, t.vsel.col)) })
else
co;
- res[i] = .{ .cur = text.len - @min(co, text.len), .anc = text.len - @min(ao, text.len), .sticky = pane.sticky_col };
+ res[i] = .{ .cur = text.len - @min(co, text.len), .anc = text.len - @min(ao, text.len), .sticky = t.sticky_col };
}
p.multi_on = false;
p.multi_first = false;
if (p.multi_stop) {
p.multi_stop = false;
const pn = p.panes[id] orelse return;
- if (pn.serial == serial) pn.nsel = 0;
+ if (pn.serial == serial) pn.body.nsel = 0;
return;
}
- const text = edit.flatSurface(p, pane) catch return;
- var rs: [Pane.max_selections]modal.Selection = undefined;
- var st: [Pane.max_selections]i32 = undefined;
+ const text = edit.flatSurface(p, t) catch return;
+ var rs: [Text.max_selections]modal.Selection = undefined;
+ var st: [Text.max_selections]i32 = undefined;
for (res[0..n], 0..) |r, k| {
- rs[k] = Pane.cellOffRange(text, text.len - @min(r.anc, text.len), text.len - @min(r.cur, text.len));
+ rs[k] = Text.cellOffRange(text, text.len - @min(r.anc, text.len), text.len - @min(r.cur, text.len));
st[k] = r.sticky;
}
- pane.setRanges(text, rs[0..n], st[0..n], pri, after_expl);
- after.into(pane);
- pane.ensureCursorVisible(); // the view follows the PRIMARY, not the last pass
+ t.setRanges(text, rs[0..n], st[0..n], pri, after_expl);
+ after.into(t);
+ t.pane().ensureCursorVisible(); // the view follows the PRIMARY, not the last pass
}
-pub fn handleNormal(p: *Pardes, pane: *Pane, key: Key) void {
- const parsed = modal.Normal.parse(&pane.normal, normalInput(key));
+pub fn handleNormal(p: *Pardes, t: *Text, key: Key) void {
+ const parsed = modal.Normal.parse(&t.normal, normalInput(key));
const semantic = switch (parsed) {
.pending, .ignored, .unbound => return,
.action => |value| value,
};
- if (pane.nsel == 0 or semantic.scope() == .once)
- return executeNormalAction(p, pane, semantic);
- replaySels(p, pane, .{ .normal = semantic });
+ if (t.nsel == 0 or semantic.scope() == .once)
+ return executeNormalAction(p, t, semantic);
+ replaySels(p, t, .{ .normal = semantic });
}
-fn executeNormalAction(p: *Pardes, pane: *Pane, semantic: modal.Normal.Action) void {
+fn executeNormalAction(p: *Pardes, t: *Text, semantic: modal.Normal.Action) void {
+ const pane = t.pane();
const raw_selection = pane.rawPointerText(pane.sel[sel_slot]) != null;
- const before = .{ pane.cur_row, pane.cur_col, pane.vsel, pane.msel, pane.select, pane.nsel, pane.mode };
- defer if (raw_selection and !std.meta.eql(before, .{ pane.cur_row, pane.cur_col, pane.vsel, pane.msel, pane.select, pane.nsel, pane.mode })) {
+ const before = .{ t.cur_row, t.cur_col, t.vsel, t.msel, t.select, t.nsel, t.mode };
+ defer if (raw_selection and !std.meta.eql(before, .{ t.cur_row, t.cur_col, t.vsel, t.msel, t.select, t.nsel, t.mode })) {
pane.clearPointerSelection(sel_slot);
pane.sel[sel_slot].state = .none;
};
- if (raw_selection and semantic == .edit and semantic.edit.kind == .yank) return edit.normalYank(p, pane);
- edit.pinPaneCursor(p, pane);
- const text = edit.flatSurface(p, pane) catch return;
- const range = pane.primaryRange(text, 0);
+ if (raw_selection and semantic == .edit and semantic.edit.kind == .yank) return edit.normalYank(p, t);
+ edit.pinPaneCursor(p, t);
+ const text = edit.flatSurface(p, t) catch return;
+ const range = t.primaryRange(text, 0);
const cur = modal.selectionCursor(text, range);
switch (semantic) {
.escape => {
- pane.select = false;
+ t.select = false;
return exec.runBuiltin(p, .Last, p.active, "", null);
},
.goto => |go| switch (go.target) {
.file_start => {
- const line = if (go.explicit_count) @min(@as(usize, go.count) - 1, Pane.maxLine(text)) else 0;
- return pane.pointMove(text, range, modal.lineStartOffset(text, line));
+ const line = if (go.explicit_count) @min(@as(usize, go.count) - 1, Text.maxLine(text)) else 0;
+ return t.pointMove(text, range, modal.lineStartOffset(text, line));
},
- .last_line => return pane.pointMove(text, range, modal.lineStartOffset(text, Pane.maxLine(text))),
- .line_start => return pane.pointMove(text, range, modal.lineStartOffset(text, modal.lineAtOffset(text, cur))),
+ .last_line => return t.pointMove(text, range, modal.lineStartOffset(text, Text.maxLine(text))),
+ .line_start => return t.pointMove(text, range, modal.lineStartOffset(text, modal.lineAtOffset(text, cur))),
.line_end => {
const line = modal.lineAtOffset(text, cur);
const ls = modal.lineStartOffset(text, line);
- return pane.pointMove(text, range, @max(ls, modal.prevGrapheme(text, modal.lineEndOffset(text, line))));
+ return t.pointMove(text, range, @max(ls, modal.prevGrapheme(text, modal.lineEndOffset(text, line))));
},
.first_nonws => {
const line = modal.lineAtOffset(text, cur);
@@ -323,35 +325,35 @@ fn executeNormalAction(p: *Pardes, pane: *Pane, semantic: modal.Normal.Action) v
const slice = text[ls..modal.lineEndOffset(text, line)];
const nw = modal.firstNonWs(slice);
if (nw == slice.len) return;
- return pane.pointMove(text, range, ls + nw);
+ return t.pointMove(text, range, ls + nw);
},
- .line_down => return pane.visualMove(text, range, true, go.count, edit.paneWrapWidth(p, pane)),
- .line_up => return pane.visualMove(text, range, false, go.count, edit.paneWrapWidth(p, pane)),
+ .line_down => return t.visualMove(text, range, true, go.count, edit.paneWrapWidth(p, t)),
+ .line_up => return t.visualMove(text, range, false, go.count, edit.paneWrapWidth(p, t)),
.column => {
const line = modal.lineAtOffset(text, cur);
const ls = modal.lineStartOffset(text, line);
const slice = text[ls..modal.lineEndOffset(text, line)];
- return pane.pointMove(text, range, ls + modal.graphemeAtColumn(slice, @as(usize, go.count) - 1));
+ return t.pointMove(text, range, ls + modal.graphemeAtColumn(slice, @as(usize, go.count) - 1));
},
- .view_top => return pane.gotoWindow(text, range, .top, go.count),
- .view_center => return pane.gotoWindow(text, range, .center, go.count),
- .view_bottom => return pane.gotoWindow(text, range, .bottom, go.count),
+ .view_top => return t.gotoWindow(text, range, .top, go.count),
+ .view_center => return t.gotoWindow(text, range, .center, go.count),
+ .view_bottom => return t.gotoWindow(text, range, .bottom, go.count),
},
.view => |view| switch (view) {
.top => {
- pane.scrollBy(pane.cur_row - pane.scroll());
+ pane.scrollBy(t.cur_row - pane.scroll());
pane.ensureCursorVisible();
},
.center => {
- pane.scrollBy(pane.cur_row - (pane.scroll() + @divTrunc(@as(i32, pane.rows), 2) - 1));
+ pane.scrollBy(t.cur_row - (pane.scroll() + @divTrunc(@as(i32, pane.rows), 2) - 1));
pane.ensureCursorVisible();
},
.bottom => {
- pane.scrollBy(pane.cur_row - (pane.scroll() + @as(i32, pane.rows) - 1));
+ pane.scrollBy(t.cur_row - (pane.scroll() + @as(i32, pane.rows) - 1));
pane.ensureCursorVisible();
},
- .scroll_down => return pane.scrollViewMove(text, range, 1),
- .scroll_up => return pane.scrollViewMove(text, range, -1),
+ .scroll_down => return t.scrollViewMove(text, range, 1),
+ .scroll_up => return t.scrollViewMove(text, range, -1),
},
.find => |find| {
const op: u8 = switch (find.kind) {
@@ -360,9 +362,9 @@ fn executeNormalAction(p: *Pardes, pane: *Pane, semantic: modal.Normal.Action) v
.till_forward => @intCast(config.till_char_fwd),
.till_backward => @intCast(config.till_char_back),
};
- pane.find_op = op;
- pane.find_ch = find.char;
- return pane.findMove(
+ t.find_op = op;
+ t.find_ch = find.char;
+ return t.findMove(
text,
range,
find.char,
@@ -371,21 +373,21 @@ fn executeNormalAction(p: *Pardes, pane: *Pane, semantic: modal.Normal.Action) v
find.count,
);
},
- .replace_char => |char| return edit.normalReplaceChar(p, pane, char),
+ .replace_char => |char| return edit.normalReplaceChar(p, t, char),
.match_bracket => {
- const lines = edit.paneCursorLines(p, pane) catch return;
+ const lines = edit.paneCursorLines(p, t) catch return;
const mc = modal.matchBracket(lines, modal.positionAt(text, cur)) orelse return;
- return pane.pointMove(text, range, modal.offsetAt(text, mc));
+ return t.pointMove(text, range, modal.offsetAt(text, mc));
},
- .textobject => |object| return edit.textobjectSelect(p, pane, text, object.char, object.around),
- .surround_add => |char| return edit.surroundAdd(p, pane, char),
- .surround_delete => |char| return edit.surroundDelete(p, pane, char),
- .surround_replace => |replace| return edit.surroundReplace(p, pane, replace.from, replace.to),
+ .textobject => |object| return edit.textobjectSelect(p, t, text, object.char, object.around),
+ .surround_add => |char| return edit.surroundAdd(p, t, char),
+ .surround_delete => |char| return edit.surroundDelete(p, t, char),
+ .surround_replace => |replace| return edit.surroundReplace(p, t, replace.from, replace.to),
.paragraph => |paragraph| {
- const r2 = modal.moveParagraph(text, range, paragraph.count, paragraph.direction == .forward, pane.select);
- return pane.setRange(text, 0, r2, false);
+ const r2 = modal.moveParagraph(text, range, paragraph.count, paragraph.direction == .forward, t.select);
+ return t.setRange(text, 0, r2, false);
},
- .add_newline => |newline| return edit.addNewline(p, pane, newline.direction == .forward, newline.count),
+ .add_newline => |newline| return edit.addNewline(p, t, newline.direction == .forward, newline.count),
.diagnostic => |diagnostic| {
const fwd = diagnostic.direction == .forward;
if (!diagnostic.endpoint) {
@@ -409,46 +411,46 @@ fn executeNormalAction(p: *Pardes, pane: *Pane, semantic: modal.Normal.Action) v
.left => {
var target = cur;
for (0..move.count) |_| target = modal.prevGrapheme(text, target);
- return pane.pointMove(text, range, target);
+ return t.pointMove(text, range, target);
},
.right => {
var target = cur;
for (0..move.count) |_| target = modal.nextGrapheme(text, target);
- return pane.pointMove(text, range, target);
+ return t.pointMove(text, range, target);
},
- .down => return pane.verticalMove(text, range, true, move.count),
- .up => return pane.verticalMove(text, range, false, move.count),
- .next_word_start => return pane.wordMove(text, range, move.count, .next_word_start),
- .prev_word_start => return pane.wordMove(text, range, move.count, .prev_word_start),
- .next_word_end => return pane.wordMove(text, range, move.count, .next_word_end),
- .next_long_word_start => return pane.wordMove(text, range, move.count, .next_long_word_start),
- .prev_long_word_start => return pane.wordMove(text, range, move.count, .prev_long_word_start),
- .next_long_word_end => return pane.wordMove(text, range, move.count, .next_long_word_end),
+ .down => return t.verticalMove(text, range, true, move.count),
+ .up => return t.verticalMove(text, range, false, move.count),
+ .next_word_start => return t.wordMove(text, range, move.count, .next_word_start),
+ .prev_word_start => return t.wordMove(text, range, move.count, .prev_word_start),
+ .next_word_end => return t.wordMove(text, range, move.count, .next_word_end),
+ .next_long_word_start => return t.wordMove(text, range, move.count, .next_long_word_start),
+ .prev_long_word_start => return t.wordMove(text, range, move.count, .prev_long_word_start),
+ .next_long_word_end => return t.wordMove(text, range, move.count, .next_long_word_end),
},
.repeat_find => |count| {
- if (pane.find_op == 0) return;
- const fwd = pane.find_op == config.find_char_fwd or pane.find_op == config.till_char_fwd;
- const till = pane.find_op == config.till_char_fwd or pane.find_op == config.till_char_back;
+ if (t.find_op == 0) return;
+ const fwd = t.find_op == config.find_char_fwd or t.find_op == config.till_char_fwd;
+ const till = t.find_op == config.till_char_fwd or t.find_op == config.till_char_back;
var repeated = range;
var moved = false;
for (0..count) |_| {
const cc = modal.selectionCursor(text, repeated);
- const target = modal.findTarget(text, cc, pane.find_ch, fwd, till, 1) orelse break;
- repeated = if (pane.select)
+ const target = modal.findTarget(text, cc, t.find_ch, fwd, till, 1) orelse break;
+ repeated = if (t.select)
modal.moveSelectionCursor(text, repeated, target, true)
else
modal.moveSelectionCursor(text, .{ .anchor = cc, .head = cc }, target, true);
moved = true;
}
if (!moved) return;
- return pane.setRange(text, 0, repeated, false);
+ return t.setRange(text, 0, repeated, false);
},
.line => |line_kind| switch (line_kind) {
- .start => return pane.pointMove(text, range, modal.lineStartOffset(text, modal.lineAtOffset(text, cur))),
+ .start => return t.pointMove(text, range, modal.lineStartOffset(text, modal.lineAtOffset(text, cur))),
.end => {
const line = modal.lineAtOffset(text, cur);
const ls = modal.lineStartOffset(text, line);
- return pane.pointMove(text, range, @max(ls, modal.prevGrapheme(text, modal.lineEndOffset(text, line))));
+ return t.pointMove(text, range, @max(ls, modal.prevGrapheme(text, modal.lineEndOffset(text, line))));
},
.first_nonws => {
const line = modal.lineAtOffset(text, cur);
@@ -456,21 +458,21 @@ fn executeNormalAction(p: *Pardes, pane: *Pane, semantic: modal.Normal.Action) v
const slice = text[ls..modal.lineEndOffset(text, line)];
const nw = modal.firstNonWs(slice);
if (nw == slice.len) return;
- return pane.pointMove(text, range, ls + nw);
+ return t.pointMove(text, range, ls + nw);
},
},
.goto_line => |go| {
if (!go.explicit) return;
- const line = @min(@as(usize, go.count) - 1, Pane.maxLine(text));
- return pane.pointMove(text, range, modal.lineStartOffset(text, line));
+ const line = @min(@as(usize, go.count) - 1, Text.maxLine(text));
+ return t.pointMove(text, range, modal.lineStartOffset(text, line));
},
.page => |page| switch (page.kind) {
// Counts were parsed historically but these four text view
// actions intentionally move exactly one viewport unit.
- .half_down => return pane.halfPageMove(text, range, true),
- .half_up => return pane.halfPageMove(text, range, false),
- .down => return pane.scrollViewMove(text, range, @as(i32, pane.rows)),
- .up => return pane.scrollViewMove(text, range, -@as(i32, pane.rows)),
+ .half_down => return t.halfPageMove(text, range, true),
+ .half_up => return t.halfPageMove(text, range, false),
+ .down => return t.scrollViewMove(text, range, @as(i32, pane.rows)),
+ .up => return t.scrollViewMove(text, range, -@as(i32, pane.rows)),
},
.insert => |insert| {
const where: edit.InsertAt = switch (insert.kind) {
@@ -481,59 +483,59 @@ fn executeNormalAction(p: *Pardes, pane: *Pane, semantic: modal.Normal.Action) v
.open_below => .open_below,
.open_above => .open_above,
};
- return edit.enterInsert(p, pane, where, insert.count);
+ return edit.enterInsert(p, t, where, insert.count);
},
.select => |select| switch (select.kind) {
.mode => {
- if (pane.select) {
- pane.select = false;
+ if (t.select) {
+ t.select = false;
} else {
- pane.select = true;
- if (pane.msel.active) {
- pane.setRange(text, 0, range, true);
- } else if (!pane.vsel.active) {
- pane.vsel = .{ .active = true, .row = pane.cur_row, .col = pane.cur_col, .explicit = true };
+ t.select = true;
+ if (t.msel.active) {
+ t.setRange(text, 0, range, true);
+ } else if (!t.vsel.active) {
+ t.vsel = .{ .active = true, .row = t.cur_row, .col = t.cur_col, .explicit = true };
} else {
- pane.vsel.explicit = true;
+ t.vsel.explicit = true;
}
}
- pane.cur_pinned = true;
+ t.cur_pinned = true;
},
- .line => return pane.lineSelect(text, range, select.count),
- .line_bounds => return pane.lineBoundsSelect(text, range),
- .shrink_to_line_bounds => return pane.shrinkSelToLineBounds(text, range),
- .collapse => return pane.setRange(text, 0, .{ .anchor = cur, .head = cur }, false),
+ .line => return t.lineSelect(text, range, select.count),
+ .line_bounds => return t.lineBoundsSelect(text, range),
+ .shrink_to_line_bounds => return t.shrinkSelToLineBounds(text, range),
+ .collapse => return t.setRange(text, 0, .{ .anchor = cur, .head = cur }, false),
.flip => {
- const explicit = (pane.vsel.active and pane.vsel.explicit) or pane.msel.active;
- return pane.setRange(text, 0, .{ .anchor = range.head, .head = range.anchor }, explicit);
+ const explicit = (t.vsel.active and t.vsel.explicit) or t.msel.active;
+ return t.setRange(text, 0, .{ .anchor = range.head, .head = range.anchor }, explicit);
},
- .all => return pane.setRange(text, 0, .{ .anchor = 0, .head = text.len }, false),
+ .all => return t.setRange(text, 0, .{ .anchor = 0, .head = text.len }, false),
},
.multi => |multi| {
if (multi.kind == .keep_primary) {
- pane.nsel = 0;
+ t.nsel = 0;
return;
}
- return pane.multiSelAction(text, multi.kind, multi.count);
+ return t.multiSelAction(text, multi.kind, multi.count);
},
.select_regex => |split| return startSelRegex(p, pane, split),
.edit => |op| switch (op.kind) {
- .delete => return edit.normalDelete(p, pane, true),
- .delete_noyank => return edit.normalDelete(p, pane, false),
- .change => return edit.normalChange(p, pane),
- .yank => return edit.normalYank(p, pane),
- .replace_with_yank => return edit.normalReplaceYank(p, pane),
- .paste_after => return edit.pasteText(p, pane, p.yank orelse return, false, op.count),
- .paste_before => return edit.pasteText(p, pane, p.yank orelse return, true, op.count),
- .switch_case => return edit.normalCase(p, pane, .toggle),
- .lowercase => return edit.normalCase(p, pane, .lower),
- .uppercase => return edit.normalCase(p, pane, .upper),
- .join_lines => return edit.normalJoin(p, pane),
- .indent => return edit.normalIndent(p, pane, op.count, true),
- .unindent => return edit.normalIndent(p, pane, op.count, false),
- .comment_toggle => return edit.normalToggleComment(p, pane),
- .undo => return edit.doUndo(p, pane),
- .redo => return edit.doRedo(p, pane),
+ .delete => return edit.normalDelete(p, t, true),
+ .delete_noyank => return edit.normalDelete(p, t, false),
+ .change => return edit.normalChange(p, t),
+ .yank => return edit.normalYank(p, t),
+ .replace_with_yank => return edit.normalReplaceYank(p, t),
+ .paste_after => return edit.pasteText(p, t, p.yank orelse return, false, op.count),
+ .paste_before => return edit.pasteText(p, t, p.yank orelse return, true, op.count),
+ .switch_case => return edit.normalCase(p, t, .toggle),
+ .lowercase => return edit.normalCase(p, t, .lower),
+ .uppercase => return edit.normalCase(p, t, .upper),
+ .join_lines => return edit.normalJoin(p, t),
+ .indent => return edit.normalIndent(p, t, op.count, true),
+ .unindent => return edit.normalIndent(p, t, op.count, false),
+ .comment_toggle => return edit.normalToggleComment(p, t),
+ .undo => return edit.doUndo(p, t),
+ .redo => return edit.doRedo(p, t),
},
.lsp => |request| return p.lspRequest(p.active, switch (request) {
.definition => .definition,
@@ -543,14 +545,14 @@ fn executeNormalAction(p: *Pardes, pane: *Pane, semantic: modal.Normal.Action) v
.references => .references,
.format => .format,
}, ""),
- .adjust_number => |delta| return edit.normalAdjustNumber(p, pane, delta),
+ .adjust_number => |delta| return edit.normalAdjustNumber(p, t, delta),
.leader => {
p.leader_on = true;
p.leader_n = 0;
},
.command_line => {
p.enterTagEdit(pane, -1);
- if (pane.tag_edit) pane.mode = .normal;
+ if (pane.tag_edit) t.mode = .normal;
},
.pipe_selection => |how| return p.startPrompt(pane, .{ .pipe = how }),
.search => return p.startPrompt(pane, .{ .search = config.search_marker }),
@@ -565,15 +567,15 @@ pub fn handlePdfNormal(p: *Pardes, pane: *Pane, key: Key) void {
if (comptime !pdf_enabled) return;
if (pane.pdf == null) return;
if (hit(key, config.leave_pane)) {
- pane.normal.clear();
- pane.select = false;
+ pane.body.normal.clear();
+ pane.body.select = false;
return exec.runBuiltin(p, .Last, p.active, "", null);
}
- if (pane.normal.prefix == .none and isPrefix(key, 'f')) {
- pane.normal.clear();
+ if (pane.body.normal.prefix == .none and isPrefix(key, 'f')) {
+ pane.body.normal.clear();
return exec.runBuiltin(p, .PdfSections, p.active, "", null);
}
- const parsed = modal.Normal.parse(&pane.normal, normalInput(key));
+ const parsed = modal.Normal.parse(&pane.body.normal, normalInput(key));
switch (parsed) {
.pending, .ignored, .unbound => {},
.action => |semantic| {
@@ -595,7 +597,7 @@ pub fn handlePdfNormal(p: *Pardes, pane: *Pane, key: Key) void {
},
.command_line => {
p.enterTagEdit(pane, -1);
- if (pane.tag_edit) pane.mode = .normal;
+ if (pane.tag_edit) pane.body.mode = .normal;
},
.search => p.startPrompt(pane, .{ .search = config.search_marker }),
.search_forward => look.lookWalk(p, 1),
@@ -618,57 +620,57 @@ test "gj/gk step the wrapped rows a body draws while j/k keep the file's lines"
// gj holds the column INSIDE the row and lands on the next break; the line
// is one line the whole way down, which is the whole point.
- pane.cur_row = 0;
- pane.cur_col = 3;
- pane.sticky_col = -1;
+ pane.body.cur_row = 0;
+ pane.body.cur_col = 3;
+ pane.body.sticky_col = -1;
p.update(.{ .key = .{ .cp = 'g' } });
p.update(.{ .key = .{ .cp = 'j' } });
- try std.testing.expectEqual(@as(i32, 0), pane.cur_row);
- try std.testing.expectEqual(@as(i32, @intCast(width + 3)), pane.cur_col);
+ try std.testing.expectEqual(@as(i32, 0), pane.body.cur_row);
+ try std.testing.expectEqual(@as(i32, @intCast(width + 3)), pane.body.cur_col);
p.update(.{ .key = .{ .cp = 'g' } });
p.update(.{ .key = .{ .cp = 'j' } });
- try std.testing.expectEqual(@as(i32, @intCast(width * 2 + 3)), pane.cur_col);
+ try std.testing.expectEqual(@as(i32, @intCast(width * 2 + 3)), pane.body.cur_col);
p.update(.{ .key = .{ .cp = 'g' } });
p.update(.{ .key = .{ .cp = 'k' } });
- try std.testing.expectEqual(@as(i32, @intCast(width + 3)), pane.cur_col);
+ try std.testing.expectEqual(@as(i32, @intCast(width + 3)), pane.body.cur_col);
// A count is counted in rows, not lines.
p.update(.{ .key = .{ .cp = '2' } });
p.update(.{ .key = .{ .cp = 'g' } });
p.update(.{ .key = .{ .cp = 'j' } });
- try std.testing.expectEqual(@as(i32, 0), pane.cur_row);
- try std.testing.expectEqual(@as(i32, @intCast(width * 3 + 3)), pane.cur_col);
+ try std.testing.expectEqual(@as(i32, 0), pane.body.cur_row);
+ try std.testing.expectEqual(@as(i32, @intCast(width * 3 + 3)), pane.body.cur_col);
// Plain j/k are untouched: one FILE line each, whatever the body drew.
- pane.cur_col = 3;
- pane.sticky_col = -1;
+ pane.body.cur_col = 3;
+ pane.body.sticky_col = -1;
p.update(.{ .key = .{ .cp = 'j' } });
- try std.testing.expectEqual(@as(i32, 1), pane.cur_row);
+ try std.testing.expectEqual(@as(i32, 1), pane.body.cur_row);
p.update(.{ .key = .{ .cp = 'k' } });
- try std.testing.expectEqual(@as(i32, 0), pane.cur_row);
+ try std.testing.expectEqual(@as(i32, 0), pane.body.cur_row);
// The last row of a line steps into the next line's first row, and the
// first row steps back onto the previous line's LAST row.
- pane.cur_row = 0;
- pane.cur_col = @intCast(long.len - 1);
- pane.sticky_col = -1;
+ pane.body.cur_row = 0;
+ pane.body.cur_col = @intCast(long.len - 1);
+ pane.body.sticky_col = -1;
p.update(.{ .key = .{ .cp = 'g' } });
p.update(.{ .key = .{ .cp = 'j' } });
- try std.testing.expectEqual(@as(i32, 1), pane.cur_row);
- pane.cur_col = 0;
- pane.sticky_col = -1;
+ try std.testing.expectEqual(@as(i32, 1), pane.body.cur_row);
+ pane.body.cur_col = 0;
+ pane.body.sticky_col = -1;
p.update(.{ .key = .{ .cp = 'g' } });
p.update(.{ .key = .{ .cp = 'k' } });
- try std.testing.expectEqual(@as(i32, 0), pane.cur_row);
- try std.testing.expect(pane.cur_col >= @as(i32, @intCast(long.len - long.len % width)));
+ try std.testing.expectEqual(@as(i32, 0), pane.body.cur_row);
+ try std.testing.expect(pane.body.cur_col >= @as(i32, @intCast(long.len - long.len % width)));
// Wrap OFF: a line is one visual row, so gj/gk ARE j/k again.
p.settings.wrap = false;
- pane.cur_row = 0;
- pane.cur_col = 3;
- pane.sticky_col = -1;
+ pane.body.cur_row = 0;
+ pane.body.cur_col = 3;
+ pane.body.sticky_col = -1;
p.update(.{ .key = .{ .cp = 'g' } });
p.update(.{ .key = .{ .cp = 'j' } });
- try std.testing.expectEqual(@as(i32, 1), pane.cur_row);
+ try std.testing.expectEqual(@as(i32, 1), pane.body.cur_row);
}
test "flat text movement and selection replay need no scratch rows" {
@@ -685,39 +687,39 @@ test "flat text movement and selection replay need no scratch rows" {
p.scratch.deinit();
p.scratch = .init(denied.allocator());
- pane.cur_row = 4094;
- handleNormal(p, pane, .{ .cp = 'l' });
- try std.testing.expectEqual(@as(i32, 1), pane.cur_col);
- handleNormal(p, pane, .{ .cp = 'j' });
- try std.testing.expectEqual(@as(i32, 4095), pane.cur_row);
- try std.testing.expectEqual(@as(i32, 1), pane.cur_col);
- handleNormal(p, pane, .{ .cp = 'h' });
- try std.testing.expectEqual(@as(i32, 0), pane.cur_col);
- executeNormalAction(p, pane, .{ .textobject = .{ .char = 'w', .around = false } });
- try std.testing.expectEqual(@as(i32, 2), pane.cur_col);
- try std.testing.expectEqual(@as(i32, 0), pane.vsel.col);
+ pane.body.cur_row = 4094;
+ handleNormal(p, &pane.body, .{ .cp = 'l' });
+ try std.testing.expectEqual(@as(i32, 1), pane.body.cur_col);
+ handleNormal(p, &pane.body, .{ .cp = 'j' });
+ try std.testing.expectEqual(@as(i32, 4095), pane.body.cur_row);
+ try std.testing.expectEqual(@as(i32, 1), pane.body.cur_col);
+ handleNormal(p, &pane.body, .{ .cp = 'h' });
+ try std.testing.expectEqual(@as(i32, 0), pane.body.cur_col);
+ executeNormalAction(p, &pane.body, .{ .textobject = .{ .char = 'w', .around = false } });
+ try std.testing.expectEqual(@as(i32, 2), pane.body.cur_col);
+ try std.testing.expectEqual(@as(i32, 0), pane.body.vsel.col);
- pane.vsel.active = false;
- pane.cur_row = 1024;
- pane.cur_col = 0;
- pane.nsel = 1;
- pane.sels[0] = .{ .row = 3072, .col = 0, .arow = 3072, .acol = 0 };
- handleNormal(p, pane, .{ .cp = 'l' });
- handleNormal(p, pane, .{ .cp = 'j' });
- try std.testing.expectEqual(@as(u8, 1), pane.nsel);
- try std.testing.expectEqual(@as(i32, 1025), pane.cur_row);
- try std.testing.expectEqual(@as(i32, 1), pane.cur_col);
- try std.testing.expectEqual(@as(i32, 3073), pane.sels[0].row);
- try std.testing.expectEqual(@as(i32, 1), pane.sels[0].col);
+ pane.body.vsel.active = false;
+ pane.body.cur_row = 1024;
+ pane.body.cur_col = 0;
+ pane.body.nsel = 1;
+ pane.body.sels[0] = .{ .row = 3072, .col = 0, .arow = 3072, .acol = 0 };
+ handleNormal(p, &pane.body, .{ .cp = 'l' });
+ handleNormal(p, &pane.body, .{ .cp = 'j' });
+ try std.testing.expectEqual(@as(u8, 1), pane.body.nsel);
+ try std.testing.expectEqual(@as(i32, 1025), pane.body.cur_row);
+ try std.testing.expectEqual(@as(i32, 1), pane.body.cur_col);
+ try std.testing.expectEqual(@as(i32, 3073), pane.body.sels[0].row);
+ try std.testing.expectEqual(@as(i32, 1), pane.body.sels[0].col);
try std.testing.expect(!denied.has_induced_failure);
try std.testing.expectEqual(@as(usize, 0), p.scratch.queryCapacity());
- pane.nsel = 0;
- pane.cur_col = 4;
- executeNormalAction(p, pane, .match_bracket);
+ pane.body.nsel = 0;
+ pane.body.cur_col = 4;
+ executeNormalAction(p, &pane.body, .match_bracket);
try std.testing.expect(denied.has_induced_failure);
- try std.testing.expectEqual(@as(i32, 4), pane.cur_col);
+ try std.testing.expectEqual(@as(i32, 4), pane.body.cur_col);
denied.fail_index = std.math.maxInt(usize);
- executeNormalAction(p, pane, .match_bracket);
- try std.testing.expectEqual(@as(i32, 8), pane.cur_col);
+ executeNormalAction(p, &pane.body, .match_bracket);
+ try std.testing.expectEqual(@as(i32, 8), pane.body.cur_col);
}