Skip to content

Commit f357600

Browse files
authored
[Feat] add range support for CLI (#32)
1 parent b45473e commit f357600

File tree

3 files changed

+90
-2
lines changed

3 files changed

+90
-2
lines changed

cli/src/code.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,18 @@ use codesnap::{
1111
utils::clipboard::Clipboard,
1212
};
1313

14-
use crate::{CLI, STDIN_CODE_DEFAULT_CHAR};
14+
use crate::{
15+
range::{cut_code_snippet_by_range, prepare_range},
16+
CLI, STDIN_CODE_DEFAULT_CHAR,
17+
};
1518

1619
pub fn create_code(cli: &CLI, config_code: Code) -> anyhow::Result<Code> {
17-
let code_snippet = get_code_snippet(cli)?;
20+
let code_snippet = cut_code_snippet_by_range(
21+
&get_code_snippet(cli)?,
22+
&cli.range
23+
.clone()
24+
.and_then(|range| Some(prepare_range(&range))),
25+
)?;
1826
let mut code_builder = CodeBuilder::from_code(config_code.clone());
1927
let mut code = code_builder.content(&code_snippet).build()?;
2028

cli/src/main.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod code;
22
mod config;
33
mod egg;
44
mod logger;
5+
mod range;
56
mod watermark;
67
mod window;
78

@@ -53,6 +54,16 @@ struct CLI {
5354
#[arg(short, long)]
5455
output: String,
5556

57+
/// You can set the range of the code snippet to display
58+
/// for example, display the 3rd to 5th:
59+
/// 3:5
60+
/// The syntax is similar to the range in Python or Golang, so basically you can use 3: to
61+
/// represent the 3rd to the end, or use :5 to represent the start to the 5th.
62+
/// This option is useful when you use the `from_file` option as the input, and you just want
63+
/// to display part of the code snippet.
64+
#[arg(long)]
65+
range: Option<String>,
66+
5667
/// Font family for the code snippet
5768
#[arg(long)]
5869
code_font_family: Option<String>,

cli/src/range.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use std::str::Lines;
2+
3+
use anyhow::bail;
4+
5+
const RANGE_SEPARATOR: &'static str = ":";
6+
const DEFAULT_RANGE: &'static str = "start:end";
7+
8+
fn parse_range_point(point: &str, lines: &Vec<&str>) -> anyhow::Result<usize> {
9+
let point = match point {
10+
"start" => 0,
11+
"end" => lines.len(),
12+
_ => point.parse::<usize>()?,
13+
};
14+
15+
Ok(point)
16+
}
17+
18+
pub fn cut_code_snippet_by_range(
19+
code_snippet: &str,
20+
range: &Option<String>,
21+
) -> anyhow::Result<String> {
22+
let range = range.clone().unwrap_or(String::from(DEFAULT_RANGE));
23+
let range_points = range.split(RANGE_SEPARATOR).collect::<Vec<_>>();
24+
25+
if range_points.len() != 2 {
26+
bail!("Invalid range format");
27+
}
28+
29+
let code_snippet_lines = code_snippet.lines();
30+
let (start, end) = parse_range(&range_points, &code_snippet_lines)?;
31+
let code_snippet = code_snippet_lines
32+
.skip(start)
33+
.take(end - start)
34+
.collect::<Vec<&str>>()
35+
.join("\n");
36+
37+
Ok(code_snippet)
38+
}
39+
40+
// Prepare range string by following rules:
41+
//
42+
// If raw_range is equal to "n:", which means x:end
43+
// If raw_range is equal to ":n", which means start:n
44+
pub fn prepare_range(raw_range: &str) -> String {
45+
if raw_range.starts_with(RANGE_SEPARATOR) {
46+
format!("{}{}", "start", raw_range)
47+
} else if raw_range.ends_with(RANGE_SEPARATOR) {
48+
format!("{}{}", raw_range, "end")
49+
} else {
50+
raw_range.to_string()
51+
}
52+
}
53+
54+
// Parse "start" to 0, "end" to lines.len(), and other values to usize
55+
pub fn parse_range(
56+
range_points: &Vec<&str>,
57+
code_snippet_lines: &Lines,
58+
) -> anyhow::Result<(usize, usize)> {
59+
let lines = code_snippet_lines.clone().collect::<Vec<&str>>();
60+
let start = parse_range_point(range_points[0], &lines)?;
61+
let end = parse_range_point(range_points[1], &lines)?;
62+
let points = if start > end {
63+
(end, start)
64+
} else {
65+
(start, end)
66+
};
67+
68+
Ok(points)
69+
}

0 commit comments

Comments
 (0)