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:
parent
0c38bf9606
commit
b7f5d3b855
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
27
src/viewer/utils.rs
Normal 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))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user