Handling the wanted column
When we move vertically, the cursor keeps its current column. That's pretty
obvious. But when it moves to a shorter line, if we don't keep track of the
previous value, it will keep moving along the shorter line, instead we want to
move along the same column from where we started. That is the wanted column,
and in our View
type is the cwant
field.
This variable should be:
-
restored when moving vertically, either with arrow keys or by page
-
set to the current column when moving left or right, or to the beginning of the line (Home key), or after typing/deleting something
-
when using the End key, it should be set to a special value that means: always stick to the end of the line when moving vertically
The special value we use is std.math.maxInt(usize)
, which we store in
a constant:
const maxUsize = std.math.maxInt(usize);
The Cwant enum
These different behaviors are listed in an enum
, which will go in our types
module:
/// Controls handling of the wanted column.
pub const Cwant = enum(u8) {
/// To set cwant to a new value
set,
/// To restore current cwant, or to the last column if too big
restore,
/// To set cwant to maxUsize, which means 'always the last column'
maxcol,
};
The doCwant()
function
Differently from the original kilo
editor, here the cwant
field will track
the rendered column, not the real one, which makes more sense in an editor.
///////////////////////////////////////////////////////////////////////////////
//
// View operations
//
///////////////////////////////////////////////////////////////////////////////
/// Handle wanted column. `want` can be:
/// .set: set e.view.cwant to a new value
/// .maxcol: set to maxUsize, which means 'always the last column'
/// .restore: set current column to cwant, or to the last column if too big
fn doCwant(e: *Editor, want: t.Cwant) void {
const V = &e.view;
const numrows = e.buffer.rows.items.len;
switch (want) {
// code to come...
}
}
So when we set cwant
, we assign it to the current column of the rendered
row.
If want
is .maxcol
, we set cwant
to our special value.
.set => {
V.cwant = if (V.cy < numrows) e.currentRow().cxToRx(V.cx) else 0;
},
.maxcol => {
V.cwant = maxUsize;
},
When we restore it, since cwant
is an index in the rendered row,
we use rxToCx()
to find out the real column, to which cx
must be set.
When we restore cwant
, we'll check if we can actually restore it. If the
length of the current row is shorter, the cursor will be moved to the last
column.
If the value of cwant
is our special value, the cursor will always be placed
in the last column, even if the starting line was shorter than the following
ones.
.restore => {
if (V.cy == numrows) { // past end of file
V.cx = 0;
}
else if (V.cwant == maxUsize) { // wants end of line
V.cx = e.currentRow().clen();
}
else {
const row = e.currentRow();
const rowlen = row.clen();
if (rowlen == 0) {
V.cx = 0;
}
else {
// cwant is an index of the rendered column, must convert
V.cx = row.rxToCx(V.cwant);
if (V.cx > rowlen) {
V.cx = rowlen;
}
}
}
},
Calls to doCwant()
Where should the wanted column be handled? Right in the processKeypress()
function. You'll have to add calls to doCwant()
as follows:
// after handling <ctrl-d>, <ctrl-u>, <page-up>, <page-down>
e.doCwant(.restore);
// after handling <up>, <down>
e.doCwant(.restore);
// after handling <left>, <right> and <home>
e.doCwant(.set);
// after handling <end>
e.doCwant(.maxcol);