1
0
mirror of https://github.com/sharkdp/bat.git synced 2025-09-03 11:52:26 +01:00

feat: add context support to line-range syntax

docs(long-help.txt): 📚 add examples for N::C and N:M:C context syntax
docs(clap_app.rs): 📚 update CLI help text with context syntax examples
feat(line_range.rs):  implement N::C and N:M:C parsing and add tests
This commit is contained in:
John Cavanaugh
2025-07-15 16:17:09 -07:00
parent 1ad294dcd3
commit 6c13a98f01
3 changed files with 77 additions and 2 deletions

View File

@@ -98,8 +98,33 @@ impl LineRange {
new_range.upper = RangeBound::Absolute(upper_absolute_bound);
Ok(new_range)
}
3 => {
// Handle context syntax: N::C or N:M:C
if line_numbers[1].is_empty() {
// Format: N::C - single line with context
let line_number: usize = line_numbers[0].parse()
.map_err(|_| "Invalid line number in N::C format")?;
let context: usize = line_numbers[2].parse()
.map_err(|_| "Invalid context number in N::C format")?;
new_range.lower = RangeBound::Absolute(line_number.saturating_sub(context));
new_range.upper = RangeBound::Absolute(line_number.saturating_add(context));
} else {
// Format: N:M:C - range with context
let start_line: usize = line_numbers[0].parse()
.map_err(|_| "Invalid start line number in N:M:C format")?;
let end_line: usize = line_numbers[1].parse()
.map_err(|_| "Invalid end line number in N:M:C format")?;
let context: usize = line_numbers[2].parse()
.map_err(|_| "Invalid context number in N:M:C format")?;
new_range.lower = RangeBound::Absolute(start_line.saturating_sub(context));
new_range.upper = RangeBound::Absolute(end_line.saturating_add(context));
}
Ok(new_range)
}
_ => Err(
"Line range contained more than one ':' character. Expected format: 'N' or 'N:M'"
"Line range contained too many ':' characters. Expected format: 'N', 'N:M', 'N::C', or 'N:M:C'"
.into(),
),
}
@@ -274,6 +299,52 @@ fn test_parse_minus_fail() {
assert!(range.is_err());
}
#[test]
fn test_parse_context_single_line() {
let range = LineRange::from("35::5").expect("Shouldn't fail on test!");
assert_eq!(RangeBound::Absolute(30), range.lower);
assert_eq!(RangeBound::Absolute(40), range.upper);
}
#[test]
fn test_parse_context_range() {
let range = LineRange::from("30:40:2").expect("Shouldn't fail on test!");
assert_eq!(RangeBound::Absolute(28), range.lower);
assert_eq!(RangeBound::Absolute(42), range.upper);
}
#[test]
fn test_parse_context_edge_cases() {
// Test with small line numbers that would underflow
let range = LineRange::from("5::10").expect("Shouldn't fail on test!");
assert_eq!(RangeBound::Absolute(0), range.lower);
assert_eq!(RangeBound::Absolute(15), range.upper);
// Test with zero context
let range = LineRange::from("50::0").expect("Shouldn't fail on test!");
assert_eq!(RangeBound::Absolute(50), range.lower);
assert_eq!(RangeBound::Absolute(50), range.upper);
// Test range with zero context
let range = LineRange::from("30:40:0").expect("Shouldn't fail on test!");
assert_eq!(RangeBound::Absolute(30), range.lower);
assert_eq!(RangeBound::Absolute(40), range.upper);
}
#[test]
fn test_parse_context_fail() {
let range = LineRange::from("40::z");
assert!(range.is_err());
let range = LineRange::from("::5");
assert!(range.is_err());
let range = LineRange::from("40::");
assert!(range.is_err());
let range = LineRange::from("30:40:z");
assert!(range.is_err());
let range = LineRange::from("30::40:5");
assert!(range.is_err());
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RangeCheckResult {
// Within one of the given ranges