Compare commits

..

2 commits

Author SHA1 Message Date
0982527944 WIP Navigation sorting 2024-08-03 18:43:54 +02:00
90cf691737 Bindings for style 2024-08-03 11:53:59 +02:00
10 changed files with 274 additions and 128 deletions

74
Cargo.lock generated
View file

@ -56,7 +56,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -67,7 +67,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -277,6 +277,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "erased-serde"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d"
dependencies = [
"serde",
"typeid",
]
[[package]]
name = "errno"
version = "0.3.9"
@ -389,7 +399,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -696,10 +706,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d111deb18a9c9bd33e1541309f4742523bfab01d276bfa9a27519f6de9c11dc7"
dependencies = [
"bstr",
"erased-serde",
"mlua-sys",
"num-traits",
"once_cell",
"rustc-hash",
"serde",
"serde-value",
]
[[package]]
@ -802,6 +815,15 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "ordered-float"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
@ -852,7 +874,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -883,7 +905,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -931,9 +953,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.79"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
@ -949,9 +971,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.35"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
@ -1159,6 +1181,16 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.204"
@ -1167,7 +1199,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -1189,7 +1221,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -1231,9 +1263,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.53"
version = "2.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [
"proc-macro2",
"quote",
@ -1291,7 +1323,7 @@ checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -1371,7 +1403,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -1438,7 +1470,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -1466,7 +1498,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]
[[package]]
@ -1478,6 +1510,12 @@ dependencies = [
"once_cell",
]
[[package]]
name = "typeid"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf"
[[package]]
name = "typenum"
version = "1.17.0"
@ -1723,5 +1761,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.53",
"syn 2.0.72",
]

View file

@ -25,7 +25,7 @@ graphviz-rust = "0.9.0"
lazy_static = "1.5.0"
lsp-server = "0.7.6"
lsp-types = "0.97.0"
mlua = { version = "0.9.9", features = ["lua54", "vendored"] }
mlua = { version = "0.9.9", features = ["lua54", "vendored", "serialize"] }
regex = "1.10.3"
rusqlite = "0.31.0"
rust-crypto = "0.2.36"

View file

@ -25,3 +25,8 @@ function make_doc(categories, title, page_title)
nml.variable.insert("compiler.output", page_title .. ".html")
end
>@
@@style.section = {
"link_pos": "Before",
"link": ["", "🔗 ", " "]
}

View file

