Vertical movement crosses lines of different lengths, so the column has to be reined in when the new line is shorter. Today you add up and down movement that clamps the column to the target line.
Move the cursor up and down between lines, clamping the column to the length of the line it lands on.
Moving up or down changes the row, but the column raises a question the horizontal moves never did: the new line might be shorter than where the column currently sits. Dropping from a long line onto a short one has to pull the column back to that line’s end, or the cursor would hover in empty space past the text and produce a bogus offset. So each vertical move, after changing the row, clamps the column to the new line’s length.
Like horizontal movement, vertical movement clamps at the document’s edges too:
MoveUp from the top line stays put, MoveDown from the bottom line stays put.
This is the deliberately simple model - the column is clamped and left there, with
no “remembered” goal column that would restore your original column when you pass
back through a long line. That refinement exists in polished editors, but the
plain clamp is correct, predictable, and exactly one idea.
func (e *Editor) MoveDown() {if e.Row < e.Buf.LineCount()-1 { e.Row++; e.clampCol() }}func (e *Editor) clampCol() {if e.Col > e.Buf.LineLen(e.Row) { e.Col = e.Buf.LineLen(e.Row) }}