Compare commits

...

3 commits

Author SHA1 Message Date
554a83a63c Update Code Bindings 2024-07-27 08:23:15 +02:00
c0f7af84ec Update Code 2024-07-27 08:07:06 +02:00
23ac532061 Fixed Tex 2024-07-26 21:15:06 +02:00
3 changed files with 962 additions and 2819 deletions

3493
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -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);
}
} }

View file

@ -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();