mirror of
https://github.com/sharkdp/bat.git
synced 2025-09-04 12:22:28 +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:
@@ -194,6 +194,8 @@ Options:
|
|||||||
'--line-range 40:' prints lines 40 to the end of the file
|
'--line-range 40:' prints lines 40 to the end of the file
|
||||||
'--line-range 40' only prints line 40
|
'--line-range 40' only prints line 40
|
||||||
'--line-range 30:+10' prints lines 30 to 40
|
'--line-range 30:+10' prints lines 30 to 40
|
||||||
|
'--line-range 35::5' prints lines 30 to 40 (line 35 with 5 lines of context)
|
||||||
|
'--line-range 30:40:2' prints lines 28 to 42 (range 30-40 with 2 lines of context)
|
||||||
|
|
||||||
-L, --list-languages
|
-L, --list-languages
|
||||||
Display a list of supported languages for syntax highlighting.
|
Display a list of supported languages for syntax highlighting.
|
||||||
|
@@ -525,7 +525,9 @@ pub fn build_app(interactive_output: bool) -> Command {
|
|||||||
'--line-range :40' prints lines 1 to 40\n \
|
'--line-range :40' prints lines 1 to 40\n \
|
||||||
'--line-range 40:' prints lines 40 to the end of the file\n \
|
'--line-range 40:' prints lines 40 to the end of the file\n \
|
||||||
'--line-range 40' only prints line 40\n \
|
'--line-range 40' only prints line 40\n \
|
||||||
'--line-range 30:+10' prints lines 30 to 40",
|
'--line-range 30:+10' prints lines 30 to 40\n \
|
||||||
|
'--line-range 35::5' prints lines 30 to 40 (line 35 with 5 lines of context)\n \
|
||||||
|
'--line-range 30:40:2' prints lines 28 to 42 (range 30-40 with 2 lines of context)",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
@@ -98,8 +98,33 @@ impl LineRange {
|
|||||||
new_range.upper = RangeBound::Absolute(upper_absolute_bound);
|
new_range.upper = RangeBound::Absolute(upper_absolute_bound);
|
||||||
Ok(new_range)
|
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(
|
_ => 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(),
|
.into(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -274,6 +299,52 @@ fn test_parse_minus_fail() {
|
|||||||
assert!(range.is_err());
|
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)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum RangeCheckResult {
|
pub enum RangeCheckResult {
|
||||||
// Within one of the given ranges
|
// Within one of the given ranges
|
||||||
|
Reference in New Issue
Block a user