diff --git a/local/recipes/tui/tlc/source/src/viewer/mod.rs b/local/recipes/tui/tlc/source/src/viewer/mod.rs index 67db3661d1..53dcf68d6b 100644 --- a/local/recipes/tui/tlc/source/src/viewer/mod.rs +++ b/local/recipes/tui/tlc/source/src/viewer/mod.rs @@ -816,7 +816,7 @@ impl Viewer { let label = match self.prompt { Some(ViewerPrompt::Search) => " Search: ", Some(ViewerPrompt::SearchBackward) => " Search backward: ", - Some(ViewerPrompt::GotoLine) => " Goto line: ", + Some(ViewerPrompt::GotoLine) => " Goto (line/50%/0x): ", Some(ViewerPrompt::SaveBeforeQuit) => " Save before quit? (Y/N/Esc) ", None => return, }; @@ -1202,13 +1202,42 @@ impl Viewer { } } ViewerPrompt::GotoLine => { - if let Ok(n) = text.trim().parse::() { - if n >= 1 { - if let Ok(target) = self.goto_line(n) { + let trimmed = text.trim(); + // Percent: "50%" → goto 50% of the file. + if let Some(pct) = trimmed.strip_suffix('%') { + if let Ok(n) = pct.parse::() { + if n <= 100 { + if let Ok(target) = self.goto_percent(n) { + self.cursor = target.offset; + self.top = target.line.saturating_sub(1); + } + } + } + // Hex offset: "0x400" → goto byte offset 1024. + } else if trimmed.starts_with("0x") || trimmed.starts_with("0X") { + if let Ok(off) = u64::from_str_radix(&trimmed[2..], 16) { + if let Ok(target) = self.goto_offset(off) { self.cursor = target.offset; self.top = target.line.saturating_sub(1); } } + // Decimal offset: plain number → goto byte offset + // if it's larger than the file's line count, else + // goto line. + } else if let Ok(n) = trimmed.parse::() { + if n >= 1 { + if n > self.goto.line_count() { + if let Ok(target) = self.goto_offset(n) { + self.cursor = target.offset; + self.top = target.line.saturating_sub(1); + } + } else { + if let Ok(target) = self.goto_line(n) { + self.cursor = target.offset; + self.top = target.line.saturating_sub(1); + } + } + } } } ViewerPrompt::SaveBeforeQuit => { @@ -1849,6 +1878,21 @@ mod tests { assert!(!v.ruler); } + #[test] + fn goto_accepts_percent_and_offset() { + // Use a 100-line file so 50% = line 50. + let lines: Vec = (1..=100).map(|i| format!("line{i}")).collect(); + let content = lines.join("\n"); + let p = make_text("goto-fmt.txt", content.as_bytes()); + let mut v = Viewer::open(&p).unwrap(); + // Open goto prompt. + v.prompt = Some(ViewerPrompt::GotoLine); + v.prompt_input = "50%".to_string(); + // Activate the prompt — the commit handler processes it. + v.handle_prompt_key(Key::ENTER); + assert!(v.top >= 49 && v.top <= 51, "50% should land near line 50, got top={}", v.top); + } + fn k(c: char) -> Key { Key { code: c as u32, mods: crate::key::Modifiers::empty() } }