Compare commits
3 commits
8c71258212
...
554a83a63c
Author | SHA1 | Date | |
---|---|---|---|
554a83a63c | |||
c0f7af84ec | |||
23ac532061 |
3 changed files with 962 additions and 2819 deletions
3493
Cargo.lock
generated
3493
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -24,6 +24,7 @@ use crate::compiler::compiler::Target;
|
||||||
use crate::document::document::Document;
|
use crate::document::document::Document;
|
||||||
use crate::document::element::ElemKind;
|
use crate::document::element::ElemKind;
|
||||||
use crate::document::element::Element;
|
use crate::document::element::Element;
|
||||||
|
use crate::lua::kernel::CTX;
|
||||||
use crate::parser::parser::Parser;
|
use crate::parser::parser::Parser;
|
||||||
use crate::parser::rule::RegexRule;
|
use crate::parser::rule::RegexRule;
|
||||||
use crate::parser::source::Source;
|
use crate::parser::source::Source;
|
||||||
|
@ -41,6 +42,15 @@ enum CodeKind {
|
||||||
Inline,
|
Inline,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&CodeKind> for ElemKind {
|
||||||
|
fn from(value: &CodeKind) -> Self {
|
||||||
|
match value {
|
||||||
|
CodeKind::FullBlock | CodeKind::MiniBlock => ElemKind::Block,
|
||||||
|
CodeKind::Inline => ElemKind::Inline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Code {
|
struct Code {
|
||||||
location: Token,
|
location: Token,
|
||||||
|
@ -73,12 +83,19 @@ impl Code {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_html(&self, compiler: &Compiler) -> Result<String, String> {
|
pub fn get_syntaxes() -> &'static SyntaxSet {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref syntax_set: SyntaxSet = SyntaxSet::load_defaults_newlines();
|
static ref syntax_set: SyntaxSet = SyntaxSet::load_defaults_newlines();
|
||||||
|
}
|
||||||
|
|
||||||
|
&syntax_set
|
||||||
|
}
|
||||||
|
|
||||||
|
fn highlight_html(&self, compiler: &Compiler) -> Result<String, String> {
|
||||||
|
lazy_static! {
|
||||||
static ref theme_set: ThemeSet = ThemeSet::load_defaults();
|
static ref theme_set: ThemeSet = ThemeSet::load_defaults();
|
||||||
}
|
}
|
||||||
let syntax = match syntax_set.find_syntax_by_name(self.language.as_str()) {
|
let syntax = match Code::get_syntaxes().find_syntax_by_name(self.language.as_str()) {
|
||||||
Some(syntax) => syntax,
|
Some(syntax) => syntax,
|
||||||
None => {
|
None => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
|
@ -116,7 +133,7 @@ impl Code {
|
||||||
|
|
||||||
// Code
|
// Code
|
||||||
result += "</td><td class=\"code-block-line\"><pre>";
|
result += "</td><td class=\"code-block-line\"><pre>";
|
||||||
match h.highlight_line(line, &syntax_set) {
|
match h.highlight_line(line, Code::get_syntaxes()) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Error highlighting line `{line}`: {}",
|
"Error highlighting line `{line}`: {}",
|
||||||
|
@ -151,7 +168,7 @@ impl Code {
|
||||||
for line in self.code.split(|c| c == '\n') {
|
for line in self.code.split(|c| c == '\n') {
|
||||||
result += "<tr><td class=\"code-block-line\"><pre>";
|
result += "<tr><td class=\"code-block-line\"><pre>";
|
||||||
// Code
|
// Code
|
||||||
match h.highlight_line(line, &syntax_set) {
|
match h.highlight_line(line, Code::get_syntaxes()) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Error highlighting line `{line}`: {}",
|
"Error highlighting line `{line}`: {}",
|
||||||
|
@ -181,7 +198,7 @@ impl Code {
|
||||||
result += "</table></div></div>";
|
result += "</table></div></div>";
|
||||||
} else if self.block == CodeKind::Inline {
|
} else if self.block == CodeKind::Inline {
|
||||||
result += "<a class=\"inline-code\"><code>";
|
result += "<a class=\"inline-code\"><code>";
|
||||||
match h.highlight_line(self.code.as_str(), &syntax_set) {
|
match h.highlight_line(self.code.as_str(), Code::get_syntaxes()) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Error highlighting line `{}`: {}",
|
"Error highlighting line `{}`: {}",
|
||||||
|
@ -242,13 +259,7 @@ impl Cached for Code {
|
||||||
impl Element for Code {
|
impl Element for Code {
|
||||||
fn location(&self) -> &Token { &self.location }
|
fn location(&self) -> &Token { &self.location }
|
||||||
|
|
||||||
fn kind(&self) -> ElemKind {
|
fn kind(&self) -> ElemKind { (&self.block).into() }
|
||||||
if self.block == CodeKind::Inline {
|
|
||||||
ElemKind::Inline
|
|
||||||
} else {
|
|
||||||
ElemKind::Block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn element_name(&self) -> &'static str { "Code Block" }
|
fn element_name(&self) -> &'static str { "Code Block" }
|
||||||
|
|
||||||
|
@ -376,11 +387,11 @@ impl RegexRule for CodeRule {
|
||||||
let code_lang = match matches.get(2) {
|
let code_lang = match matches.get(2) {
|
||||||
None => "Plain Text".to_string(),
|
None => "Plain Text".to_string(),
|
||||||
Some(lang) => {
|
Some(lang) => {
|
||||||
let code_lang = lang.as_str().trim_end().trim_start().to_string();
|
let code_lang = lang.as_str().trim_start().trim_end().to_string();
|
||||||
if code_lang.is_empty() {
|
if code_lang.is_empty() {
|
||||||
reports.push(
|
reports.push(
|
||||||
Report::build(ReportKind::Error, token.source(), lang.start())
|
Report::build(ReportKind::Error, token.source(), lang.start())
|
||||||
.with_message("Missing code language")
|
.with_message("Missing Code Language")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source().clone(), lang.range()))
|
Label::new((token.source().clone(), lang.range()))
|
||||||
.with_message("No language specified")
|
.with_message("No language specified")
|
||||||
|
@ -391,8 +402,26 @@ impl RegexRule for CodeRule {
|
||||||
|
|
||||||
return reports;
|
return reports;
|
||||||
}
|
}
|
||||||
|
if Code::get_syntaxes()
|
||||||
|
.find_syntax_by_name(code_lang.as_str())
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
reports.push(
|
||||||
|
Report::build(ReportKind::Error, token.source(), lang.start())
|
||||||
|
.with_message("Unknown Code Language")
|
||||||
|
.with_label(
|
||||||
|
Label::new((token.source().clone(), lang.range()))
|
||||||
|
.with_message(format!(
|
||||||
|
"Language `{}` cannot be found",
|
||||||
|
code_lang.fg(parser.colors().info)
|
||||||
|
))
|
||||||
|
.with_color(parser.colors().error),
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: validate language
|
return reports;
|
||||||
|
}
|
||||||
|
|
||||||
code_lang
|
code_lang
|
||||||
}
|
}
|
||||||
|
@ -516,5 +545,214 @@ impl RegexRule for CodeRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Vec<(String, Function<'lua>)> { vec![] }
|
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Vec<(String, Function<'lua>)> {
|
||||||
|
let mut bindings = vec![];
|
||||||
|
bindings.push((
|
||||||
|
"push_inline".to_string(),
|
||||||
|
lua.create_function(|_, (language, content): (String, String)| {
|
||||||
|
CTX.with_borrow(|ctx| {
|
||||||
|
ctx.as_ref().map(|ctx| {
|
||||||
|
let theme = ctx
|
||||||
|
.document
|
||||||
|
.get_variable("code.theme")
|
||||||
|
.and_then(|var| Some(var.to_string()));
|
||||||
|
|
||||||
|
ctx.parser.push(
|
||||||
|
ctx.document,
|
||||||
|
Box::new(Code {
|
||||||
|
location: ctx.location.clone(),
|
||||||
|
block: CodeKind::Inline,
|
||||||
|
language,
|
||||||
|
name: None,
|
||||||
|
code: content,
|
||||||
|
theme,
|
||||||
|
line_offset: 1,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
bindings.push((
|
||||||
|
"push_miniblock".to_string(),
|
||||||
|
lua.create_function(
|
||||||
|
|_, (language, content, line_offset): (String, String, Option<usize>)| {
|
||||||
|
CTX.with_borrow(|ctx| {
|
||||||
|
ctx.as_ref().map(|ctx| {
|
||||||
|
let theme = ctx
|
||||||
|
.document
|
||||||
|
.get_variable("code.theme")
|
||||||
|
.and_then(|var| Some(var.to_string()));
|
||||||
|
|
||||||
|
ctx.parser.push(
|
||||||
|
ctx.document,
|
||||||
|
Box::new(Code {
|
||||||
|
location: ctx.location.clone(),
|
||||||
|
block: CodeKind::MiniBlock,
|
||||||
|
language,
|
||||||
|
name: None,
|
||||||
|
code: content,
|
||||||
|
theme,
|
||||||
|
line_offset: line_offset.unwrap_or(1),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
bindings.push((
|
||||||
|
"push_block".to_string(),
|
||||||
|
lua.create_function(
|
||||||
|
|_, (language, name, content, line_offset): (String, Option<String>, String, Option<usize>)| {
|
||||||
|
CTX.with_borrow(|ctx| {
|
||||||
|
ctx.as_ref().map(|ctx| {
|
||||||
|
let theme = ctx
|
||||||
|
.document
|
||||||
|
.get_variable("code.theme")
|
||||||
|
.and_then(|var| Some(var.to_string()));
|
||||||
|
|
||||||
|
ctx.parser.push(
|
||||||
|
ctx.document,
|
||||||
|
Box::new(Code {
|
||||||
|
location: ctx.location.clone(),
|
||||||
|
block: CodeKind::FullBlock,
|
||||||
|
language,
|
||||||
|
name,
|
||||||
|
code: content,
|
||||||
|
theme,
|
||||||
|
line_offset: line_offset.unwrap_or(1),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
bindings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::parser::langparser::LangParser;
|
||||||
|
use crate::parser::source::SourceFile;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn code_block() {
|
||||||
|
let source = Rc::new(SourceFile::with_content(
|
||||||
|
"".to_string(),
|
||||||
|
r#"
|
||||||
|
```[line_offset=32] C, Some Code...
|
||||||
|
static int INT32_MIN = 0x80000000;
|
||||||
|
```
|
||||||
|
%<nml.code.push_block("Lua", "From Lua", "print(\"Hello, World!\")", nil)>%
|
||||||
|
``Rust
|
||||||
|
fn fact(n: usize) -> usize
|
||||||
|
{
|
||||||
|
match n
|
||||||
|
{
|
||||||
|
0 | 1 => 1,
|
||||||
|
_ => n * fact(n-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
``
|
||||||
|
%<nml.code.push_miniblock("Bash", "NUM=$(($RANDOM % 10))", 18)>%
|
||||||
|
"#
|
||||||
|
.to_string(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
let parser = LangParser::default();
|
||||||
|
//let compiler = Compiler::new(Target::HTML, None);
|
||||||
|
let doc = parser.parse(source, None);
|
||||||
|
|
||||||
|
let borrow = doc.content().borrow();
|
||||||
|
let found = borrow
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| e.downcast_ref::<Code>())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(found[0].block, CodeKind::FullBlock);
|
||||||
|
assert_eq!(found[0].language, "C");
|
||||||
|
assert_eq!(found[0].name, Some("Some Code...".to_string()));
|
||||||
|
assert_eq!(found[0].code, "static int INT32_MIN = 0x80000000;");
|
||||||
|
assert_eq!(found[0].line_offset, 32);
|
||||||
|
|
||||||
|
assert_eq!(found[1].block, CodeKind::FullBlock);
|
||||||
|
assert_eq!(found[1].language, "Lua");
|
||||||
|
assert_eq!(found[1].name, Some("From Lua".to_string()));
|
||||||
|
assert_eq!(found[1].code, "print(\"Hello, World!\")");
|
||||||
|
assert_eq!(found[1].line_offset, 1);
|
||||||
|
|
||||||
|
assert_eq!(found[2].block, CodeKind::MiniBlock);
|
||||||
|
assert_eq!(found[2].language, "Rust");
|
||||||
|
assert_eq!(found[2].name, None);
|
||||||
|
assert_eq!(found[2].code, "fn fact(n: usize) -> usize\n{\n\tmatch n\n\t{\n\t\t0 | 1 => 1,\n\t\t_ => n * fact(n-1)\n\t}\n}");
|
||||||
|
assert_eq!(found[2].line_offset, 1);
|
||||||
|
|
||||||
|
assert_eq!(found[3].block, CodeKind::MiniBlock);
|
||||||
|
assert_eq!(found[3].language, "Bash");
|
||||||
|
assert_eq!(found[3].name, None);
|
||||||
|
assert_eq!(found[3].code, "NUM=$(($RANDOM % 10))");
|
||||||
|
assert_eq!(found[3].line_offset, 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn code_inline() {
|
||||||
|
let source = Rc::new(SourceFile::with_content(
|
||||||
|
"".to_string(),
|
||||||
|
r#"
|
||||||
|
``C, int fact(int n)``
|
||||||
|
``Plain Text, Text in a code block!``
|
||||||
|
%<nml.code.push_inline("C++", "std::vector<std::vector<int>> u;")>%
|
||||||
|
"#
|
||||||
|
.to_string(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
let parser = LangParser::default();
|
||||||
|
//let compiler = Compiler::new(Target::HTML, None);
|
||||||
|
let doc = parser.parse(source, None);
|
||||||
|
|
||||||
|
let borrow = doc.content().borrow();
|
||||||
|
let found = borrow
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.as_container()
|
||||||
|
.unwrap()
|
||||||
|
.contained()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| e.downcast_ref::<Code>())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(found[0].block, CodeKind::Inline);
|
||||||
|
assert_eq!(found[0].language, "C");
|
||||||
|
assert_eq!(found[0].name, None);
|
||||||
|
assert_eq!(found[0].code, "int fact(int n)");
|
||||||
|
assert_eq!(found[0].line_offset, 1);
|
||||||
|
|
||||||
|
assert_eq!(found[1].block, CodeKind::Inline);
|
||||||
|
assert_eq!(found[1].language, "Plain Text");
|
||||||
|
assert_eq!(found[1].name, None);
|
||||||
|
assert_eq!(found[1].code, "Text in a code block!");
|
||||||
|
assert_eq!(found[1].line_offset, 1);
|
||||||
|
|
||||||
|
assert_eq!(found[2].block, CodeKind::Inline);
|
||||||
|
assert_eq!(found[2].language, "C++");
|
||||||
|
assert_eq!(found[2].name, None);
|
||||||
|
assert_eq!(found[2].code, "std::vector<std::vector<int>> u;");
|
||||||
|
assert_eq!(found[2].line_offset, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ impl From<&TexKind> for ElemKind {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Tex {
|
struct Tex {
|
||||||
pub(self) location: Token,
|
pub(self) location: Token,
|
||||||
|
pub(self) mathmode: bool,
|
||||||
pub(self) kind: TexKind,
|
pub(self) kind: TexKind,
|
||||||
pub(self) env: String,
|
pub(self) env: String,
|
||||||
pub(self) tex: String,
|
pub(self) tex: String,
|
||||||
|
@ -176,7 +177,7 @@ impl Element for Tex {
|
||||||
let preamble = document
|
let preamble = document
|
||||||
.get_variable(format!("tex.{}.preamble", self.env).as_str())
|
.get_variable(format!("tex.{}.preamble", self.env).as_str())
|
||||||
.map_or("".to_string(), |var| var.to_string());
|
.map_or("".to_string(), |var| var.to_string());
|
||||||
let prepend = if self.kind == TexKind::Inline {
|
let prepend = if self.mathmode {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
} else {
|
} else {
|
||||||
document
|
document
|
||||||
|
@ -184,11 +185,10 @@ impl Element for Tex {
|
||||||
.map_or("".to_string(), |var| var.to_string() + "\n")
|
.map_or("".to_string(), |var| var.to_string() + "\n")
|
||||||
};
|
};
|
||||||
|
|
||||||
let latex = match self.kind {
|
let latex = if self.mathmode {
|
||||||
TexKind::Inline => {
|
Tex::format_latex(&fontsize, &preamble, &format!("${{{}}}$", self.tex))
|
||||||
Tex::format_latex(&fontsize, &preamble, &format!("${{{}}}$", self.tex))
|
} else {
|
||||||
}
|
Tex::format_latex(&fontsize, &preamble, &format!("{prepend}{}", self.tex))
|
||||||
_ => Tex::format_latex(&fontsize, &preamble, &format!("{prepend}{}", self.tex)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(mut con) = compiler.cache() {
|
if let Some(mut con) = compiler.cache() {
|
||||||
|
@ -236,7 +236,8 @@ impl TexRule {
|
||||||
);
|
);
|
||||||
Self {
|
Self {
|
||||||
re: [
|
re: [
|
||||||
Regex::new(r"\$\|(?:\[((?:\\.|[^\\\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\|\$)?").unwrap(),
|
Regex::new(r"\$\|(?:\[((?:\\.|[^\\\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\|\$)?")
|
||||||
|
.unwrap(),
|
||||||
Regex::new(r"\$(?:\[((?:\\.|[^\\\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\$)?").unwrap(),
|
Regex::new(r"\$(?:\[((?:\\.|[^\\\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\$)?").unwrap(),
|
||||||
],
|
],
|
||||||
properties: PropertyParser::new(props),
|
properties: PropertyParser::new(props),
|
||||||
|
@ -403,6 +404,7 @@ impl RegexRule for TexRule {
|
||||||
parser.push(
|
parser.push(
|
||||||
document,
|
document,
|
||||||
Box::new(Tex {
|
Box::new(Tex {
|
||||||
|
mathmode: index == 1,
|
||||||
location: token,
|
location: token,
|
||||||
kind: tex_kind,
|
kind: tex_kind,
|
||||||
env: tex_env.to_string(),
|
env: tex_env.to_string(),
|
||||||
|
@ -438,7 +440,6 @@ $[kind=block,env=another] e^{i\pi}=-1$
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let compiler = Compiler::new(Target::HTML, None);
|
|
||||||
let doc = parser.parse(source, None);
|
let doc = parser.parse(source, None);
|
||||||
|
|
||||||
let borrow = doc.content().borrow();
|
let borrow = doc.content().borrow();
|
||||||
|
@ -469,7 +470,6 @@ $[env=another] e^{i\pi}=-1$
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let compiler = Compiler::new(Target::HTML, None);
|
|
||||||
let doc = parser.parse(source, None);
|
let doc = parser.parse(source, None);
|
||||||
|
|
||||||
let borrow = doc.content().borrow();
|
let borrow = doc.content().borrow();
|
||||||
|
|
Loading…
Reference in a new issue