feat(export): add SyntectHtmlHandler

This commit is contained in:
PoiScript 2019-09-13 23:40:30 +08:00
parent e4a3247f63
commit 78a6b33aa9
7 changed files with 102 additions and 101 deletions

View file

@ -199,6 +199,8 @@ By now, orgize provides two features:
+ `chrono`: adds the ability to convert `Datetime` into `chrono` structs, disabled by default. + `chrono`: adds the ability to convert `Datetime` into `chrono` structs, disabled by default.
+ `syntect`: provides `SyntectHtmlHandler` for highlighting code block, disabled by default.
## License ## License
MIT MIT

View file

@ -28,10 +28,10 @@ memchr = "2.2.1"
nom = "5.0.1" nom = "5.0.1"
serde = { version = "1.0.100", optional = true, features = ["derive"] } serde = { version = "1.0.100", optional = true, features = ["derive"] }
serde_indextree = { version = "0.2.0", optional = true } serde_indextree = { version = "0.2.0", optional = true }
syntect = { version = "3.2.1", optional = true }
[dev-dependencies] [dev-dependencies]
lazy_static = "1.4.0" lazy_static = "1.4.0"
pretty_assertions = "0.6.1" pretty_assertions = "0.6.1"
serde_json = "1.0.40" serde_json = "1.0.40"
slugify = "0.1.0" slugify = "0.1.0"
syntect = "3.2.1"

View file

@ -1,94 +0,0 @@
use std::env::args;
use std::fs;
use std::io::{Error, Result, Write};
use syntect::{
easy::HighlightLines,
highlighting::ThemeSet,
html::{styled_line_to_highlighted_html, IncludeBackground},
parsing::SyntaxSet,
};
use orgize::export::{DefaultHtmlHandler, HtmlHandler};
use orgize::{Element, Org};
pub struct SyntectHtmlHandler {
syntax_set: SyntaxSet,
theme_set: ThemeSet,
default_hanlder: DefaultHtmlHandler,
}
impl Default for SyntectHtmlHandler {
fn default() -> Self {
SyntectHtmlHandler {
syntax_set: SyntaxSet::load_defaults_newlines(),
theme_set: ThemeSet::load_defaults(),
default_hanlder: DefaultHtmlHandler,
}
}
}
impl SyntectHtmlHandler {
fn highlight(&self, language: Option<&str>, content: &str) -> String {
let mut highlighter = HighlightLines::new(
language
.and_then(|lang| self.syntax_set.find_syntax_by_token(lang))
.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text()),
&self.theme_set.themes["InspiredGitHub"],
);
let regions = highlighter.highlight(content, &self.syntax_set);
styled_line_to_highlighted_html(&regions[..], IncludeBackground::No)
}
}
impl HtmlHandler<Error> for SyntectHtmlHandler {
fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<()> {
match element {
Element::InlineSrc(inline_src) => write!(
w,
"<code>{}</code>",
self.highlight(Some(&inline_src.lang), &inline_src.body)
)?,
Element::SourceBlock(block) => {
if block.language.is_empty() {
write!(w, "<pre class=\"example\">{}</pre>", block.contents)?;
} else {
write!(
w,
"<div class=\"org-src-container\"><pre class=\"src src-{}\">{}</pre></div>",
block.language,
self.highlight(Some(&block.language), &block.contents)
)?
}
}
Element::FixedWidth { value } => write!(
w,
"<pre class=\"example\">{}</pre>",
self.highlight(None, value)
)?,
Element::ExampleBlock(block) => write!(
w,
"<pre class=\"example\">{}</pre>",
self.highlight(None, &block.contents)
)?,
_ => self.default_hanlder.start(w, element)?,
}
Ok(())
}
}
fn main() {
let args: Vec<_> = args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <org-file>", args[0]);
} else {
let contents = String::from_utf8(fs::read(&args[1]).unwrap()).unwrap();
let mut writer = Vec::new();
Org::parse(&contents)
.html_with_handler(&mut writer, SyntectHtmlHandler::default())
.unwrap();
println!("{}", String::from_utf8(writer).unwrap());
}
}

View file

