From cf3491e5a7706979a8a9376fa4df7251a41cd279 Mon Sep 17 00:00:00 2001 From: ef3d0c3e Date: Mon, 5 Aug 2024 09:04:09 +0200 Subject: [PATCH] Update docs --- docs/external/graphviz.nml | 6 +++- docs/sections.nml | 56 ++++++++++++++++++++++++++++++++++++ docs/start.nml | 32 +++++++++++++++++++++ docs/styles/layout.nml | 8 +++--- docs/styles/user-defined.nml | 37 +++++++++++++++++++++++- src/elements/comment.rs | 2 -- src/elements/customstyle.rs | 15 ++++++++-- src/elements/list.rs | 2 +- src/elements/section.rs | 26 +++++++++++++++++ src/lua/kernel.rs | 16 ----------- src/parser/langparser.rs | 4 +++ src/parser/parser.rs | 16 +++++------ 12 files changed, 184 insertions(+), 36 deletions(-) create mode 100644 docs/sections.nml create mode 100644 docs/start.nml diff --git a/docs/external/graphviz.nml b/docs/external/graphviz.nml index d0dabf0..778f50d 100644 --- a/docs/external/graphviz.nml +++ b/docs/external/graphviz.nml @@ -35,6 +35,10 @@ digraph { [/graph] #+LAYOUT_END +The Graphviz functionnality requires the `dot` executable. More information on [Graphviz's website](file:///home/baraquiel/Programming/nml_rs/out/Graphviz.html). + +# Synopsis + Graphs blocks are delimited by `` [graph]...[/graph]`` # Properties @@ -307,5 +311,5 @@ digraph UML_Class_diagram { # Graphiz cache -Rendered Graphviz graphs that have been rendered to **svg** are stored in the cache database, under table ``cached_dot``. +Graphviz graphs that have been rendered to **svg** are stored in the cache database, under table ``cached_dot``. Unless you modify the graph or it's properties, it won't be rendered again, instead it will be sourced from the database. diff --git a/docs/sections.nml b/docs/sections.nml new file mode 100644 index 0000000..d12c188 --- /dev/null +++ b/docs/sections.nml @@ -0,0 +1,56 @@ +@import template.nml +@nav.previous = Getting Started +%% + +#{first} Sections + +To add a section to your document, put one or more ``Plain Text, #`` at the start of the line, followed a space and the name of your section. + + +Which will render as: + +#+LAYOUT_BEGIN Split +:: Make sure they don't pollute the ToC +#+ Section name +##+ Subsection +##*+ Unnumbered section +##+ Unnumbered section +#+ This section is not in the ToC + +#+LAYOUT_NEXT + +Given by the following: +`` +# Section name +## Subsection +#* Unnumbered section +#+ This section is not in the ToC +`` +#+LAYOUT_END + +# Sections references + +You can create a referenceable section by using ``Plain Text, #{refname}``, where `refname` is an internal reference name for use only within this document. +You can then create a clickable reference to this section: ``§{refname}`` or ``§{refname}[caption=Click me!]``. Below is an example of this in action: + +###{refname}+* Section +§{refname}[caption=Click me!] or §{first}[caption=First section] + +`` +###{refname}+* Section +§{refname}[caption=Click me!] or §{first}[caption=First section] +`` + +# Section styling + +The styling for the section link is controlled by the style key ``style.section`` + + * ``link_pos``: `Before|After|None` Position of the section link. + * ``link``: `[Before, Link, After]` 3 strings-array + +```JSON, Default Style +{ + "link_pos": "Before", + "link": ["", "🔗", " "] +} +``` diff --git a/docs/start.nml b/docs/start.nml new file mode 100644 index 0000000..bf54848 --- /dev/null +++ b/docs/start.nml @@ -0,0 +1,32 @@ +@import template.nml +@nav.previous = Index +%% + +# Building NML + +You need at least the nightly version of rustc to compile NML. +Instruction for your operating system can be found on [Rust's website](https://forge.rust-lang.org/infra/other-installation-methods.html). +You'll also need liblua 5.4 installed. You can then move the `nml` executable in `target/release/nml` into your `\$PATH` + +``cargo build --bin nml`` or for release mode: ``cargo build --release --bin nml`` + +# Building your first document + + * ``nml -i input.nml -o output.html`` + +# Using the cache + +NML relies on sqlite to keep a cache of precompiled elements that take a long time to process (e.g $|[kind=inline] \LaTeX|$). +To enable caching, use option `-d` with a path: ``-d cache.db``. You can reuse the same cache for multiple documents and benefit from cached elements. +Note that in directory-processing mode, a cache is required so that only modified ``.nml`` files get reprocessed. + +# Directory-Processing mode + +To use directory-processing mode, you need to pass an input directory and an output directory. Directory-processing mode requires that you use a database, so that it knows which documents have already been compiled. If the output directory doesn't exist, it will be automatically created. + +Compiling the docs: +``Plain Text, +nml -i docs -o docs_out -d cache.db +`` + +If you modify an ``Plain Text,@import``ed file, you will need to use the ``--force-rebuild`` option, as NML currently doesn't track which files are imported by other files. diff --git a/docs/styles/layout.nml b/docs/styles/layout.nml index 8104100..41eac58 100644 --- a/docs/styles/layout.nml +++ b/docs/styles/layout.nml @@ -46,17 +46,17 @@ First Centered layout align text to the center of the current block. -#### Style +####+* Style The ``Centered`` layout uses the `.centered` css class to center the text. -#### Properties +####+* Properties * ``style`` Added css style to the div (defaults to none) ## Split -#### Style +####+* Style The ``Split`` layout uses the `.split-container` and `.split` css class to create the desired layout. If you wish to modify the relative width of the splits: add `style=flex: 0.5` in the properties, this makes the following split half the width of the other splits. -#### Properties +####+* Properties * ``style`` Added css style to the div (defaults to none) diff --git a/docs/styles/user-defined.nml b/docs/styles/user-defined.nml index 4082253..bcd0216 100644 --- a/docs/styles/user-defined.nml +++ b/docs/styles/user-defined.nml @@ -1,4 +1,39 @@ @import ../template.nml %% -# TODO +# Defining a custom style +```Lua +%<[main] +function undercustom_start(color) + nml.raw.push("inline", "") +end + +function undercustom_end() + nml.raw.push("inline", "") +end + +nml.custom_style.define_toggled("Undercustom Red", "~", "undercustom_start(\"red\")", "undercustom_end()") +nml.custom_style.define_paired("Undercustom Green", "[|", "|]", "undercustom_start(\"Green\")", "undercustom_end()") +>% +``` + +%<[main] +function undercustom_start(color) + nml.raw.push("inline", "") +end + +function undercustom_end() + nml.raw.push("inline", "") +end + +nml.custom_style.define_toggled("Undercustom Red", "~", "undercustom_start(\"red\")", "undercustom_end()") +nml.custom_style.define_paired("Undercustom Green", "[|", "|]", "undercustom_start(\"Green\")", "undercustom_end()") +>% +Results in the following: + * ``Plain Text,~Dashed underline~`` → ~Dashed underline~ + * ``Plain Text,[|Dashed underline|]`` → [|Dashed underline|] + +# Limitations + + * Custom styles cannot be removed and will be defined through the entire document + * Custom styles defined from lua must have their `start` and `end` functions in the `main` lua kernel. diff --git a/src/elements/comment.rs b/src/elements/comment.rs index 308ef10..2363243 100644 --- a/src/elements/comment.rs +++ b/src/elements/comment.rs @@ -92,8 +92,6 @@ impl RegexRule for CommentRule { return reports; } - - fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option)>> { None } } #[cfg(test)] diff --git a/src/elements/customstyle.rs b/src/elements/customstyle.rs index 6a256a6..c235083 100644 --- a/src/elements/customstyle.rs +++ b/src/elements/customstyle.rs @@ -17,7 +17,6 @@ use crate::document::customstyle::CustomStyle; use crate::document::customstyle::CustomStyleToken; use crate::document::document::Document; use crate::document::document::DocumentAccessors; -use crate::lua::kernel::function_with_context; use crate::lua::kernel::KernelContext; use crate::lua::kernel::CTX; use crate::parser::parser::Parser; @@ -423,6 +422,18 @@ impl Rule for CustomStyleRule { )| { let mut result = Ok(()); + if token_start == token_end + { + return Err(BadArgument { + to: Some("define_paired".to_string()), + pos: 3, + name: Some("token_end".to_string()), + cause: Arc::new(mlua::Error::external(format!( + "Custom style with name `{name}` cannot be defined: The start token must differ from the end token, use `define_toggled` insteda" + ))), + }); + } + let style = LuaCustomStyle { tokens: CustomStyleToken::Pair(token_start, token_end), name: name.clone(), @@ -434,7 +445,7 @@ impl Rule for CustomStyleRule { ctx.as_ref().map(|ctx| { if let Some(_) = ctx.parser.get_custom_style(name.as_str()) { result = Err(BadArgument { - to: Some("define_toggled".to_string()), + to: Some("define_paired".to_string()), pos: 1, name: Some("name".to_string()), cause: Arc::new(mlua::Error::external(format!( diff --git a/src/elements/list.rs b/src/elements/list.rs index 889b16d..56d943f 100644 --- a/src/elements/list.rs +++ b/src/elements/list.rs @@ -373,7 +373,7 @@ impl Rule for ListRule { } // Parse entry content - let token = Token::new(end_cursor.pos..end_cursor.pos, end_cursor.source.clone()); + let token = Token::new(entry_start..end_cursor.pos, end_cursor.source.clone()); let entry_src = Rc::new(VirtualSource::new( token.clone(), "List Entry".to_string(), diff --git a/src/elements/section.rs b/src/elements/section.rs index e303674..0662084 100644 --- a/src/elements/section.rs +++ b/src/elements/section.rs @@ -409,6 +409,7 @@ mod section_style { #[cfg(test)] mod tests { + use crate::document::style::StyleHolder; use crate::parser::langparser::LangParser; use crate::parser::source::SourceFile; use crate::validate_document; @@ -472,4 +473,29 @@ nml.section.push("6", 6, "", "refname") Section { depth == 6, title == "6", reference == Some("refname".to_string()) }; ); } + + #[test] + fn style() { + let source = Rc::new(SourceFile::with_content( + "".to_string(), + r#" +@@style.section = { + "link_pos": "None", + "link": ["a", "b", "c"] +} + "# + .to_string(), + None, + )); + let parser = LangParser::default(); + let _ = parser.parse(source, None); + + let style = parser + .current_style(section_style::STYLE_KEY) + .downcast_rc::() + .unwrap(); + + assert_eq!(style.link_pos, SectionLinkPos::None); + assert_eq!(style.link, ["a".to_string(), "b".to_string(), "c".to_string()]); + } } diff --git a/src/lua/kernel.rs b/src/lua/kernel.rs index 8503bfb..17798a2 100644 --- a/src/lua/kernel.rs +++ b/src/lua/kernel.rs @@ -76,19 +76,3 @@ pub trait KernelHolder { fn insert_kernel(&self, name: String, kernel: Kernel) -> RefMut<'_, Kernel>; } - -/// Runs a lua function with a context -/// -/// This is the only way lua functions shoule be ran, because exported -/// functions may require the context in order to operate -pub fn function_with_context<'lua, A, R>(context: KernelContext, fun: &Function<'lua>, args: A) -> Result -where - A: IntoLuaMulti<'lua>, - R: FromLuaMulti<'lua>, -{ - CTX.set(Some(unsafe { std::mem::transmute(context) })); - let ret = fun.call::(args); - CTX.set(None); - - ret -} diff --git a/src/parser/langparser.rs b/src/parser/langparser.rs index f0bba36..0f66114 100644 --- a/src/parser/langparser.rs +++ b/src/parser/langparser.rs @@ -1,3 +1,4 @@ +use std::any::Any; use std::cell::Ref; use std::cell::RefCell; use std::cell::RefMut; @@ -48,6 +49,8 @@ pub struct LangParser { // Parser state pub err_flag: RefCell, + pub matches: RefCell>)>>, + pub state: RefCell, pub kernels: RefCell>, pub styles: RefCell>>, @@ -61,6 +64,7 @@ impl LangParser { rules: vec![], colors: ReportColors::with_colors(), err_flag: RefCell::new(false), + matches: RefCell::new(Vec::new()), state: RefCell::new(StateHolder::new()), kernels: RefCell::new(HashMap::new()), styles: RefCell::new(HashMap::new()), diff --git a/src/parser/parser.rs b/src/parser/parser.rs index a01ea16..43bf465 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -13,6 +13,7 @@ use crate::document::document::Document; use crate::document::element::Element; use crate::document::layout::LayoutHolder; use crate::document::style::StyleHolder; +use crate::elements::customstyle::CustomStyleRule; use crate::lua::kernel::KernelHolder; use ariadne::Color; @@ -50,7 +51,9 @@ pub trait Parser: KernelHolder + StyleHolder + LayoutHolder + CustomStyleHolder /// When colors are disabled, all colors should resolve to empty string fn colors(&self) -> &ReportColors; + /// Gets a reference to all the [`Rule`]s defined for the parser fn rules(&self) -> &Vec>; + /// Gets a mutable reference to all the [`Rule`]s defined for the parser fn rules_mut(&mut self) -> &mut Vec>; fn state(&self) -> Ref<'_, StateHolder>; @@ -84,18 +87,13 @@ pub trait ParserStrategy { impl ParserStrategy for T { fn add_rule(&mut self, rule: Box, after: Option<&'static str>) -> Result<(), String> { - // Error on duplicate rule let rule_name = (*rule).name(); - if let Err(e) = self.rules().iter().try_for_each(|rule| { - if (*rule).name() != rule_name { - return Ok(()); - } - + // Error on duplicate rule + if let Some(_) = self.rules().iter().find(|rule| rule.name() == rule_name) + { return Err(format!( "Attempted to introduce duplicate rule: `{rule_name}`" )); - }) { - return Err(e); } match after { @@ -134,7 +132,7 @@ impl ParserStrategy for T { .zip(matches.iter_mut()) .for_each(|(rule, (matched_at, match_data))| { // Don't upate if not stepped over yet - if *matched_at > cursor.pos && rule.downcast_ref::().is_none() { + if *matched_at > cursor.pos && rule.downcast_ref::().is_none() { // TODO: maybe we should expose matches() so it becomes possible to dynamically register a new rule return; }