Update docs

This commit is contained in:
ef3d0c3e 2024-08-05 09:04:09 +02:00
parent b5c8fbbfea
commit cf3491e5a7
12 changed files with 184 additions and 36 deletions

View file

@ -35,6 +35,10 @@ digraph {
[/graph] [/graph]
#+LAYOUT_END #+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]`` Graphs blocks are delimited by `` [graph]...[/graph]``
# Properties # Properties
@ -307,5 +311,5 @@ digraph UML_Class_diagram {
# Graphiz cache # 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. Unless you modify the graph or it's properties, it won't be rendered again, instead it will be sourced from the database.

56
docs/sections.nml Normal file
View file

@ -0,0 +1,56 @@
@import template.nml
@nav.previous = Getting Started
%<make_doc({}, "Sections", "Sections")>%
#{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": ["", "🔗", " "]
}
```

32
docs/start.nml Normal file
View file

@ -0,0 +1,32 @@
@import template.nml
@nav.previous = Index
%<make_doc({}, "Getting Started", "Getting Started")>%
# 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.

View file

@ -46,17 +46,17 @@ First
Centered layout align text to the center of the current block. 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. The ``Centered`` layout uses the `.centered` css class to center the text.
#### Properties ####+* Properties
* ``style`` Added css style to the div (defaults to none) * ``style`` Added css style to the div (defaults to none)
## Split ## Split
#### Style ####+* Style
The ``Split`` layout uses the `.split-container` and `.split` css class to create the desired layout. 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. 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) * ``style`` Added css style to the div (defaults to none)

View file

@ -1,4 +1,39 @@
@import ../template.nml @import ../template.nml
%<make_doc({"Styles"}, "User-Defined", "User-Defined Styles")>% %<make_doc({"Styles"}, "User-Defined", "User-Defined Styles")>%
# TODO # Defining a custom style
```Lua
%<[main]
function undercustom_start(color)
nml.raw.push("inline", "<span style=\"border-bottom: 1px dashed " .. color .. "\">")
end
function undercustom_end()
nml.raw.push("inline", "</span>")
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", "<span style=\"border-bottom: 1px dashed " .. color .. "\">")
end
function undercustom_end()
nml.raw.push("inline", "</span>")
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.

View file

@ -92,8 +92,6 @@ impl RegexRule for CommentRule {
return reports; return reports;
} }
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
} }
#[cfg(test)] #[cfg(test)]

View file