@ -7,7 +7,7 @@ use super::compiler::Target;
#[derive(Debug, Default)]
pub struct NavEntry {
pub(self) entries: Vec<(String, String)>,
pub(self) entries: Vec<(String, String, Option<String>)>,
pub(self) children: HashMap<String, NavEntry>,
}
@ -34,7 +34,7 @@ impl NavEntry {
depth: usize,
) {
// Orphans = Links
for (title, path) in &entry.entries {
for (title, path, _) in &entry.entries {
result.push_str(
format!(
r#"<li><a href="{}">{}</a></li>"#,
@ -75,6 +75,36 @@ impl NavEntry {
}
result
}
/// Gets the insert index of the entry inside an already sorted entry list
fn sort_entry(
title: &String,
previous: &Option<String>,
entries: &Vec<(String, String, Option<String>)>,
) -> usize {
let mut insert_at = 0;
if let Some(previous) = &previous
// Using sort key
{
for (i, (ent_title, _, _)) in entries.iter().enumerate() {
if ent_title == previous {
insert_at = i + 1;
break;
}
}
}
// Then sort alphabetically
for (ent_title, _, ent_previous) in entries.iter().skip(insert_at) {
if (previous.is_some() && ent_previous != previous) || ent_title > title {
break;
}
insert_at += 1;
}
insert_at
}
}
pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, String> {
@ -83,12 +113,16 @@ pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, Strin
children: HashMap::new(),
};
// All paths (for duplicate checking)
let mut all_paths = HashMap::new();
for doc in docs {
let cat = doc.get_variable("nav.category");
let subcat = doc.get_variable("nav.subcategory");
let title = doc
.get_variable("nav.title")
.or(doc.get_variable("doc.title"));
let previous = doc.get_variable("nav.previous").map(|s| s.clone());
let path = doc.get_variable("compiler.output");
let (title, path) = match (title, path) {
@ -142,8 +176,67 @@ pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, Strin
&mut nav
};
pent.entries.push((title.clone(), path.clone()))
// Find duplicates titles in current parent
for (ent_title, _, _) in &pent.entries {
if ent_title == title {
return Err(format!(
"Conflicting entry title `{title}` for entries with the same parent: ({})",
pent.entries
.iter()
.map(|(title, _, _)| title.clone())
.collect::<Vec<_>>()
.join(", ")
));
}
}
// Find duplicate paths
if let Some(dup_title) = all_paths.get(path) {
return Err(format!("Conflicting paths: `{path}`. Previously used for entry: `{dup_title}`, conflicting use in `{title}`"));
}
all_paths.insert(path.clone(), title.clone());
pent.entries.insert(
NavEntry::sort_entry(title, &previous, &pent.entries),
(title.clone(), path.clone(), previous),
);
}
Ok(nav)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sort() {
let entries: Vec<(String, String, Option<String>)> = vec![
("Root".into(), "".into(), None),
("First".into(), "".into(), Some("Root".into())),
("1".into(), "".into(), Some("First".into())),
("2".into(), "".into(), Some("First".into())),
];
assert_eq!(
NavEntry::sort_entry(&"E".into(), &Some("Root".into()), &entries),
1
);
assert_eq!(
NavEntry::sort_entry(&"G".into(), &Some("Root".into()), &entries),
2
);
// Orphans
assert_eq!(NavEntry::sort_entry(&"Q".into(), &None, &entries), 0);
assert_eq!(NavEntry::sort_entry(&"S".into(), &None, &entries), 4);
assert_eq!(
NavEntry::sort_entry(&"1.1".into(), &Some("First".into()), &entries),
3
);
assert_eq!(
NavEntry::sort_entry(&"2.1".into(), &Some("First".into()), &entries),
4
);
}
}

View file

@ -20,6 +20,13 @@ pub trait ElementStyle: Downcast + core::fmt::Debug {
/// Serializes sytle into json string
fn to_json(&self) -> String;
/// Attempts to deserialize lua table into a new style
fn from_lua(
&self,
lua: &mlua::Lua,
value: mlua::Value,
) -> Result<Rc<dyn ElementStyle>, mlua::Error>;
}
impl_downcast!(ElementStyle);
@ -31,7 +38,7 @@ pub trait StyleHolder {
fn styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>>;
/// Checks if a given style key is registered
fn is_registered(&self, style_key: &str) -> bool { self.styles().contains_key(style_key) }
fn is_style_registered(&self, style_key: &str) -> bool { self.styles().contains_key(style_key) }
/// Gets the current active style for an element
/// NOTE: Will panic if a style is not defined for a given element
@ -59,6 +66,15 @@ macro_rules! impl_elementstyle {
}
fn to_json(&self) -> String { serde_json::to_string(self).unwrap() }
fn from_lua(
&self,
lua: &mlua::Lua,
value: mlua::Value,
) -> Result<std::rc::Rc<dyn ElementStyle>, mlua::Error> {
mlua::LuaSerdeExt::from_value::<$t>(lua, value)
.map(|obj| std::rc::Rc::new(obj) as std::rc::Rc<dyn ElementStyle>)
}
}
};
}

View file

