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.
This commit is contained in:
Robin Krahl 2020-09-15 21:37:33 +02:00
parent 0c38bf9606
commit b7f5d3b855
No known key found for this signature in database
GPG Key ID: 8E9B0870524F69D8
4 changed files with 66 additions and 62 deletions

View File

@ -2,8 +2,8 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
mod text; mod text;
mod utils;
use std::cmp;
use std::fmt; use std::fmt;
use std::io; use std::io;
@ -39,13 +39,3 @@ pub fn get_default() -> Box<dyn Viewer> {
Box::new(text::TextViewer::with_plain_text()) 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
}
}

View File

@ -7,7 +7,7 @@ use html2text::render::text_renderer;
use crate::args; use crate::args;
use crate::doc; use crate::doc;
use crate::viewer; use crate::viewer::utils;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PlainTextRenderer { pub struct PlainTextRenderer {
@ -23,7 +23,7 @@ struct Decorator {
impl super::Printer for PlainTextRenderer { impl super::Printer for PlainTextRenderer {
fn new(args: args::ViewerArgs) -> anyhow::Result<Self> { fn new(args: args::ViewerArgs) -> anyhow::Result<Self> {
Ok(Self { Ok(Self {
line_length: viewer::get_line_length(&args), line_length: utils::get_line_length(&args),
}) })
} }

View File

@ -7,7 +7,7 @@ use html2text::render::text_renderer;
use crate::args; use crate::args;
use crate::doc; use crate::doc;
use crate::viewer; use crate::viewer::utils;
type RichString = text_renderer::TaggedString<Vec<text_renderer::RichAnnotation>>; type RichString = text_renderer::TaggedString<Vec<text_renderer::RichAnnotation>>;
@ -19,56 +19,13 @@ pub struct RichTextRenderer {
theme: syntect::highlighting::Theme, 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 { impl super::Printer for RichTextRenderer {
fn new(args: args::ViewerArgs) -> anyhow::Result<Self> { fn new(args: args::ViewerArgs) -> anyhow::Result<Self> {
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 { Ok(Self {
line_length: viewer::get_line_length(&args), line_length: utils::get_line_length(&args),
highlight: !args.no_syntax_highlight, highlight: !args.no_syntax_highlight,
syntax_set: syntect::parsing::SyntaxSet::load_defaults_newlines(), 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))?; write!(io::stdout(), "{}", " ".repeat(indent))?;
for element in line.iter() { for element in line.iter() {
if let text_renderer::TaggedLineElement::Str(ts) = element { if let text_renderer::TaggedLineElement::Str(ts) = element {
self.render_string(ts)?; write!(io::stdout(), "{}", style_rich_string(ts))?;
} }
} }
writeln!(io::stdout())?; writeln!(io::stdout())?;
@ -105,7 +62,10 @@ impl super::Printer for RichTextRenderer {
for line in syntect::util::LinesWithEndings::from(code.as_ref()) { for line in syntect::util::LinesWithEndings::from(code.as_ref()) {
let ranges = h.highlight(line, &self.syntax_set); let ranges = h.highlight(line, &self.syntax_set);
write!(io::stdout(), "{}", " ".repeat(indent))?; 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())?; writeln!(io::stdout())?;
} else { } 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 crossterm::style::{Attribute, Color};
use text_renderer::RichAnnotation; use text_renderer::RichAnnotation;

27
src/viewer/utils.rs Normal file
View File

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
// 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<syntect::highlighting::Theme> {
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))
}