@ -17,7 +17,6 @@ use crate::document::customstyle::CustomStyle;
use crate::document::customstyle::CustomStyleToken; use crate::document::customstyle::CustomStyleToken;
use crate::document::document::Document; use crate::document::document::Document;
use crate::document::document::DocumentAccessors; use crate::document::document::DocumentAccessors;
use crate::lua::kernel::function_with_context;
use crate::lua::kernel::KernelContext; use crate::lua::kernel::KernelContext;
use crate::lua::kernel::CTX; use crate::lua::kernel::CTX;
use crate::parser::parser::Parser; use crate::parser::parser::Parser;
@ -423,6 +422,18 @@ impl Rule for CustomStyleRule {
)| { )| {
let mut result = Ok(()); 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 { let style = LuaCustomStyle {
tokens: CustomStyleToken::Pair(token_start, token_end), tokens: CustomStyleToken::Pair(token_start, token_end),
name: name.clone(), name: name.clone(),
@ -434,7 +445,7 @@ impl Rule for CustomStyleRule {
ctx.as_ref().map(|ctx| { ctx.as_ref().map(|ctx| {
if let Some(_) = ctx.parser.get_custom_style(name.as_str()) { if let Some(_) = ctx.parser.get_custom_style(name.as_str()) {
result = Err(BadArgument { result = Err(BadArgument {
to: Some("define_toggled".to_string()), to: Some("define_paired".to_string()),
pos: 1, pos: 1,
name: Some("name".to_string()), name: Some("name".to_string()),
cause: Arc::new(mlua::Error::external(format!( cause: Arc::new(mlua::Error::external(format!(

View file

@ -373,7 +373,7 @@ impl Rule for ListRule {
} }
// Parse entry content // 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( let entry_src = Rc::new(VirtualSource::new(
token.clone(), token.clone(),
"List Entry".to_string(), "List Entry".to_string(),

View file

@ -409,6 +409,7 @@ mod section_style {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::document::style::StyleHolder;
use crate::parser::langparser::LangParser; use crate::parser::langparser::LangParser;
use crate::parser::source::SourceFile; use crate::parser::source::SourceFile;
use crate::validate_document; use crate::validate_document;
@ -472,4 +473,29 @@ nml.section.push("6", 6, "", "refname")
Section { depth == 6, title == "6", reference == Some("refname".to_string()) }; 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::<SectionStyle>()
.unwrap();
assert_eq!(style.link_pos, SectionLinkPos::None);
assert_eq!(style.link, ["a".to_string(), "b".to_string(), "c".to_string()]);
}
} }

View file

@ -76,19 +76,3 @@ pub trait KernelHolder {
fn insert_kernel(&self, name: String, kernel: Kernel) -> RefMut<'_, Kernel>; 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<R, Error>
where
A: IntoLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
CTX.set(Some(unsafe { std::mem::transmute(context) }));
let ret = fun.call::<A, R>(args);
CTX.set(None);
ret
}

View file

@ -1,3 +1,4 @@
use std::any::Any;
use std::cell::Ref; use std::cell::Ref;
use std::cell::RefCell; use std::cell::RefCell;
use std::cell::RefMut; use std::cell::RefMut;
@ -48,6 +49,8 @@ pub struct LangParser {
// Parser state // Parser state
pub err_flag: RefCell<bool>, pub err_flag: RefCell<bool>,
pub matches: RefCell<Vec<(usize, Option<Box<dyn Any>>)>>,
pub state: RefCell<StateHolder>, pub state: RefCell<StateHolder>,
pub kernels: RefCell<HashMap<String, Kernel>>, pub kernels: RefCell<HashMap<String, Kernel>>,
pub styles: RefCell<HashMap<String, Rc<dyn ElementStyle>>>, pub styles: RefCell<HashMap<String, Rc<dyn ElementStyle>>>,
@ -61,6 +64,7 @@ impl LangParser {
rules: vec![], rules: vec![],
colors: ReportColors::with_colors(), colors: ReportColors::with_colors(),
err_flag: RefCell::new(false), err_flag: RefCell::new(false),
matches: RefCell::new(Vec::new()),
state: RefCell::new(StateHolder::new()), state: RefCell::new(StateHolder::new()),
kernels: RefCell::new(HashMap::new()), kernels: RefCell::new(HashMap::new()),
styles: RefCell::new(HashMap::new()), styles: RefCell::new(HashMap::new()),

View file

@ -13,6 +13,7 @@ use crate::document::document::Document;
use crate::document::element::Element; use crate::document::element::Element;
use crate::document::layout::LayoutHolder; use crate::document::layout::LayoutHolder;
use crate::document::style::StyleHolder; use crate::document::style::StyleHolder;
use crate::elements::customstyle::CustomStyleRule;
use crate::lua::kernel::KernelHolder; use crate::lua::kernel::KernelHolder;
use ariadne::Color; 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 /// When colors are disabled, all colors should resolve to empty string
fn colors(&self) -> &ReportColors; fn colors(&self) -> &ReportColors;
/// Gets a reference to all the [`Rule`]s defined for the parser
fn rules(&self) -> &Vec<Box<dyn Rule>>; fn rules(&self) -> &Vec<Box<dyn Rule>>;
/// Gets a mutable reference to all the [`Rule`]s defined for the parser
fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>>; fn rules_mut(&mut self) -> &mut Vec<Box<dyn Rule>>;
fn state(&self) -> Ref<'_, StateHolder>; fn state(&self) -> Ref<'_, StateHolder>;
@ -84,18 +87,13 @@ pub trait ParserStrategy {
impl<T: Parser> ParserStrategy for T { impl<T: Parser> ParserStrategy for T {
fn add_rule(&mut self, rule: Box<dyn Rule>, after: Option<&'static str>) -> Result<(), String> { fn add_rule(&mut self, rule: Box<dyn Rule>, after: Option<&'static str>) -> Result<(), String> {
// Error on duplicate rule
let rule_name = (*rule).name(); let rule_name = (*rule).name();
if let Err(e) = self.rules().iter().try_for_each(|rule| { // Error on duplicate rule
if (*rule).name() != rule_name { if let Some(_) = self.rules().iter().find(|rule| rule.name() == rule_name)
return Ok(()); {
}
return Err(format!( return Err(format!(
"Attempted to introduce duplicate rule: `{rule_name}`" "Attempted to introduce duplicate rule: `{rule_name}`"
)); ));
}) {
return Err(e);
} }
match after { match after {
@ -134,7 +132,7 @@ impl<T: Parser> ParserStrategy for T {
.zip(matches.iter_mut()) .zip(matches.iter_mut())
.for_each(|(rule, (matched_at, match_data))| { .for_each(|(rule, (matched_at, match_data))| {
// Don't upate if not stepped over yet // Don't upate if not stepped over yet
if *matched_at > cursor.pos && rule.downcast_ref::<crate::elements::customstyle::CustomStyleRule>().is_none() { if *matched_at > cursor.pos && rule.downcast_ref::<CustomStyleRule>().is_none() {
// TODO: maybe we should expose matches() so it becomes possible to dynamically register a new rule // TODO: maybe we should expose matches() so it becomes possible to dynamically register a new rule
return; return;
} }