From b7f5d3b855ddd4d5156c16d1574b93b007088a03 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 15 Sep 2020 21:37:33 +0200 Subject: [PATCH] Introduce viewer::utils module This patch adds the viewer::utils module with utility methods for viewer implementations. It contains common functions for text rendering and syntax highlighting. --- src/viewer/mod.rs | 12 +----- src/viewer/text/plain.rs | 4 +- src/viewer/text/rich.rs | 85 +++++++++++++++++----------------------- src/viewer/utils.rs | 27 +++++++++++++ 4 files changed, 66 insertions(+), 62 deletions(-) create mode 100644 src/viewer/utils.rs diff --git a/src/viewer/mod.rs b/src/viewer/mod.rs index 0075cd2..c0ed26c 100644 --- a/src/viewer/mod.rs +++ b/src/viewer/mod.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: MIT mod text; +mod utils; -use std::cmp; use std::fmt; use std::io; @@ -39,13 +39,3 @@ pub fn get_default() -> Box { Box::new(text::TextViewer::with_plain_text()) } } - -pub fn get_line_length(args: &args::ViewerArgs) -> usize { - if let Some(width) = args.width { - width - } else if let Ok((cols, _)) = crossterm::terminal::size() { - cmp::min(cols.into(), args.max_width) - } else { - args.max_width - } -} diff --git a/src/viewer/text/plain.rs b/src/viewer/text/plain.rs index 0954257..71206b8 100644 --- a/src/viewer/text/plain.rs +++ b/src/viewer/text/plain.rs @@ -7,7 +7,7 @@ use html2text::render::text_renderer; use crate::args; use crate::doc; -use crate::viewer; +use crate::viewer::utils; #[derive(Clone, Debug)] pub struct PlainTextRenderer { @@ -23,7 +23,7 @@ struct Decorator { impl super::Printer for PlainTextRenderer { fn new(args: args::ViewerArgs) -> anyhow::Result { Ok(Self { - line_length: viewer::get_line_length(&args), + line_length: utils::get_line_length(&args), }) } diff --git a/src/viewer/text/rich.rs b/src/viewer/text/rich.rs index d7c5341..e2fa001 100644 --- a/src/viewer/text/rich.rs +++ b/src/viewer/text/rich.rs @@ -7,7 +7,7 @@ use html2text::render::text_renderer; use crate::args; use crate::doc; -use crate::viewer; +use crate::viewer::utils; type RichString = text_renderer::TaggedString>; @@ -19,56 +19,13 @@ pub struct RichTextRenderer { theme: syntect::highlighting::Theme, } -impl RichTextRenderer { - fn render_string(&self, ts: &RichString) -> io::Result<()> { - let content = get_styled_content(ts); - write!(io::stdout(), "{}", content) - } - - fn render_syntax(&self, line: &[(syntect::highlighting::Style, &str)]) -> io::Result<()> { - use crossterm::style::{Attribute, Color}; - use syntect::highlighting::FontStyle; - - for (style, text) in line { - let mut content = crossterm::style::style(text).with(Color::Rgb { - r: style.foreground.r, - g: style.foreground.g, - b: style.foreground.b, - }); - if style.font_style.contains(FontStyle::BOLD) { - // TODO: investigate why NoBold does not work - content = content - .attribute(Attribute::Bold) - .attribute(Attribute::Reset); - } - if style.font_style.contains(FontStyle::UNDERLINE) { - content = content.attribute(Attribute::Underlined); - } - if style.font_style.contains(FontStyle::ITALIC) { - content = content.attribute(Attribute::Italic); - } - write!(io::stdout(), "{}", content)?; - } - - Ok(()) - } -} - impl super::Printer for RichTextRenderer { fn new(args: args::ViewerArgs) -> anyhow::Result { - use anyhow::Context; - - let mut theme_set = syntect::highlighting::ThemeSet::load_defaults(); - let theme_name = args.theme.as_deref().unwrap_or("base16-eighties.dark"); - let theme = theme_set - .themes - .remove(theme_name) - .with_context(|| format!("Could not find theme {}", theme_name))?; Ok(Self { - line_length: viewer::get_line_length(&args), + line_length: utils::get_line_length(&args), highlight: !args.no_syntax_highlight, syntax_set: syntect::parsing::SyntaxSet::load_defaults_newlines(), - theme, + theme: utils::get_syntect_theme(&args)?, }) } @@ -89,7 +46,7 @@ impl super::Printer for RichTextRenderer { write!(io::stdout(), "{}", " ".repeat(indent))?; for element in line.iter() { if let text_renderer::TaggedLineElement::Str(ts) = element { - self.render_string(ts)?; + write!(io::stdout(), "{}", style_rich_string(ts))?; } } writeln!(io::stdout())?; @@ -105,7 +62,10 @@ impl super::Printer for RichTextRenderer { for line in syntect::util::LinesWithEndings::from(code.as_ref()) { let ranges = h.highlight(line, &self.syntax_set); write!(io::stdout(), "{}", " ".repeat(indent))?; - self.render_syntax(&ranges)?; + for (style, text) in ranges { + let content = style_syntect_string(style, text); + write!(io::stdout(), "{}", content)?; + } } writeln!(io::stdout())?; } else { @@ -131,7 +91,34 @@ impl super::Printer for RichTextRenderer { } } -fn get_styled_content(ts: &RichString) -> crossterm::style::StyledContent<&str> { +pub fn style_syntect_string( + style: syntect::highlighting::Style, + s: &str, +) -> crossterm::style::StyledContent<&str> { + use crossterm::style::{Attribute, Color}; + use syntect::highlighting::FontStyle; + + let mut content = crossterm::style::style(s).with(Color::Rgb { + r: style.foreground.r, + g: style.foreground.g, + b: style.foreground.b, + }); + if style.font_style.contains(FontStyle::BOLD) { + // TODO: investigate why NoBold does not work + content = content + .attribute(Attribute::Bold) + .attribute(Attribute::Reset); + } + if style.font_style.contains(FontStyle::UNDERLINE) { + content = content.attribute(Attribute::Underlined); + } + if style.font_style.contains(FontStyle::ITALIC) { + content = content.attribute(Attribute::Italic); + } + content +} + +pub fn style_rich_string(ts: &RichString) -> crossterm::style::StyledContent<&str> { use crossterm::style::{Attribute, Color}; use text_renderer::RichAnnotation; diff --git a/src/viewer/utils.rs b/src/viewer/utils.rs new file mode 100644 index 0000000..e454ec8 --- /dev/null +++ b/src/viewer/utils.rs @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2020 Robin Krahl +// SPDX-License-Identifier: MIT + +use std::cmp; + +use anyhow::Context as _; + +use crate::args; + +pub fn get_line_length(args: &args::ViewerArgs) -> usize { + if let Some(width) = args.width { + width + } else if let Ok((cols, _)) = crossterm::terminal::size() { + cmp::min(cols.into(), args.max_width) + } else { + args.max_width + } +} + +pub fn get_syntect_theme(args: &args::ViewerArgs) -> anyhow::Result { + let mut theme_set = syntect::highlighting::ThemeSet::load_defaults(); + let theme_name = args.theme.as_deref().unwrap_or("base16-eighties.dark"); + theme_set + .themes + .remove(theme_name) + .with_context(|| format!("Could not find theme {}", theme_name)) +}