Add normal dot repeat
This commit is contained in:
@@ -188,6 +188,7 @@ such as arrow keys, Home/End, PageUp/PageDown, Escape, or `%`.
|
|||||||
| `Ctrl-u` `Ctrl-d` | half-page up/down when the SSH client exposes Ctrl; `g u` / `g d` are mobile fallback aliases |
|
| `Ctrl-u` `Ctrl-d` | half-page up/down when the SSH client exposes Ctrl; `g u` / `g d` are mobile fallback aliases |
|
||||||
| `H` `L` | mobile-reachable line start/end aliases |
|
| `H` `L` | mobile-reachable line start/end aliases |
|
||||||
| `0` `^` `$` | attached/Vim-style line start/first-nonblank/end aliases; `Space g` `l` opens line targets |
|
| `0` `^` `$` | attached/Vim-style line start/first-nonblank/end aliases; `Space g` `l` opens line targets |
|
||||||
|
| `.` | repeat the last repeatable local edit (insert/change/delete/put/open-line subset) |
|
||||||
| `u` | undo |
|
| `u` | undo |
|
||||||
| `Space u` `r` | redo |
|
| `Space u` `r` | redo |
|
||||||
| `Space w` | write/save current buffer |
|
| `Space w` | write/save current buffer |
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ Rows are redgate TSV requirements: `ring<TAB>id<TAB>summary [tag]`.
|
|||||||
1 011 Vertical line motions SHALL preserve a preferred cursor cell across ragged lines, clamped to each target line until horizontal/editing motion resets the preference. [mobile]
|
1 011 Vertical line motions SHALL preserve a preferred cursor cell across ragged lines, clamped to each target line until horizontal/editing motion resets the preference. [mobile]
|
||||||
1 012 Line selections SHALL be able to include the line's newline boundary so linewise edits can preserve Vim-like newline semantics. [mobile]
|
1 012 Line selections SHALL be able to include the line's newline boundary so linewise edits can preserve Vim-like newline semantics. [mobile]
|
||||||
1 013 Document start/end and half-page motions SHALL be reachable from Normal mode with Ctrl-capable SSH keys and documented mobile fallback aliases. [mobile]
|
1 013 Document start/end and half-page motions SHALL be reachable from Normal mode with Ctrl-capable SSH keys and documented mobile fallback aliases. [mobile]
|
||||||
|
1 014 Normal-mode dot repeat SHALL replay the last deterministic local edit for the supported insert/change/delete/put subset without recording panel, search, or external side effects. [mobile]
|
||||||
|
|
||||||
## ui
|
## ui
|
||||||
|
|
||||||
|
|||||||
+173
-5
@@ -53,6 +53,17 @@ const EditSnapshot = struct {
|
|||||||
cursor_byte: usize,
|
cursor_byte: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const RepeatInsert = struct {
|
||||||
|
before_protocol: ?[]u8 = null,
|
||||||
|
text: []u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const RepeatEdit = union(enum) {
|
||||||
|
insert: RepeatInsert,
|
||||||
|
protocol: []u8,
|
||||||
|
paste,
|
||||||
|
};
|
||||||
|
|
||||||
const ObjectRange = struct {
|
const ObjectRange = struct {
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
@@ -170,6 +181,11 @@ pub const Client = struct {
|
|||||||
undo_stack: std.ArrayList(EditSnapshot) = .empty,
|
undo_stack: std.ArrayList(EditSnapshot) = .empty,
|
||||||
redo_stack: std.ArrayList(EditSnapshot) = .empty,
|
redo_stack: std.ArrayList(EditSnapshot) = .empty,
|
||||||
yank_bytes: ?[]u8 = null,
|
yank_bytes: ?[]u8 = null,
|
||||||
|
last_repeat: ?RepeatEdit = null,
|
||||||
|
insert_repeat_bytes: std.ArrayList(u8) = .empty,
|
||||||
|
pending_insert_protocol: ?[]u8 = null,
|
||||||
|
recording_insert_repeat: bool = false,
|
||||||
|
replaying_repeat: bool = false,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, viewport: Viewport) !Client {
|
pub fn init(allocator: std.mem.Allocator, viewport: Viewport) !Client {
|
||||||
return initWithIo(allocator, viewport, null);
|
return initWithIo(allocator, viewport, null);
|
||||||
@@ -193,6 +209,9 @@ pub const Client = struct {
|
|||||||
if (self.saved_bytes) |bytes| self.allocator.free(bytes);
|
if (self.saved_bytes) |bytes| self.allocator.free(bytes);
|
||||||
if (self.owned_message) |bytes| self.allocator.free(bytes);
|
if (self.owned_message) |bytes| self.allocator.free(bytes);
|
||||||
if (self.yank_bytes) |bytes| self.allocator.free(bytes);
|
if (self.yank_bytes) |bytes| self.allocator.free(bytes);
|
||||||
|
self.freeRepeatEdit();
|
||||||
|
if (self.pending_insert_protocol) |line| self.allocator.free(line);
|
||||||
|
self.insert_repeat_bytes.deinit(self.allocator);
|
||||||
self.search_prompt.deinit(self.allocator);
|
self.search_prompt.deinit(self.allocator);
|
||||||
self.project_search_prompt.deinit(self.allocator);
|
self.project_search_prompt.deinit(self.allocator);
|
||||||
self.allocator.free(self.search_query);
|
self.allocator.free(self.search_query);
|
||||||
@@ -463,6 +482,7 @@ pub const Client = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn enterInsertMode(self: *Client) void {
|
pub fn enterInsertMode(self: *Client) void {
|
||||||
|
self.beginInsertRepeat();
|
||||||
self.mode = .insert;
|
self.mode = .insert;
|
||||||
self.message = "insert";
|
self.message = "insert";
|
||||||
}
|
}
|
||||||
@@ -1026,6 +1046,7 @@ pub const Client = struct {
|
|||||||
switch (event) {
|
switch (event) {
|
||||||
.text => |text| {
|
.text => |text| {
|
||||||
if (std.mem.eql(u8, text, "i")) {
|
if (std.mem.eql(u8, text, "i")) {
|
||||||
|
self.beginInsertRepeat();
|
||||||
self.mode = .insert;
|
self.mode = .insert;
|
||||||
self.message = "insert";
|
self.message = "insert";
|
||||||
self.pending_count = 0;
|
self.pending_count = 0;
|
||||||
@@ -1033,6 +1054,7 @@ pub const Client = struct {
|
|||||||
}
|
}
|
||||||
if (std.mem.eql(u8, text, "a")) {
|
if (std.mem.eql(u8, text, "a")) {
|
||||||
try self.applyProtocol("command move_right");
|
try self.applyProtocol("command move_right");
|
||||||
|
try self.beginInsertRepeatAfter("command move_right");
|
||||||
self.mode = .insert;
|
self.mode = .insert;
|
||||||
self.message = "insert";
|
self.message = "insert";
|
||||||
self.pending_count = 0;
|
self.pending_count = 0;
|
||||||
@@ -1040,6 +1062,7 @@ pub const Client = struct {
|
|||||||
}
|
}
|
||||||
if (std.mem.eql(u8, text, "A")) {
|
if (std.mem.eql(u8, text, "A")) {
|
||||||
try self.applyProtocol("command move_line_end");
|
try self.applyProtocol("command move_line_end");
|
||||||
|
try self.beginInsertRepeatAfter("command move_line_end");
|
||||||
self.mode = .insert;
|
self.mode = .insert;
|
||||||
self.message = "insert";
|
self.message = "insert";
|
||||||
self.pending_count = 0;
|
self.pending_count = 0;
|
||||||
@@ -1047,6 +1070,7 @@ pub const Client = struct {
|
|||||||
}
|
}
|
||||||
if (std.mem.eql(u8, text, "o")) {
|
if (std.mem.eql(u8, text, "o")) {
|
||||||
try self.applyMutatingProtocol("command open_line_below");
|
try self.applyMutatingProtocol("command open_line_below");
|
||||||
|
try self.beginInsertRepeatAfter("command open_line_below");
|
||||||
self.mode = .insert;
|
self.mode = .insert;
|
||||||
self.message = "insert";
|
self.message = "insert";
|
||||||
self.pending_count = 0;
|
self.pending_count = 0;
|
||||||
@@ -1054,6 +1078,7 @@ pub const Client = struct {
|
|||||||
}
|
}
|
||||||
if (std.mem.eql(u8, text, "O")) {
|
if (std.mem.eql(u8, text, "O")) {
|
||||||
try self.applyMutatingProtocol("command open_line_above");
|
try self.applyMutatingProtocol("command open_line_above");
|
||||||
|
try self.beginInsertRepeatAfter("command open_line_above");
|
||||||
self.mode = .insert;
|
self.mode = .insert;
|
||||||
self.message = "insert";
|
self.message = "insert";
|
||||||
self.pending_count = 0;
|
self.pending_count = 0;
|
||||||
@@ -1074,7 +1099,8 @@ pub const Client = struct {
|
|||||||
if (std.mem.eql(u8, text, "/")) return self.openSearchPrompt();
|
if (std.mem.eql(u8, text, "/")) return self.openSearchPrompt();
|
||||||
if (std.mem.eql(u8, text, "n")) return self.nextSearchMatch();
|
if (std.mem.eql(u8, text, "n")) return self.nextSearchMatch();
|
||||||
if (std.mem.eql(u8, text, "N")) return self.previousSearchMatch();
|
if (std.mem.eql(u8, text, "N")) return self.previousSearchMatch();
|
||||||
if (std.mem.eql(u8, text, "p")) return self.pasteRegister();
|
if (std.mem.eql(u8, text, "p")) return self.pasteRegister(true);
|
||||||
|
if (std.mem.eql(u8, text, ".")) return self.repeatLastEdit();
|
||||||
if (std.mem.eql(u8, text, "u")) return self.undoEdit();
|
if (std.mem.eql(u8, text, "u")) return self.undoEdit();
|
||||||
if (std.mem.eql(u8, text, "U")) return self.redoEdit();
|
if (std.mem.eql(u8, text, "U")) return self.redoEdit();
|
||||||
if (std.mem.eql(u8, text, "H") or std.mem.eql(u8, text, "0") or std.mem.eql(u8, text, "^")) return self.applyProtocol("command move_line_start");
|
if (std.mem.eql(u8, text, "H") or std.mem.eql(u8, text, "0") or std.mem.eql(u8, text, "^")) return self.applyProtocol("command move_line_start");
|
||||||
@@ -1122,6 +1148,7 @@ pub const Client = struct {
|
|||||||
.enter => try self.insertNewlineWithIndent(),
|
.enter => try self.insertNewlineWithIndent(),
|
||||||
.tab => try self.insertText(" "),
|
.tab => try self.insertText(" "),
|
||||||
.escape => {
|
.escape => {
|
||||||
|
try self.finishInsertRepeat();
|
||||||
self.mode = .normal;
|
self.mode = .normal;
|
||||||
self.message = "normal";
|
self.message = "normal";
|
||||||
},
|
},
|
||||||
@@ -1905,11 +1932,11 @@ pub const Client = struct {
|
|||||||
const text = eventText(event) orelse return self.unknownPrefixOrInput("normal");
|
const text = eventText(event) orelse return self.unknownPrefixOrInput("normal");
|
||||||
if (std.mem.eql(u8, text, "d")) {
|
if (std.mem.eql(u8, text, "d")) {
|
||||||
try self.yankCurrentLine(true);
|
try self.yankCurrentLine(true);
|
||||||
try self.applyMutatingProtocol("command delete_line");
|
try self.applyRepeatableMutatingProtocol("command delete_line");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (std.mem.eql(u8, text, "h")) return self.applyMutatingProtocol("command delete_backward");
|
if (std.mem.eql(u8, text, "h")) return self.applyRepeatableMutatingProtocol("command delete_backward");
|
||||||
if (std.mem.eql(u8, text, "l")) return self.applyMutatingProtocol("command delete_forward");
|
if (std.mem.eql(u8, text, "l")) return self.applyRepeatableMutatingProtocol("command delete_forward");
|
||||||
if (text.len == 1) {
|
if (text.len == 1) {
|
||||||
if (self.objectRangeForKey(text[0])) |range| return self.deleteRange(range) else |_| {}
|
if (self.objectRangeForKey(text[0])) |range| return self.deleteRange(range) else |_| {}
|
||||||
}
|
}
|
||||||
@@ -1921,18 +1948,21 @@ pub const Client = struct {
|
|||||||
if (std.mem.eql(u8, text, "c")) {
|
if (std.mem.eql(u8, text, "c")) {
|
||||||
try self.yankCurrentLine(false);
|
try self.yankCurrentLine(false);
|
||||||
try self.applyMutatingProtocol("command change_line");
|
try self.applyMutatingProtocol("command change_line");
|
||||||
|
try self.beginInsertRepeatAfter("command change_line");
|
||||||
self.mode = .insert;
|
self.mode = .insert;
|
||||||
self.message = "insert";
|
self.message = "insert";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (std.mem.eql(u8, text, "h")) {
|
if (std.mem.eql(u8, text, "h")) {
|
||||||
try self.applyMutatingProtocol("command delete_backward");
|
try self.applyMutatingProtocol("command delete_backward");
|
||||||
|
try self.beginInsertRepeatAfter("command delete_backward");
|
||||||
self.mode = .insert;
|
self.mode = .insert;
|
||||||
self.message = "insert";
|
self.message = "insert";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (std.mem.eql(u8, text, "l")) {
|
if (std.mem.eql(u8, text, "l")) {
|
||||||
try self.applyMutatingProtocol("command delete_forward");
|
try self.applyMutatingProtocol("command delete_forward");
|
||||||
|
try self.beginInsertRepeatAfter("command delete_forward");
|
||||||
self.mode = .insert;
|
self.mode = .insert;
|
||||||
self.message = "insert";
|
self.message = "insert";
|
||||||
return;
|
return;
|
||||||
@@ -2013,7 +2043,7 @@ pub const Client = struct {
|
|||||||
self.yank_bytes = copy;
|
self.yank_bytes = copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pasteRegister(self: *Client) !void {
|
fn pasteRegister(self: *Client, record_repeat: bool) !void {
|
||||||
const bytes = self.yank_bytes orelse {
|
const bytes = self.yank_bytes orelse {
|
||||||
self.message = "nothing yanked";
|
self.message = "nothing yanked";
|
||||||
return;
|
return;
|
||||||
@@ -2025,6 +2055,7 @@ pub const Client = struct {
|
|||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
self.noteDocumentChanged();
|
self.noteDocumentChanged();
|
||||||
|
if (record_repeat and !self.replaying_repeat) try self.setRepeatPaste();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn yankCurrentLine(self: *Client, include_newline: bool) !void {
|
fn yankCurrentLine(self: *Client, include_newline: bool) !void {
|
||||||
@@ -2068,6 +2099,79 @@ pub const Client = struct {
|
|||||||
return .{ .bytes = try self.allocator.dupe(u8, snap.bytes), .cursor_byte = snap.cursor_byte };
|
return .{ .bytes = try self.allocator.dupe(u8, snap.bytes), .cursor_byte = snap.cursor_byte };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn beginInsertRepeat(self: *Client) void {
|
||||||
|
self.beginInsertRepeatAfter(null) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn beginInsertRepeatAfter(self: *Client, before_protocol: ?[]const u8) !void {
|
||||||
|
self.insert_repeat_bytes.clearRetainingCapacity();
|
||||||
|
self.recording_insert_repeat = !self.replaying_repeat;
|
||||||
|
if (self.pending_insert_protocol) |old| self.allocator.free(old);
|
||||||
|
self.pending_insert_protocol = if (before_protocol) |line| try self.allocator.dupe(u8, line) else null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finishInsertRepeat(self: *Client) !void {
|
||||||
|
defer {
|
||||||
|
self.insert_repeat_bytes.clearRetainingCapacity();
|
||||||
|
self.recording_insert_repeat = false;
|
||||||
|
if (self.pending_insert_protocol) |old| self.allocator.free(old);
|
||||||
|
self.pending_insert_protocol = null;
|
||||||
|
}
|
||||||
|
if (!self.recording_insert_repeat or self.replaying_repeat) return;
|
||||||
|
if (self.pending_insert_protocol == null and self.insert_repeat_bytes.items.len == 0) return;
|
||||||
|
try self.setRepeatInsert(self.pending_insert_protocol, self.insert_repeat_bytes.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setRepeatInsert(self: *Client, before_protocol: ?[]const u8, text: []const u8) !void {
|
||||||
|
self.freeRepeatEdit();
|
||||||
|
const protocol_copy = if (before_protocol) |line| try self.allocator.dupe(u8, line) else null;
|
||||||
|
errdefer if (protocol_copy) |line| self.allocator.free(line);
|
||||||
|
self.last_repeat = .{ .insert = .{
|
||||||
|
.before_protocol = protocol_copy,
|
||||||
|
.text = try self.allocator.dupe(u8, text),
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setRepeatProtocol(self: *Client, line: []const u8) !void {
|
||||||
|
self.freeRepeatEdit();
|
||||||
|
self.last_repeat = .{ .protocol = try self.allocator.dupe(u8, line) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setRepeatPaste(self: *Client) !void {
|
||||||
|
self.freeRepeatEdit();
|
||||||
|
self.last_repeat = .paste;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn freeRepeatEdit(self: *Client) void {
|
||||||
|
if (self.last_repeat) |repeat| switch (repeat) {
|
||||||
|
.insert => |insert| {
|
||||||
|
if (insert.before_protocol) |line| self.allocator.free(line);
|
||||||
|
self.allocator.free(insert.text);
|
||||||
|
},
|
||||||
|
.protocol => |line| self.allocator.free(line),
|
||||||
|
.paste => {},
|
||||||
|
};
|
||||||
|
self.last_repeat = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn repeatLastEdit(self: *Client) !void {
|
||||||
|
const repeat = self.last_repeat orelse {
|
||||||
|
self.message = "nothing to repeat";
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.replaying_repeat = true;
|
||||||
|
defer self.replaying_repeat = false;
|
||||||
|
switch (repeat) {
|
||||||
|
.insert => |insert| {
|
||||||
|
if (insert.before_protocol) |line| try self.applyMutatingProtocol(line);
|
||||||
|
if (insert.text.len != 0) try self.insertText(insert.text);
|
||||||
|
},
|
||||||
|
.protocol => |line| try self.applyMutatingProtocol(line),
|
||||||
|
.paste => try self.pasteRegister(false),
|
||||||
|
}
|
||||||
|
self.message = "repeat";
|
||||||
|
}
|
||||||
|
|
||||||
fn objectRangeForKey(self: *Client, key: u8) !ObjectRange {
|
fn objectRangeForKey(self: *Client, key: u8) !ObjectRange {
|
||||||
return switch (key) {
|
return switch (key) {
|
||||||
'w' => try self.wordRange(),
|
'w' => try self.wordRange(),
|
||||||
@@ -2171,6 +2275,7 @@ pub const Client = struct {
|
|||||||
self.dropLastUndoSnapshot();
|
self.dropLastUndoSnapshot();
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
|
if (self.recording_insert_repeat and !self.replaying_repeat) try self.insert_repeat_bytes.appendSlice(self.allocator, text);
|
||||||
self.noteDocumentChanged();
|
self.noteDocumentChanged();
|
||||||
self.message = null;
|
self.message = null;
|
||||||
}
|
}
|
||||||
@@ -2213,6 +2318,11 @@ pub const Client = struct {
|
|||||||
self.noteDocumentChanged();
|
self.noteDocumentChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn applyRepeatableMutatingProtocol(self: *Client, line: []const u8) !void {
|
||||||
|
try self.applyMutatingProtocol(line);
|
||||||
|
if (!self.replaying_repeat) try self.setRepeatProtocol(line);
|
||||||
|
}
|
||||||
|
|
||||||
const PageDirection = enum { up, down };
|
const PageDirection = enum { up, down };
|
||||||
|
|
||||||
fn halfPage(self: *Client, direction: PageDirection) !void {
|
fn halfPage(self: *Client, direction: PageDirection) !void {
|
||||||
@@ -5185,3 +5295,61 @@ test "regular: vertical viewport follows cursor line" {
|
|||||||
try std.testing.expect(std.mem.indexOf(u8, frame, "12│") != null);
|
try std.testing.expect(std.mem.indexOf(u8, frame, "12│") != null);
|
||||||
try std.testing.expect(std.mem.indexOf(u8, frame, " 1│") == null);
|
try std.testing.expect(std.mem.indexOf(u8, frame, " 1│") == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "regular: dot repeats insert session" {
|
||||||
|
var client = try Client.init(std.testing.allocator, .{ .width = 40, .height = 8 });
|
||||||
|
defer client.deinit();
|
||||||
|
|
||||||
|
try client.handleTraceLine("open alpha beta");
|
||||||
|
try client.handleInput("i");
|
||||||
|
try client.handleInput("X");
|
||||||
|
try client.handleInput("\x1b");
|
||||||
|
try client.handleInput("w");
|
||||||
|
try client.handleInput(".");
|
||||||
|
const snap = try client.session.snapshot();
|
||||||
|
try std.testing.expectEqualStrings("Xalpha Xbeta", snap.bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "regular: dot repeats delete command" {
|
||||||
|
var client = try Client.init(std.testing.allocator, .{ .width = 40, .height = 8 });
|
||||||
|
defer client.deinit();
|
||||||
|
|
||||||
|
try client.handleTraceLine("open abcd");
|
||||||
|
try client.handleInput("d");
|
||||||
|
try client.handleInput("l");
|
||||||
|
try client.handleInput(".");
|
||||||
|
const snap = try client.session.snapshot();
|
||||||
|
try std.testing.expectEqualStrings("cd", snap.bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "regular: dot repeats simple change command and typed text" {
|
||||||
|
var client = try Client.init(std.testing.allocator, .{ .width = 40, .height = 8 });
|
||||||
|
defer client.deinit();
|
||||||
|
|
||||||
|
try client.handleTraceLine("open abcd");
|
||||||
|
try client.handleInput("c");
|
||||||
|
try client.handleInput("l");
|
||||||
|
try client.handleInput("X");
|
||||||
|
try client.handleInput("\x1b");
|
||||||
|
try client.handleInput(".");
|
||||||
|
const snap = try client.session.snapshot();
|
||||||
|
try std.testing.expectEqualStrings("XXcd", snap.bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "regular: dot repeats put and remains undoable" {
|
||||||
|
var client = try Client.init(std.testing.allocator, .{ .width = 40, .height = 8 });
|
||||||
|
defer client.deinit();
|
||||||
|
|
||||||
|
try client.handleTraceLine("open abc");
|
||||||
|
try client.handleInput("y");
|
||||||
|
try client.handleInput("w");
|
||||||
|
try client.handleInput("G");
|
||||||
|
try client.handleInput("p");
|
||||||
|
try client.handleInput(".");
|
||||||
|
var snap = try client.session.snapshot();
|
||||||
|
try std.testing.expectEqualStrings("abcabcabc", snap.bytes);
|
||||||
|
|
||||||
|
try client.handleInput("u");
|
||||||
|
snap = try client.session.snapshot();
|
||||||
|
try std.testing.expectEqualStrings("abcabc", snap.bytes);
|
||||||
|
}
|
||||||
|
|||||||
@@ -515,6 +515,7 @@ SCENARIOS = [
|
|||||||
Scenario("normal-A-append", 56, 14, "existing", "attached-keyboard", "append-eol", "truecolor", "file", setup_existing, (b"A", b"!", b"\x1b", b" ", b"w", b" ", b"q"), "alpha!\nbeta", ("1│", "☻", "alpha")),
|
Scenario("normal-A-append", 56, 14, "existing", "attached-keyboard", "append-eol", "truecolor", "file", setup_existing, (b"A", b"!", b"\x1b", b" ", b"w", b" ", b"q"), "alpha!\nbeta", ("1│", "☻", "alpha")),
|
||||||
Scenario("insert-enter-indent", 56, 14, "indented", "attached-keyboard", "newline-indent", "truecolor", "file", setup_indented, (b"A", b"\r", b"x", b"\x1b", b" ", b"w", b" ", b"q"), " item\n x", ("1│", "2│", "☻")),
|
Scenario("insert-enter-indent", 56, 14, "indented", "attached-keyboard", "newline-indent", "truecolor", "file", setup_indented, (b"A", b"\r", b"x", b"\x1b", b" ", b"w", b" ", b"q"), " item\n x", ("1│", "2│", "☻")),
|
||||||
Scenario("insert-tab-expands-spaces", 56, 14, "tabbed", "attached-keyboard", "tab-indent", "truecolor", "file", setup_tabbed, (b"A", b"\t", b"x", b"\x1b", b" ", b"w", b" ", b"q"), "\titem x", ("1│", "☻")),
|
Scenario("insert-tab-expands-spaces", 56, 14, "tabbed", "attached-keyboard", "tab-indent", "truecolor", "file", setup_tabbed, (b"A", b"\t", b"x", b"\x1b", b" ", b"w", b" ", b"q"), "\titem x", ("1│", "☻")),
|
||||||
|
Scenario("dot-repeat-local-edit", 56, 12, "short", "attached-keyboard", "repeat", "truecolor", "file", setup_short, (b"i", b"X", b"\x1b", b"w", b".", b" ", b"w", b" ", b"q"), "XabcdefX", ("mode:normal", "☻")),
|
||||||
Scenario("page-keys-move-cursor", 56, 10, "long", "attached-keyboard", "page-keys", "truecolor", "file", setup_long, (b"\x1b[6~", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "l1\nl2\nl3\nl4\nl5\nl6\nl7\nXl8", ("8│", "☻")),
|
Scenario("page-keys-move-cursor", 56, 10, "long", "attached-keyboard", "page-keys", "truecolor", "file", setup_long, (b"\x1b[6~", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "l1\nl2\nl3\nl4\nl5\nl6\nl7\nXl8", ("8│", "☻")),
|
||||||
Scenario("counted-move-inserts-at-count", 56, 12, "short", "attached-keyboard", "counts", "truecolor", "file", setup_short, (b"3", b"l", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "abcXdef", ("1│", "☻")),
|
Scenario("counted-move-inserts-at-count", 56, 12, "short", "attached-keyboard", "counts", "truecolor", "file", setup_short, (b"3", b"l", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "abcXdef", ("1│", "☻")),
|
||||||
Scenario("percent-match-jump", 56, 12, "brackets", "attached-keyboard", "match-jump", "truecolor", "file", setup_brackets, (b"%", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "(abX)", ("1│", "☻")),
|
Scenario("percent-match-jump", 56, 12, "brackets", "attached-keyboard", "match-jump", "truecolor", "file", setup_brackets, (b"%", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "(abX)", ("1│", "☻")),
|
||||||
|
|||||||
Reference in New Issue
Block a user