feat: show relative line numbers around primary cursor

This commit is contained in:
Valdemar Lorenzen
2026-10-08 11:23:52 +02:00
parent a3a0da2586
commit bd78656691
2 changed files with 28 additions and 3 deletions
+1 -1
View File
@@ -91,7 +91,7 @@ The terminal adapter owns raw mode, alternate-screen state, bracketed paste, and
The TUI uses Ratatui constraint-based layout for a flexible buffer viewport, a one-row status bar, and a one-row prompt/message area. Scrolling, clipping, highlights, and cursor placement use the allocated regions, not terminal-wide offsets. Below three rows the status bar is hidden; at one row the buffer remains visible unless an active command/search prompt takes priority. Zero-sized terminals render nothing. Layout and terminal-cell text mapping remain frontend-owned.
The buffer viewport includes a fixed left gutter with right-aligned, one-based absolute line numbers and a highlighted primary cursor line number. Gutter width follows the total line count plus one separator cell, including an empty final row after a trailing newline. Horizontal scrolling affects only text; search/selection highlights and carets never paint over the gutter. Rows beyond the buffer have no number. The gutter is hidden when it would leave no text column.
The buffer viewport includes a fixed left gutter with right-aligned hybrid line numbers: the highlighted primary cursor line shows its one-based absolute number; every other line shows its distance from the primary. Secondary carets do not establish separate numbering origins. Gutter width follows the total line count plus one separator cell, including an empty final row after a trailing newline. Horizontal scrolling affects only text; search/selection highlights and carets never paint over the gutter. Rows beyond the buffer have no number. The gutter is hidden when it would leave no text column.
Built-in Ex commands register as the protected `core` feature in the same command registry used by plugins. External plugins cannot claim core status, replace existing commands, or disable core. User configuration can override any keybinding without replacing the core implementation. `:plugins` lists active features and frontend capabilities.
+27 -2
View File
@@ -246,7 +246,12 @@ fn draw(frame: &mut Frame, editor: &Editor, top: &mut usize, left: &mut usize) {
} else {
Style::default().fg(Color::DarkGray)
};
Line::styled(format!("{:>digits$} ", row + 1), style)
let number = if row == cursor.row {
row + 1
} else {
row.abs_diff(cursor.row)
};
Line::styled(format!("{number:>digits$} "), style)
})
.collect();
frame.render_widget(Paragraph::new(numbers), gutter);
@@ -509,7 +514,7 @@ mod tests {
.unwrap();
assert_eq!((top, left), (8, 8));
let buffer = terminal.backend().buffer();
for (y, expected) in [(2, " 9 "), (3, "10 "), (4, "11 ")] {
for (y, expected) in [(2, " 2 "), (3, " 1 "), (4, "11 ")] {
let number: String = (3..6).map(|x| buffer[(x, y)].symbol()).collect();
assert_eq!(number, expected);
assert_eq!(buffer[(6, y)].symbol(), "8");
@@ -533,6 +538,26 @@ mod tests {
terminal.get_cursor_position().unwrap(),
ratatui::layout::Position::new(6, 4)
);
editor.handle(Input::Up);
editor.handle(Input::Up);
terminal
.draw(|frame| draw(frame, &editor, &mut top, &mut left))
.unwrap();
let buffer = terminal.backend().buffer();
for (y, expected) in [(2, "10 "), (3, " 1 "), (4, " 2 ")] {
let number: String = (3..6).map(|x| buffer[(x, y)].symbol()).collect();
assert_eq!(number, expected);
}
editor.handle(Input::Ctrl('e'));
assert_eq!(editor.selection_count(), 2);
terminal
.draw(|frame| draw(frame, &editor, &mut top, &mut left))
.unwrap();
let buffer = terminal.backend().buffer();
for (y, expected) in [(2, " 1 "), (3, "11 "), (4, " 1 ")] {
let number: String = (3..6).map(|x| buffer[(x, y)].symbol()).collect();
assert_eq!(number, expected);
}
}
#[test]