Skip to content

Commit 5a62b2e

Browse files
jgarzikclaude
andcommitted
vi: Implement :set number line display
Display line numbers with "%6d " format (8 chars) when number option is enabled. Adjusts available columns and cursor positioning accordingly. Fixes #530 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent d927425 commit 5a62b2e

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

editors/tests/pty/mod.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,57 @@ fn test_pty_vi_utf8_display() {
272272
let contents = std::fs::read_to_string(&file_path).unwrap();
273273
assert_eq!(contents, "Привет мир\n");
274274
}
275+
276+
/// Test: `:set number` displays line numbers without panic.
277+
/// Regression test for issue #530.
278+
#[test]
279+
fn test_pty_vi_set_number() {
280+
let td = tempdir().unwrap();
281+
let file_path = td.path().join("test_number.txt");
282+
std::fs::write(&file_path, "line1\nline2\nline3\n").unwrap();
283+
284+
let pty_system = native_pty_system();
285+
let pair = pty_system
286+
.openpty(PtySize {
287+
rows: 25,
288+
cols: 80,
289+
pixel_width: 0,
290+
pixel_height: 0,
291+
})
292+
.unwrap();
293+
294+
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_vi"));
295+
cmd.arg(&file_path);
296+
cmd.env("TERM", "vt100");
297+
298+
let mut child = pair.slave.spawn_command(cmd).unwrap();
299+
drop(pair.slave);
300+
301+
let reader = pair.master.try_clone_reader().unwrap();
302+
let _reader_thread = spawn_reader_drain(reader);
303+
let mut writer = pair.master.take_writer().unwrap();
304+
305+
// Wait for vi startup
306+
thread::sleep(Duration::from_millis(500));
307+
308+
// Enable line numbers
309+
write_keys(&mut writer, ":set number\r");
310+
thread::sleep(Duration::from_millis(200));
311+
312+
// Move cursor to verify positioning works with line numbers
313+
write_keys(&mut writer, "jjk");
314+
thread::sleep(Duration::from_millis(100));
315+
316+
// Disable line numbers
317+
write_keys(&mut writer, ":set nonumber\r");
318+
thread::sleep(Duration::from_millis(100));
319+
320+
// Quit without saving
321+
write_keys(&mut writer, ":q!\r");
322+
323+
wait_with_timeout(&mut child, Duration::from_secs(5));
324+
325+
// File should be unchanged
326+
let contents = std::fs::read_to_string(&file_path).unwrap();
327+
assert_eq!(contents, "line1\nline2\nline3\n");
328+
}

editors/vi/editor.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3013,6 +3013,8 @@ impl Editor {
30133013

30143014
/// Refresh the screen.
30153015
fn refresh_screen(&mut self) -> Result<()> {
3016+
const LINE_NUMBER_WIDTH: usize = 8; // "%6d " format
3017+
30163018
let size = self.terminal.size();
30173019
self.screen.resize(size);
30183020
self.screen
@@ -3032,8 +3034,17 @@ impl Editor {
30323034

30333035
if line_num <= self.buffer.line_count() {
30343036
if let Some(line) = self.buffer.line(line_num) {
3037+
// Line number prefix when option set
3038+
if self.options.number {
3039+
self.terminal.write_str(&format!("{:6} ", line_num))?;
3040+
}
3041+
let avail_cols = if self.options.number {
3042+
(size.cols as usize).saturating_sub(LINE_NUMBER_WIDTH)
3043+
} else {
3044+
size.cols as usize
3045+
};
30353046
// Expand tabs and truncate (expand_line already caps at max_cols)
3036-
let content = self.screen.expand_line(line.content(), size.cols as usize);
3047+
let content = self.screen.expand_line(line.content(), avail_cols);
30373048
self.terminal.write_str(&content)?;
30383049
}
30393050
} else {
@@ -3058,6 +3069,12 @@ impl Editor {
30583069
cursor.column,
30593070
) as u16
30603071
+ 1;
3072+
// Add line number offset
3073+
let display_col = if self.options.number {
3074+
display_col + LINE_NUMBER_WIDTH as u16
3075+
} else {
3076+
display_col
3077+
};
30613078

30623079
if self.mode == Mode::Ex {
30633080
// Cursor at command line

0 commit comments

Comments
 (0)