Deleting a character
By deleting a character, we mean deleting the character to the left of our cursor, what the Backspace key normally does.
/// Delete a character before cursor position (backspace).
fn deleteChar(e: *Editor) !void {
const V = &e.view;
const B = &e.buffer;
// code to come...
}
We'll want to handle different cases:
Cursor is past the end of file: move to the end of the previous line, don't return, we will possibly delete a character.
// past the end of the file
if (V.cy == B.rows.items.len) {
e.moveCursorWithKey(.left);
}
Cursor at the start of the file: nothing to do.
// start of file
if (V.cx == 0 and V.cy == 0) {
return;
}
Cursor after the first column: delete the character at column before the current one.
// delete character in current line
if (V.cx > 0) {
try e.rowDelChar(V.cy, V.cx - 1);
V.cx -= 1;
}
Cursor is at the start of a line which isn't the first one: we'll append the current line to the previous one, then delete the current row. The cursor will then be moved to the row above, at a column that is the length of the previous row before the lines were joined.
// join with previous line
else {
V.cx = B.rows.items[V.cy - 1].clen();
try e.rowInsertString(V.cy - 1, V.cx, e.currentRow().chars.items);
e.deleteRow(V.cy);
V.cy -= 1;
}
rowDelChar()
For the actual character deletion we write rowDelChar()
, which closely
resembles rowInsertChar()
:
/// Delete a character in the row with index `ix`, at column `at`.
fn rowDelChar(e: *Editor, ix: usize, at: usize) !void {
_ = e.rowAt(ix).chars.orderedRemove(at);
try e.updateRow(ix);
e.buffer.dirty = true;
}
rowInsertString()
In case we want to join lines, we'll need two new functions.
/// Insert a string at position `at`, in the row at index `ix`.
fn rowInsertString(e: *Editor, ix: usize, at: usize, chars: []const u8) !void {
try e.rowAt(ix).chars.insertSlice(e.buffer.alc, at, chars);
try e.updateRow(ix);
e.buffer.dirty = true;
}
This is very similar to rowInsertChar()
, but inserts a slice instead of
inserting a character. Here we're just appending at the end of the row, since
we're passing an at
argument that is equal to the length of the row.
deleteRow()
The last function we need for now is the one that deletes a row from the
Buffer. I put this function below insertRow()
.
As mentioned when we talked about the Buffer type, we're sometimes deinitializing individual rows in the Editor methods, which isn't ideal, but I don't think that creating a method in Buffer just for this is that much better. We can access the Buffer allocator just fine, but we must remember that a Row uses the Buffer allocator, not the Editor one. It's only happening right now that both Editor and Buffer use the same allocator, but things might change in the future.
/// Delete a row and deinitialize it.
fn deleteRow(e: *Editor, ix: usize) void {
var row = e.buffer.rows.orderedRemove(ix);
row.deinit(e.buffer.alc);
e.buffer.dirty = true;
}