@ -3,6 +3,7 @@ use crate::elements::Element;
use jetscii::bytes; use jetscii::bytes;
use std::fmt; use std::fmt;
use std::io::{Error, Write}; use std::io::{Error, Write};
use std::marker::PhantomData;
pub struct Escape<S: AsRef<str>>(pub S); pub struct Escape<S: AsRef<str>>(pub S);
@ -195,3 +196,93 @@ pub trait HtmlHandler<E: From<Error>> {
pub struct DefaultHtmlHandler; pub struct DefaultHtmlHandler;
impl HtmlHandler<Error> for DefaultHtmlHandler {} impl HtmlHandler<Error> for DefaultHtmlHandler {}
#[cfg(feature = "syntect")]
pub mod syntect_feature {
use super::*;
use syntect::{
easy::HighlightLines,
highlighting::ThemeSet,
html::{styled_line_to_highlighted_html, IncludeBackground},
parsing::SyntaxSet,
};
pub struct SyntectHtmlHandler<E: From<Error>, H: HtmlHandler<E>> {
pub syntax_set: SyntaxSet,
pub theme_set: ThemeSet,
pub inner: H,
error_type: PhantomData<E>,
}
impl Default for SyntectHtmlHandler<Error, DefaultHtmlHandler> {
fn default() -> Self {
SyntectHtmlHandler {
syntax_set: SyntaxSet::load_defaults_newlines(),
theme_set: ThemeSet::load_defaults(),
inner: DefaultHtmlHandler,
error_type: PhantomData,
}
}
}
impl<E: From<Error>, H: HtmlHandler<E>> SyntectHtmlHandler<E, H> {
pub fn new(inner: H) -> Self {
SyntectHtmlHandler {
syntax_set: SyntaxSet::load_defaults_newlines(),
theme_set: ThemeSet::load_defaults(),
inner,
error_type: PhantomData,
}
}
fn highlight(&self, language: Option<&str>, content: &str) -> String {
let mut highlighter = HighlightLines::new(
language
.and_then(|lang| self.syntax_set.find_syntax_by_token(lang))
.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text()),
&self.theme_set.themes["InspiredGitHub"],
);
let regions = highlighter.highlight(content, &self.syntax_set);
styled_line_to_highlighted_html(&regions[..], IncludeBackground::No)
}
}
impl<E: From<Error>, H: HtmlHandler<E>> HtmlHandler<E> for SyntectHtmlHandler<E, H> {
fn start<W: Write>(&mut self, mut w: W, element: &Element<'_>) -> Result<(), E> {
match element {
Element::InlineSrc(inline_src) => write!(
w,
"<code>{}</code>",
self.highlight(Some(&inline_src.lang), &inline_src.body)
)?,
Element::SourceBlock(block) => {
if block.language.is_empty() {
write!(w, "<pre class=\"example\">{}</pre>", block.contents)?;
} else {
write!(
w,
"<div class=\"org-src-container\"><pre class=\"src src-{}\">{}</pre></div>",
block.language,
self.highlight(Some(&block.language), &block.contents)
)?
}
}
Element::FixedWidth { value } => write!(
w,
"<pre class=\"example\">{}</pre>",
self.highlight(None, value)
)?,
Element::ExampleBlock(block) => write!(
w,
"<pre class=\"example\">{}</pre>",
self.highlight(None, &block.contents)
)?,
_ => self.inner.start(w, element)?,
}
Ok(())
}
}
}
#[cfg(feature = "syntect")]
pub use syntect_feature::*;

View file

@ -3,8 +3,8 @@
pub mod html; pub mod html;
pub mod org; pub mod org;
pub use html::{DefaultHtmlHandler, HtmlHandler}; pub use html::*;
pub use org::{DefaultOrgHandler, OrgHandler}; pub use org::*;
use std::io::{Error, Write}; use std::io::{Error, Write};

View file

@ -210,6 +210,8 @@
//! //!
//! + `chrono`: adds the ability to convert `Datetime` into `chrono` structs, disabled by default. //! + `chrono`: adds the ability to convert `Datetime` into `chrono` structs, disabled by default.
//! //!
//! + `syntect`: provides `SyntectHtmlHandler` for highlighting code block, disabled by default.
//!
//! # License //! # License
//! //!
//! MIT //! MIT

View file

@ -104,10 +104,10 @@ impl<'a> Org<'a> {
} }
pub fn html<W: Write>(&self, wrtier: W) -> Result<(), Error> { pub fn html<W: Write>(&self, wrtier: W) -> Result<(), Error> {
self.html_with_handler(wrtier, DefaultHtmlHandler) self.html_with_handler(wrtier, &mut DefaultHtmlHandler)
} }
pub fn html_with_handler<W, H, E>(&self, mut writer: W, mut handler: H) -> Result<(), E> pub fn html_with_handler<W, H, E>(&self, mut writer: W, handler: &mut H) -> Result<(), E>
where where
W: Write, W: Write,
E: From<Error>, E: From<Error>,
@ -124,10 +124,10 @@ impl<'a> Org<'a> {
} }
pub fn org<W: Write>(&self, wrtier: W) -> Result<(), Error> { pub fn org<W: Write>(&self, wrtier: W) -> Result<(), Error> {
self.org_with_handler(wrtier, DefaultOrgHandler) self.org_with_handler(wrtier, &mut DefaultOrgHandler)
} }
pub fn org_with_handler<W, H, E>(&self, mut writer: W, mut handler: H) -> Result<(), E> pub fn org_with_handler<W, H, E>(&self, mut writer: W, handler: &mut H) -> Result<(), E>
where where
W: Write, W: Write,
E: From<Error>, E: From<Error>,