diff --git a/src/bin/silicon/config.rs b/src/bin/silicon/config.rs index eb4311f..d904780 100644 --- a/src/bin/silicon/config.rs +++ b/src/bin/silicon/config.rs @@ -76,9 +76,30 @@ fn parse_line_range(s: &str) -> Result, ParseIntError> { Ok(result) } +fn parse_line_col(s: &str) -> Result, ParseIntError> { + let mut result = vec![]; + for range in s.split(';') { + let mut line_col = range.split(','); + if line_col.clone().count() != 3 { + continue; + } + let first = line_col.next().unwrap(); + let second = line_col.next().unwrap(); + let third = line_col.next().unwrap(); + + let first = first.parse::()?; + let second = second.parse::()?; + let third = third.parse::()?; + + result.push((first, second, third)); + } + Ok(result) +} + // https://github.com/TeXitoi/structopt/blob/master/CHANGELOG.md#support-optional-vectors-of-arguments-for-distinguishing-between--o-1-2--o-and-no-option-provided-at-all-by-sphynx-180 type FontList = Vec<(String, f32)>; type Lines = Vec; +type LineCol = Vec<(u32, u32, u32)>; #[derive(StructOpt, Debug)] #[structopt(name = "silicon")] @@ -118,6 +139,11 @@ pub struct Config { #[structopt(long, value_name = "LINES", parse(try_from_str = parse_line_range))] pub highlight_lines: Option, + /// Lines and cols to highlight specified as a triple (e.g. 10,2,8 highlights columns 2 to 8 in + /// line 10). To suply multiple highlight ranges separate each triple with a semicolon; + #[structopt(long, parse(try_from_str = parse_line_col))] + pub highlight_cols: Option, + /// The language for syntax highlighting. You can use full name ("Rust") or file extension ("rs"). #[structopt(short, value_name = "LANG", long)] pub language: Option, @@ -167,6 +193,10 @@ pub struct Config { #[structopt(long)] pub no_round_corner: bool, + /// Set corner radius + #[structopt(long, default_value = "12")] + pub corner_radius: u8, + /// Pad horiz #[structopt(long, value_name = "PAD", default_value = "80")] pub pad_horiz: u32, @@ -282,9 +312,11 @@ impl Config { .line_number(!self.no_line_number) .font(self.font.clone().unwrap_or_default()) .round_corner(!self.no_round_corner) + .corner_radius(self.corner_radius) .shadow_adder(self.get_shadow_adder()?) .tab_width(self.tab_width) .highlight_lines(self.highlight_lines.clone().unwrap_or_default()) + .highlight_cols(self.highlight_cols.clone().unwrap_or_default()) .line_offset(self.line_offset) .code_pad_right(self.code_pad_right); diff --git a/src/formatter.rs b/src/formatter.rs index 5b31d78..33a26aa 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -37,6 +37,9 @@ pub struct ImageFormatter { /// round corner /// Default: true round_corner: bool, + /// corner radius + /// Default: 12 + corner_radius: u8, /// pad between code and line number /// Default: 6 line_number_pad: u32, @@ -48,6 +51,8 @@ pub struct ImageFormatter { font: T, /// Highlight lines highlight_lines: Vec, + /// Highlight col range in line + highlight_cols: Vec<(u32, u32, u32)>, /// Shadow adder shadow_adder: Option, /// Tab width @@ -68,12 +73,16 @@ pub struct ImageFormatterBuilder { font: Vec<(S, f32)>, /// Highlight lines highlight_lines: Vec, + /// Highlight col range in line + highlight_cols: Vec<(u32, u32, u32)>, /// Whether show the window controls window_controls: bool, /// Window title window_title: Option, /// Whether round the corner of the image round_corner: bool, + /// Sets corner radius + corner_radius: u8, /// Shadow adder, shadow_adder: Option, /// Tab width @@ -144,6 +153,11 @@ impl + Default> ImageFormatterBuilder { self } + pub fn corner_radius(mut self, r: u8) -> Self { + self.corner_radius = r; + self + } + /// Add the shadow pub fn shadow_adder(mut self, adder: ShadowAdder) -> Self { self.shadow_adder = Some(adder); @@ -156,6 +170,11 @@ impl + Default> ImageFormatterBuilder { self } + pub fn highlight_cols(mut self, cols: Vec<(u32, u32, u32)>) -> Self { + self.highlight_cols = cols; + self + } + /// Set tab width pub fn tab_width(mut self, width: u8) -> Self { self.tab_width = width; @@ -185,7 +204,9 @@ impl + Default> ImageFormatterBuilder { line_number_pad: 6, line_number_chars: 0, highlight_lines: self.highlight_lines, + highlight_cols: self.highlight_cols, round_corner: self.round_corner, + corner_radius: self.corner_radius, shadow_adder: self.shadow_adder, tab_width: self.tab_width, font, @@ -209,11 +230,21 @@ impl ImageFormatter { self.font.height(" ") + self.line_pad } + /// width of a single character + fn get_col_width(&mut self) -> u32 { + self.font.width(" ") + } + /// calculate the Y coordinate of a line fn get_line_y(&mut self, lineno: u32) -> u32 { lineno * self.get_line_height() + self.code_pad + self.code_pad_top } + /// calculate the X coordinate of a column + fn get_col_x(&mut self, col: u32) -> u32 { + col * self.get_col_width() + self.code_pad + } + /// calculate the size of code area fn get_image_size(&mut self, max_width: u32, lineno: u32) -> (u32, u32) { ( @@ -334,6 +365,38 @@ impl ImageFormatter { } } + fn highlight_cols>( + &mut self, + image: &mut RgbaImage, + cols: I, + max_lineno_digit_count: u32, + ) { + let height = self.get_line_height(); + let mut cloned = image.clone(); + let color = cloned.get_pixel_mut(20, 20); + + for i in color.0.iter_mut() { + *i = (*i).saturating_add(40); + } + + for (y, x_start, x_end) in cols { + if x_start > x_end { + continue; + } + let x_start = self.get_col_x(x_start - 1); + let x_end = self.get_col_x(x_end - 1) + self.get_col_width(); + let shadow = RgbaImage::from_pixel(x_end - x_start, height, *color); + let x = x_start + + if self.line_number { + self.get_col_width() * max_lineno_digit_count + 12 + } else { + 0 + }; + let y = self.get_line_y(y - 1); + copy_alpha(&shadow, image, x, y); + } + } + // TODO: use &T instead of &mut T ? pub fn format(&mut self, v: &[Vec<(Style, &str)>], theme: &Theme) -> RgbaImage { if self.line_number { @@ -362,6 +425,23 @@ impl ImageFormatter { .collect::>(); self.highlight_lines(&mut image, highlight_lines); } + + if !self.highlight_cols.is_empty() { + let mut count = 1; + let mut max_lineno = drawables.max_lineno; + while max_lineno != 0 { + max_lineno /= 10; + count += 1; + } + let highlight_cols = self + .highlight_cols + .iter() + .cloned() + .filter(|&(y, _, _)| y >= 1 && y <= drawables.max_lineno + 1) + .collect::>(); + self.highlight_cols(&mut image, highlight_cols, count); + } + if self.line_number { self.draw_line_number(&mut image, drawables.max_lineno, foreground.to_rgba()); } @@ -381,8 +461,14 @@ impl ImageFormatter { add_window_controls(&mut image, ¶ms); } - if self.round_corner { - round_corner(&mut image, 12); + if self.round_corner && self.corner_radius != 0 { + if u32::from(self.corner_radius) > self.code_pad { + println!( + "Warning: r = {} > {} (code pad); Parts of the image may start to overlap!", + self.corner_radius, self.code_pad + ); + } + round_corner(&mut image, self.corner_radius.into()); } if let Some(adder) = &self.shadow_adder {