Separate first nonblank line motion
This commit is contained in:
@@ -96,6 +96,7 @@ fn commandResponse(allocator: std.mem.Allocator, session: *session_mod.Session,
|
||||
if (std.mem.eql(u8, command, "move_word_forward")) return dispatchAndRespond(allocator, session, .move_word_forward);
|
||||
if (std.mem.eql(u8, command, "move_word_back")) return dispatchAndRespond(allocator, session, .move_word_back);
|
||||
if (std.mem.eql(u8, command, "move_word_end")) return dispatchAndRespond(allocator, session, .move_word_end);
|
||||
if (std.mem.eql(u8, command, "move_line_first_nonblank")) return dispatchAndRespond(allocator, session, .move_line_first_nonblank);
|
||||
if (std.mem.eql(u8, command, "move_line_start")) return dispatchAndRespond(allocator, session, .move_line_start);
|
||||
if (std.mem.eql(u8, command, "move_line_end")) return dispatchAndRespond(allocator, session, .move_line_end);
|
||||
if (std.mem.eql(u8, command, "move_document_start")) return dispatchAndRespond(allocator, session, .move_document_start);
|
||||
@@ -489,3 +490,19 @@ test "adversarial: protocol list errors are explicit" {
|
||||
try std.testing.expectEqualStrings("err no panel open\n", response);
|
||||
}
|
||||
}
|
||||
|
||||
test "regular: protocol exposes first-nonblank line motion" {
|
||||
var session = session_mod.Session.init(std.testing.allocator);
|
||||
defer session.deinit();
|
||||
|
||||
var response = try handleLine(std.testing.allocator, &session, "open top\n alpha\n");
|
||||
std.testing.allocator.free(response);
|
||||
response = try handleLine(std.testing.allocator, &session, "command move_down\n");
|
||||
std.testing.allocator.free(response);
|
||||
response = try handleLine(std.testing.allocator, &session, "command move_line_end\n");
|
||||
std.testing.allocator.free(response);
|
||||
response = try handleLine(std.testing.allocator, &session, "command move_line_first_nonblank\n");
|
||||
defer std.testing.allocator.free(response);
|
||||
|
||||
try std.testing.expectEqualStrings("ok state cursor_byte=6 cursor_cell=2 bytes_len=11 panel_depth=0 active_panel=- panel_path=- panel_summary=-\n", response);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ pub const Command = union(enum) {
|
||||
move_word_forward,
|
||||
move_word_back,
|
||||
move_word_end,
|
||||
move_line_first_nonblank,
|
||||
insert: []const u8,
|
||||
insert_pair: Pair,
|
||||
delete_backward,
|
||||
@@ -95,6 +96,7 @@ pub const Buffer = struct {
|
||||
.move_word_forward => self.moveWordForward(),
|
||||
.move_word_back => self.moveWordBack(),
|
||||
.move_word_end => self.moveWordEnd(),
|
||||
.move_line_first_nonblank => self.moveLineFirstNonblank(),
|
||||
.insert => |text| try self.insert(text),
|
||||
.insert_pair => |pair| try self.insertPair(pair),
|
||||
.delete_backward => self.deleteBackward(),
|
||||
@@ -267,6 +269,14 @@ pub const Buffer = struct {
|
||||
self.refreshCellResetPreferred();
|
||||
}
|
||||
|
||||
pub fn moveLineFirstNonblank(self: *Buffer) void {
|
||||
const range = self.currentLineRange(false);
|
||||
var at = range.start;
|
||||
while (at < range.end and (self.bytes.items[at] == ' ' or self.bytes.items[at] == '\t')) : (at += 1) {}
|
||||
self.cursor.byte = at;
|
||||
self.refreshCellResetPreferred();
|
||||
}
|
||||
|
||||
pub fn moveDocumentStart(self: *Buffer) void {
|
||||
self.cursor.byte = 0;
|
||||
self.refreshCellResetPreferred();
|
||||
@@ -795,3 +805,33 @@ test "regular: document motions jump to top and bottom line" {
|
||||
try std.testing.expectEqual(@as(usize, 0), snap.cursor_byte);
|
||||
try std.testing.expectEqual(@as(usize, 0), snap.cursor_cell);
|
||||
}
|
||||
|
||||
test "regular: line first-nonblank is distinct from absolute line start" {
|
||||
var buffer = try Buffer.openFromBytes(std.testing.allocator, "top\n \talpha\nend");
|
||||
defer buffer.deinit();
|
||||
|
||||
buffer.moveDown();
|
||||
buffer.moveLineEnd();
|
||||
try std.testing.expectEqual(@as(usize, 12), buffer.cursor.byte);
|
||||
|
||||
buffer.moveLineStart();
|
||||
try std.testing.expectEqual(@as(usize, 4), buffer.cursor.byte);
|
||||
|
||||
buffer.moveLineEnd();
|
||||
buffer.moveLineFirstNonblank();
|
||||
try std.testing.expectEqual(@as(usize, 7), buffer.cursor.byte);
|
||||
try std.testing.expectEqual(@as(usize, 2), buffer.cursor.cell);
|
||||
}
|
||||
|
||||
test "adversarial: line first-nonblank on blank or whitespace-only line goes to line end" {
|
||||
var buffer = try Buffer.openFromBytes(std.testing.allocator, "a\n \n\t\tb");
|
||||
defer buffer.deinit();
|
||||
|
||||
buffer.moveDown();
|
||||
buffer.moveLineFirstNonblank();
|
||||
try std.testing.expectEqual(@as(usize, 4), buffer.cursor.byte);
|
||||
|
||||
buffer.moveDown();
|
||||
buffer.moveLineFirstNonblank();
|
||||
try std.testing.expectEqual(@as(usize, 7), buffer.cursor.byte);
|
||||
}
|
||||
|
||||
+4
-2
@@ -1115,7 +1115,8 @@ pub const Client = struct {
|
||||
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.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")) return self.applyProtocol("command move_line_start");
|
||||
if (std.mem.eql(u8, text, "^")) return self.applyProtocol("command move_line_first_nonblank");
|
||||
if (std.mem.eql(u8, text, "L") or std.mem.eql(u8, text, "$")) return self.applyProtocol("command move_line_end");
|
||||
if (std.mem.eql(u8, text, "%")) return self.jumpToMatch(null);
|
||||
if (std.mem.eql(u8, text, "m")) return self.openPrefix(.match);
|
||||
@@ -1186,7 +1187,8 @@ pub const Client = struct {
|
||||
return;
|
||||
}
|
||||
if (std.mem.eql(u8, text, "%")) return self.extendSelectionWithJump(null);
|
||||
if (std.mem.eql(u8, text, "H") or std.mem.eql(u8, text, "0") or std.mem.eql(u8, text, "^")) return self.extendSelectionWithProtocol("command move_line_start");
|
||||
if (std.mem.eql(u8, text, "H") or std.mem.eql(u8, text, "0")) return self.extendSelectionWithProtocol("command move_line_start");
|
||||
if (std.mem.eql(u8, text, "^")) return self.extendSelectionWithProtocol("command move_line_first_nonblank");
|
||||
if (std.mem.eql(u8, text, "L") or std.mem.eql(u8, text, "$")) return self.extendSelectionWithProtocol("command move_line_end");
|
||||
if (std.mem.eql(u8, text, "j")) return self.repeatSelectionProtocol("command move_down", self.takeRepeat());
|
||||
if (std.mem.eql(u8, text, "k")) return self.repeatSelectionProtocol("command move_up", self.takeRepeat());
|
||||
|
||||
Reference in New Issue
Block a user