@ -1,16 +1,24 @@
use std::any::Any;
use std::ops::Range;
use std::rc::Rc;
use std::sync::Arc;
use ariadne::Fmt;
use ariadne::Label;
use ariadne::Report;
use ariadne::ReportKind;
use mlua::Error::BadArgument;
use mlua::Function;
use mlua::Lua;
use mlua::LuaSerdeExt;
use mlua::Table;
use mlua::Value;
use regex::Captures;
use regex::Regex;
use crate::document::document::Document;
use crate::document::{self};
use crate::lua::kernel::CTX;
use crate::parser::parser::Parser;
use crate::parser::rule::RegexRule;
use crate::parser::rule::Rule;
@ -58,71 +66,6 @@ impl ElemStyleRule {
impl Rule for ElemStyleRule {
fn name(&self) -> &'static str { "Element Style" }
/*
fn on_regex_match<'a>(
&self,
_: usize,
parser: &dyn Parser,
_document: &'a dyn Document,
token: Token,
matches: Captures,
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
let mut reports = vec![];
// Get value
let new_style = if let Some(value) = matches.get(2) {
let value_str = match VariableRule::validate_value(value.as_str()) {
Err(err) => {
reports.push(
Report::build(ReportKind::Error, token.source(), value.start())
.with_message("Invalid Style Value")
.with_label(
Label::new((token.source(), value.range()))
.with_message(format!(
"Value `{}` is not allowed: {err}",
value.as_str().fg(parser.colors().highlight)
))
.with_color(parser.colors().error),
)
.finish(),
);
return reports;
}
Ok(value) => value,
};
// Attempt to serialize
match style.from_json(value_str.as_str()) {
Err(err) => {
reports.push(
Report::build(ReportKind::Error, token.source(), value.start())
.with_message("Invalid Style Value")
.with_label(
Label::new((token.source(), value.range()))
.with_message(format!(
"Failed to serialize `{}` into style with key `{}`: {err}",
value_str.fg(parser.colors().highlight),
style.key().fg(parser.colors().info)
))
.with_color(parser.colors().error),
)
.finish(),
);
return reports;
}
Ok(style) => style,
}
} else {
panic!("Unknown error")
};
parser.set_style(new_style);
reports
}
*/
fn next_match(&self, cursor: &Cursor) -> Option<(usize, Box<dyn Any>)> {
self.start_re
.find_at(cursor.source.content(), cursor.pos)
@ -164,7 +107,7 @@ impl Rule for ElemStyleRule {
}
// Check if key exists
if !parser.is_registered(trimmed) {
if !parser.is_style_registered(trimmed) {
reports.push(
Report::build(ReportKind::Error, cursor.source.clone(), key.start())
.with_message("Unknown Style Key")
@ -240,4 +183,46 @@ impl Rule for ElemStyleRule {
(cursor, reports)
}
fn lua_bindings<'lua>(&self, lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> {
let mut bindings = vec![];
bindings.push((
"set".to_string(),
lua.create_function(|lua, (style_key, new_style): (String, Value)| {
let mut result = Ok(());
CTX.with_borrow(|ctx| {
ctx.as_ref().map(|ctx| {
if !ctx.parser.is_style_registered(style_key.as_str()) {
result = Err(BadArgument {
to: Some("set".to_string()),
pos: 1,
name: Some("style_key".to_string()),
cause: Arc::new(mlua::Error::external(format!(
"Unable to find style with key: {style_key}"
))),
});
return;
}
let style = ctx.parser.current_style(style_key.as_str());
let new_style = match style.from_lua(lua, new_style) {
Err(err) => {
result = Err(err);
return;
}
Ok(new_style) => new_style,
};
ctx.parser.set_current_style(new_style);
})
});
result
})
.unwrap(),
));
Some(bindings)
}
}

View file

@ -81,7 +81,7 @@ impl ReferenceRule {
),
);
Self {
re: [Regex::new(r"§\{(.*)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
re: [Regex::new(r"§\{(.*?)\}(\[((?:\\.|[^\\\\])*?)\])?").unwrap()],
properties: PropertyParser{ properties: props },
}
}

View file

@ -46,16 +46,17 @@ impl Element for Section {
match compiler.target() {
Target::HTML => {
// Section numbering
let number = if (self.kind & section_kind::NO_NUMBER) == section_kind::NO_NUMBER {
let number = if (self.kind & section_kind::NO_NUMBER) != section_kind::NO_NUMBER {
let numbering = compiler.section_counter(self.depth);
let number = " ".to_string()
+ numbering
.iter()
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(".")
.as_str();
number
let mut result = String::new();
for num in numbering.iter()
{
result = result + num.to_string().as_str() + ".";
}
result += " ";
result
} else {
String::new()
};
@ -71,8 +72,10 @@ impl Element for Section {
let refname = Compiler::refname(compiler.target(), self.title.as_str());
let link = format!(
"<a class=\"section-link\" href=\"#{refname}\">{}</a>",
Compiler::sanitize(compiler.target(), self.style.link.as_str())
"{}<a class=\"section-link\" href=\"#{refname}\">{}</a>{}",
Compiler::sanitize(compiler.target(), self.style.link[0].as_str()),
Compiler::sanitize(compiler.target(), self.style.link[1].as_str()),
Compiler::sanitize(compiler.target(), self.style.link[2].as_str())
);
if self.style.link_pos == SectionLinkPos::After {
@ -123,7 +126,7 @@ impl ReferenceableElement for Section {
);
Ok(format!(
"<a class=\"section-ref\" href=\"#{}\">{caption}</a>",
"<a class=\"section-reference\" href=\"#{}\">{caption}</a>",
Compiler::refname(compiler.target(), self.title.as_str())
))
}
@ -390,14 +393,14 @@ mod section_style {
#[derive(Debug, Serialize, Deserialize)]
pub struct SectionStyle {
pub link_pos: SectionLinkPos,
pub link: String,
pub link: [String; 3],
}
impl Default for SectionStyle {
fn default() -> Self {
Self {
link_pos: SectionLinkPos::After,
link: "🔗".to_string(),
link_pos: SectionLinkPos::Before,
link: ["".into(), "🔗".into(), " ".into()],
}
}
}

View file

@ -1,6 +1,6 @@
use std::{cell::{RefCell, RefMut}, collections::HashMap, rc::Rc};
use crate::{document::{document::Document, element::Element}, lua::kernel::{Kernel, KernelHolder}, parser::{parser::{Parser, ReportColors}, rule::Rule, source::{Cursor, Source}, state::StateHolder}};
use crate::{document::{document::Document, element::Element, style::{ElementStyle, StyleHolder}}, lua::kernel::{Kernel, KernelHolder}, parser::{parser::{Parser, ReportColors}, rule::Rule, source::{Cursor, Source}, state::StateHolder}};
#[derive(Debug, Clone)]
pub struct LineCursor
@ -146,3 +146,13 @@ impl KernelHolder for LsParser
self.get_kernel(name.as_str()).unwrap()
}
}
impl StyleHolder for LsParser {
fn styles(&self) -> std::cell::Ref<'_, HashMap<String, Rc<dyn ElementStyle>>> {
todo!()
}
fn styles_mut(&self) -> RefMut<'_, HashMap<String, Rc<dyn ElementStyle>>> {
todo!()
}
}

View file

@ -1,11 +1,13 @@
use std::cell::{RefCell, RefMut};
use std::cell::RefCell;
use std::cell::RefMut;
use mlua::Lua;
use crate::{document::document::Document, parser::{parser::Parser, source::Token}};
use crate::document::document::Document;
use crate::parser::parser::Parser;
use crate::parser::source::Token;
pub struct KernelContext<'a, 'b>
{
pub struct KernelContext<'a, 'b> {
pub location: Token,
pub parser: &'a dyn Parser,
pub document: &'b dyn Document<'b>,
@ -17,8 +19,7 @@ thread_local! {
}
#[derive(Debug)]
pub struct Kernel
{
pub struct Kernel {
lua: Lua,
}
@ -32,15 +33,12 @@ impl Kernel {
{
let nml_table = lua.create_table().unwrap();
for rule in parser.rules()
{
if let Some(bindings) = rule.lua_bindings(&lua)
{
for rule in parser.rules() {
if let Some(bindings) = rule.lua_bindings(&lua) {
let table = lua.create_table().unwrap();
let name = rule.name().to_lowercase();
let name = rule.name().to_lowercase().replace(' ', "_");
for (fun_name, fun) in bindings
{
for (fun_name, fun) in bindings {
table.set(fun_name, fun).unwrap();
}
@ -57,10 +55,9 @@ impl Kernel {
///
/// This is the only way lua code shoule be ran, because exported
/// functions may require the context in order to operate
pub fn run_with_context<T, F>(&self, context: KernelContext, f: F)
-> T
pub fn run_with_context<T, F>(&self, context: KernelContext, f: F) -> T
where
F: FnOnce(&Lua) -> T
F: FnOnce(&Lua) -> T,
{
CTX.set(Some(unsafe { std::mem::transmute(context) }));
let ret = f(&self.lua);
@ -70,8 +67,7 @@ impl Kernel {
}
}
pub trait KernelHolder
{
pub trait KernelHolder {
fn get_kernel(&self, name: &str) -> Option<RefMut<'_, Kernel>>;
fn insert_kernel(&self, name: String, kernel: Kernel) -> RefMut<'_, Kernel>;