Compare commits
No commits in common. "e4ce3edc4df0227ad7878386ab47aa84d010d9c3" and "a00db70bf64d7dc2d29b4ae1452247b45a2147d9" have entirely different histories.
e4ce3edc4d
...
a00db70bf6
15 changed files with 195 additions and 829 deletions
301
docs/external/graphviz.nml
vendored
301
docs/external/graphviz.nml
vendored
|
@ -1,31 +1,28 @@
|
||||||
@import ../template.nml
|
@import ../template.nml
|
||||||
%<make_doc({"External Tools"}, "Graphviz", "Graphviz")>%
|
%<make_doc({"External Tools"}, "Graphviz", "Graphvis")>%
|
||||||
|
|
||||||
# Graphs from graphviz
|
# Graphs from graphviz
|
||||||
|
|
||||||
#+LAYOUT_BEGIN Centered
|
[graph]
|
||||||
[graph][width=50%]
|
|
||||||
digraph {
|
digraph {
|
||||||
bgcolor=transparent;
|
bgcolor=transparent;
|
||||||
graph[fontcolor=darkgray];
|
|
||||||
node[fontcolor=darkgray];
|
|
||||||
edge[fontcolor=darkgray, color=gray90];
|
|
||||||
|
|
||||||
filelist [shape=box, color=orange, label="File List"];
|
filelist [color=green, label="File List"];
|
||||||
doclist [shape=box, color=orange, label="Document List"];
|
doclist [color=green, label="Document List"];
|
||||||
iscached [shape=diamond, color=red, label="Cached?"];
|
iscached [shape=diamond, color=red, label="Cached?"];
|
||||||
parse [shape=box, color=white, label=Parse];
|
parse [color=white, label=Parse];
|
||||||
compile [shape=box,color=white, label=Compile];
|
compile [color=white, label=Compile];
|
||||||
cache [shape=box, color=orange, label=Cache];
|
cache [shape=box, color=blue, label=Cache];
|
||||||
|
|
||||||
|
edge [color=gray]
|
||||||
filelist -> iscached;
|
filelist -> iscached;
|
||||||
iscached -> cache[dir=both];
|
iscached -> cache[dir=both];
|
||||||
iscached -> doclist[label="Yes"];
|
iscached -> doclist[label="+"];
|
||||||
|
|
||||||
iscached -> parse[label="No"];
|
iscached -> parse[label="No"];
|
||||||
parse -> compile;
|
parse -> compile;
|
||||||
compile -> cache[label=""];
|
compile -> cache[label="+"];
|
||||||
compile -> doclist[label=""];
|
compile -> doclist[label="+"];
|
||||||
|
|
||||||
buildnav [color=white, label="Build Navigation"];
|
buildnav [color=white, label="Build Navigation"];
|
||||||
doclist -> buildnav;
|
doclist -> buildnav;
|
||||||
|
@ -33,279 +30,3 @@ digraph {
|
||||||
buildnav -> output;
|
buildnav -> output;
|
||||||
}
|
}
|
||||||
[/graph]
|
[/graph]
|
||||||
#+LAYOUT_END
|
|
||||||
|
|
||||||
Graphs blocks are delimited by `` [graph]...[/graph]``
|
|
||||||
|
|
||||||
# Properties
|
|
||||||
* ``layout`` The layout engine, defaults to `dot`
|
|
||||||
see [Graphviz's documentation](https://graphviz.org/docs/layouts/), allowed values:
|
|
||||||
*- [`dot`](https://graphviz.org/docs/layouts/dot/)
|
|
||||||
*- [`neato`](https://graphviz.org/docs/layouts/neato/)
|
|
||||||
*- [`fdp`](https://graphviz.org/docs/layouts/fdp/)
|
|
||||||
*- [`sfdp`](https://graphviz.org/docs/layouts/sfdp/)
|
|
||||||
*- [`circo`](https://graphviz.org/docs/layouts/circo/)
|
|
||||||
*- [`twopi`](https://graphviz.org/docs/layouts/twopi/)
|
|
||||||
*- [`osage`](https://graphviz.org/docs/layouts/osage/)
|
|
||||||
*- [`patchwork`](https://graphviz.org/docs/layouts/patchwork/)
|
|
||||||
* ``width`` The resulting svg's width property, defaults to `100%`
|
|
||||||
|
|
||||||
# Examples
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#+LAYOUT_BEGIN[style=flex:0.33] Split
|
|
||||||
[graph]
|
|
||||||
digraph UML_Class_diagram {
|
|
||||||
bgcolor=transparent;
|
|
||||||
graph[fontcolor=darkgray];
|
|
||||||
node[fontcolor=darkgray];
|
|
||||||
edge[fontcolor=darkgray, color=gray90];
|
|
||||||
graph [
|
|
||||||
label="UML Class diagram demo"
|
|
||||||
labelloc="t"
|
|
||||||
fontname="Helvetica,Arial,sans-serif"
|
|
||||||
]
|
|
||||||
node [
|
|
||||||
fontname="Helvetica,Arial,sans-serif"
|
|
||||||
shape=record
|
|
||||||
style=filled
|
|
||||||
fillcolor=gray95
|
|
||||||
]
|
|
||||||
edge [fontname="Helvetica,Arial,sans-serif"]
|
|
||||||
edge [arrowhead=vee style=dashed]
|
|
||||||
Client -> Interface1 [label=dependency]
|
|
||||||
Client -> Interface2
|
|
||||||
|
|
||||||
edge [dir=back arrowtail=empty style=""]
|
|
||||||
Interface1 -> Class1 [xlabel=inheritance]
|
|
||||||
Interface2 -> Class1 [dir=none]
|
|
||||||
Interface2 [label="" xlabel="Simple\ninterface" shape=circle]
|
|
||||||
|
|
||||||
Interface1[label = <{<b>«interface» I/O</b> | + property<br align="left"/>...<br align="left"/>|+ method<br align="left"/>...<br align="left"/>}>]
|
|
||||||
Class1[label = <{<b>I/O class</b> | + property<br align="left"/>...<br align="left"/>|+ method<br align="left"/>...<br align="left"/>}>]
|
|
||||||
edge [dir=back arrowtail=empty style=dashed]
|
|
||||||
Class1 -> System_1 [label=implementation]
|
|
||||||
System_1 [
|
|
||||||
shape=plain
|
|
||||||
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
|
|
||||||
<tr> <td> <b>System</b> </td> </tr>
|
|
||||||
<tr> <td>
|
|
||||||
<table border="0" cellborder="0" cellspacing="0" >
|
|
||||||
<tr> <td align="left" >+ property</td> </tr>
|
|
||||||
<tr> <td port="ss1" align="left" >- Subsystem 1</td> </tr>
|
|
||||||
<tr> <td port="ss2" align="left" >- Subsystem 2</td> </tr>
|
|
||||||
<tr> <td port="ss3" align="left" >- Subsystem 3</td> </tr>
|
|
||||||
<tr> <td align="left">...</td> </tr>
|
|
||||||
</table>
|
|
||||||
</td> </tr>
|
|
||||||
<tr> <td align="left">+ method<br/>...<br align="left"/></td> </tr>
|
|
||||||
</table>>
|
|
||||||
]
|
|
||||||
|
|
||||||
edge [dir=back arrowtail=diamond]
|
|
||||||
System_1:ss1 -> Subsystem_1 [xlabel="composition"]
|
|
||||||
|
|
||||||
Subsystem_1 [
|
|
||||||
shape=plain
|
|
||||||
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
|
|
||||||
<tr> <td> <b>Subsystem 1</b> </td> </tr>
|
|
||||||
<tr> <td>
|
|
||||||
<table border="0" cellborder="0" cellspacing="0" >
|
|
||||||
<tr> <td align="left">+ property</td> </tr>
|
|
||||||
<tr> <td align="left" port="r1">- resource</td> </tr>
|
|
||||||
<tr> <td align="left">...</td> </tr>
|
|
||||||
</table>
|
|
||||||
</td> </tr>
|
|
||||||
<tr> <td align="left">
|
|
||||||
+ method<br/>
|
|
||||||
...<br align="left"/>
|
|
||||||
</td> </tr>
|
|
||||||
</table>>
|
|
||||||
]
|
|
||||||
Subsystem_2 [
|
|
||||||
shape=plain
|
|
||||||
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
|
|
||||||
<tr> <td> <b>Subsystem 2</b> </td> </tr>
|
|
||||||
<tr> <td>
|
|
||||||
<table align="left" border="0" cellborder="0" cellspacing="0" >
|
|
||||||
<tr> <td align="left">+ property</td> </tr>
|
|
||||||
<tr> <td align="left" port="r1">- resource</td> </tr>
|
|
||||||
<tr> <td align="left">...</td> </tr>
|
|
||||||
</table>
|
|
||||||
</td> </tr>
|
|
||||||
<tr> <td align="left">
|
|
||||||
+ method<br/>
|
|
||||||
...<br align="left"/>
|
|
||||||
</td> </tr>
|
|
||||||
</table>>
|
|
||||||
]
|
|
||||||
Subsystem_3 [
|
|
||||||
shape=plain
|
|
||||||
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
|
|
||||||
<tr> <td> <b>Subsystem 3</b> </td> </tr>
|
|
||||||
<tr> <td>
|
|
||||||
<table border="0" cellborder="0" cellspacing="0" >
|
|
||||||
<tr> <td align="left">+ property</td> </tr>
|
|
||||||
<tr> <td align="left" port="r1">- resource</td> </tr>
|
|
||||||
<tr> <td align="left">...</td> </tr>
|
|
||||||
</table>
|
|
||||||
</td> </tr>
|
|
||||||
<tr> <td align="left">
|
|
||||||
+ method<br/>
|
|
||||||
...<br align="left"/>
|
|
||||||
</td> </tr>
|
|
||||||
</table>>
|
|
||||||
]
|
|
||||||
System_1:ss2 -> Subsystem_2;
|
|
||||||
System_1:ss3 -> Subsystem_3;
|
|
||||||
|
|
||||||
edge [xdir=back arrowtail=odiamond]
|
|
||||||
Subsystem_1:r1 -> "Shared resource" [label=aggregation]
|
|
||||||
Subsystem_2:r1 -> "Shared resource"
|
|
||||||
Subsystem_3:r1 -> "Shared resource"
|
|
||||||
"Shared resource" [
|
|
||||||
label = <{
|
|
||||||
<b>Shared resource</b>
|
|
||||||
|
|
|
||||||
+ property<br align="left"/>
|
|
||||||
...<br align="left"/>
|
|
||||||
|
|
|
||||||
+ method<br align="left"/>
|
|
||||||
...<br align="left"/>
|
|
||||||
}>
|
|
||||||
]
|
|
||||||
}
|
|
||||||
[/graph]
|
|
||||||
#+LAYOUT_NEXT[style=flex:0.66]
|
|
||||||
Generated by the following code:
|
|
||||||
``
|
|
||||||
[graph]
|
|
||||||
digraph UML_Class_diagram {
|
|
||||||
bgcolor=transparent;
|
|
||||||
graph[fontcolor=darkgray];
|
|
||||||
node[fontcolor=darkgray];
|
|
||||||
edge[fontcolor=darkgray, color=gray90];
|
|
||||||
graph [
|
|
||||||
label="UML Class diagram demo"
|
|
||||||
labelloc="t"
|
|
||||||
fontname="Helvetica,Arial,sans-serif"
|
|
||||||
]
|
|
||||||
node [
|
|
||||||
fontname="Helvetica,Arial,sans-serif"
|
|
||||||
shape=record
|
|
||||||
style=filled
|
|
||||||
fillcolor=gray95
|
|
||||||
]
|
|
||||||
edge [fontname="Helvetica,Arial,sans-serif"]
|
|
||||||
edge [arrowhead=vee style=dashed]
|
|
||||||
Client -> Interface1 [label=dependency]
|
|
||||||
Client -> Interface2
|
|
||||||
|
|
||||||
edge [dir=back arrowtail=empty style=""]
|
|
||||||
Interface1 -> Class1 [xlabel=inheritance]
|
|
||||||
Interface2 -> Class1 [dir=none]
|
|
||||||
Interface2 [label="" xlabel="Simple\ninterface" shape=circle]
|
|
||||||
|
|
||||||
Interface1[label = <{<b>«interface» I/O</b> | + property<br align="left"/>...<br align="left"/>|+ method<br align="left"/>...<br align="left"/>}>]
|
|
||||||
Class1[label = <{<b>I/O class</b> | + property<br align="left"/>...<br align="left"/>|+ method<br align="left"/>...<br align="left"/>}>]
|
|
||||||
edge [dir=back arrowtail=empty style=dashed]
|
|
||||||
Class1 -> System_1 [label=implementation]
|
|
||||||
System_1 [
|
|
||||||
shape=plain
|
|
||||||
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
|
|
||||||
<tr> <td> <b>System</b> </td> </tr>
|
|
||||||
<tr> <td>
|
|
||||||
<table border="0" cellborder="0" cellspacing="0" >
|
|
||||||
<tr> <td align="left" >+ property</td> </tr>
|
|
||||||
<tr> <td port="ss1" align="left" >- Subsystem 1</td> </tr>
|
|
||||||
<tr> <td port="ss2" align="left" >- Subsystem 2</td> </tr>
|
|
||||||
<tr> <td port="ss3" align="left" >- Subsystem 3</td> </tr>
|
|
||||||
<tr> <td align="left">...</td> </tr>
|
|
||||||
</table>
|
|
||||||
</td> </tr>
|
|
||||||
<tr> <td align="left">+ method<br/>...<br align="left"/></td> </tr>
|
|
||||||
</table>>
|
|
||||||
]
|
|
||||||
|
|
||||||
edge [dir=back arrowtail=diamond]
|
|
||||||
System_1:ss1 -> Subsystem_1 [xlabel="composition"]
|
|
||||||
|
|
||||||
Subsystem_1 [
|
|
||||||
shape=plain
|
|
||||||
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
|
|
||||||
<tr> <td> <b>Subsystem 1</b> </td> </tr>
|
|
||||||
<tr> <td>
|
|
||||||
<table border="0" cellborder="0" cellspacing="0" >
|
|
||||||
<tr> <td align="left">+ property</td> </tr>
|
|
||||||
<tr> <td align="left" port="r1">- resource</td> </tr>
|
|
||||||
<tr> <td align="left">...</td> </tr>
|
|
||||||
</table>
|
|
||||||
</td> </tr>
|
|
||||||
<tr> <td align="left">
|
|
||||||
+ method<br/>
|
|
||||||
...<br align="left"/>
|
|
||||||
</td> </tr>
|
|
||||||
</table>>
|
|
||||||
]
|
|
||||||
Subsystem_2 [
|
|
||||||
shape=plain
|
|
||||||
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
|
|
||||||
<tr> <td> <b>Subsystem 2</b> </td> </tr>
|
|
||||||
<tr> <td>
|
|
||||||
<table align="left" border="0" cellborder="0" cellspacing="0" >
|
|
||||||
<tr> <td align="left">+ property</td> </tr>
|
|
||||||
<tr> <td align="left" port="r1">- resource</td> </tr>
|
|
||||||
<tr> <td align="left">...</td> </tr>
|
|
||||||
</table>
|
|
||||||
</td> </tr>
|
|
||||||
<tr> <td align="left">
|
|
||||||
+ method<br/>
|
|
||||||
...<br align="left"/>
|
|
||||||
</td> </tr>
|
|
||||||
</table>>
|
|
||||||
]
|
|
||||||
Subsystem_3 [
|
|
||||||
shape=plain
|
|
||||||
label=<<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
|
|
||||||
<tr> <td> <b>Subsystem 3</b> </td> </tr>
|
|
||||||
<tr> <td>
|
|
||||||
<table border="0" cellborder="0" cellspacing="0" >
|
|
||||||
<tr> <td align="left">+ property</td> </tr>
|
|
||||||
<tr> <td align="left" port="r1">- resource</td> </tr>
|
|
||||||
<tr> <td align="left">...</td> </tr>
|
|
||||||
</table>
|
|
||||||
</td> </tr>
|
|
||||||
<tr> <td align="left">
|
|
||||||
+ method<br/>
|
|
||||||
...<br align="left"/>
|
|
||||||
</td> </tr>
|
|
||||||
</table>>
|
|
||||||
]
|
|
||||||
System_1:ss2 -> Subsystem_2;
|
|
||||||
System_1:ss3 -> Subsystem_3;
|
|
||||||
|
|
||||||
edge [xdir=back arrowtail=odiamond]
|
|
||||||
Subsystem_1:r1 -> "Shared resource" [label=aggregation]
|
|
||||||
Subsystem_2:r1 -> "Shared resource"
|
|
||||||
Subsystem_3:r1 -> "Shared resource"
|
|
||||||
"Shared resource" [
|
|
||||||
label = <{
|
|
||||||
<b>Shared resource</b>
|
|
||||||
|
|
|
||||||
+ property<br align="left"/>
|
|
||||||
...<br align="left"/>
|
|
||||||
|
|
|
||||||
+ method<br align="left"/>
|
|
||||||
...<br align="left"/>
|
|
||||||
}>
|
|
||||||
]
|
|
||||||
}
|
|
||||||
[/graph]
|
|
||||||
``
|
|
||||||
#+LAYOUT_END
|
|
||||||
|
|
||||||
# Graphiz cache
|
|
||||||
|
|
||||||
Rendered 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.
|
|
||||||
|
|
2
docs/external/latex.nml
vendored
2
docs/external/latex.nml
vendored
|
@ -41,7 +41,6 @@ $|\begin{tikzpicture}
|
||||||
``
|
``
|
||||||
Gives the following:
|
Gives the following:
|
||||||
|
|
||||||
#+LAYOUT_BEGIN Centered
|
|
||||||
$|\begin{tikzpicture}
|
$|\begin{tikzpicture}
|
||||||
\begin{axis}
|
\begin{axis}
|
||||||
\addplot3[patch,patch refines=3,
|
\addplot3[patch,patch refines=3,
|
||||||
|
@ -62,7 +61,6 @@ $|\begin{tikzpicture}
|
||||||
};
|
};
|
||||||
\end{axis}
|
\end{axis}
|
||||||
\end{tikzpicture}|$
|
\end{tikzpicture}|$
|
||||||
#+LAYOUT_END
|
|
||||||
|
|
||||||
# LaTeX environment
|
# LaTeX environment
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
Enclose text between two ``**`` to render it **bold**!
|
Enclose text between two ``**`` to render it **bold**!
|
||||||
* ``**Bold text**`` → **Bold text**
|
* ``**Bold text**`` → **Bold text**
|
||||||
* ``Bold [**link**](#)`` → Bold [**link**](#)
|
* ``**Bold [link](#)**`` → **Bold [link](#)**
|
||||||
|
|
||||||
## Italic
|
## Italic
|
||||||
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
@import ../template.nml
|
|
||||||
%<make_doc({"Styles"}, "Layouts", "Basic Layouts")>%
|
|
||||||
|
|
||||||
# Layouts
|
|
||||||
|
|
||||||
You can create layout blocks by using the following tokens:
|
|
||||||
* ``#+LAYOUT_BEGIN <layout_name>`` Starts layout `<layout_name>`
|
|
||||||
* ``#+LAYOUT_NEXT`` Advances layout to the next block
|
|
||||||
* ``#+LAYOUT_END`` Ends last created layout
|
|
||||||
|
|
||||||
Here's an example of what you can do using layouts (with flashy colors for show):
|
|
||||||
#+LAYOUT_BEGIN[style=background-color:#F00;flex:0.5] Split
|
|
||||||
First
|
|
||||||
#+LAYOUT_BEGIN[style=background-color:#FF0] Centered
|
|
||||||
Second
|
|
||||||
#+LAYOUT_END
|
|
||||||
#+LAYOUT_NEXT[style=background-color:#00F]
|
|
||||||
Third
|
|
||||||
#+LAYOUT_BEGIN[style=background-color:#0FF] Split
|
|
||||||
Fourth
|
|
||||||
#+LAYOUT_NEXT[style=background-color:#0F0]
|
|
||||||
Fifth
|
|
||||||
#+LAYOUT_END
|
|
||||||
#+LAYOUT_END
|
|
||||||
|
|
||||||
Given by the following code:
|
|
||||||
```Plain Text
|
|
||||||
#+LAYOUT_BEGIN[style=background-color:#F00;flex:0.5] Split
|
|
||||||
First
|
|
||||||
#+LAYOUT_BEGIN[style=background-color:#FF0] Centered
|
|
||||||
Second
|
|
||||||
#+LAYOUT_END
|
|
||||||
#+LAYOUT_NEXT[style=background-color:#00F]
|
|
||||||
Third
|
|
||||||
#+LAYOUT_BEGIN[style=background-color:#0FF] Split
|
|
||||||
Fourth
|
|
||||||
#+LAYOUT_NEXT[style=background-color:#0F0]
|
|
||||||
Fifth
|
|
||||||
#+LAYOUT_END
|
|
||||||
#+LAYOUT_END
|
|
||||||
```
|
|
||||||
*(indentation is for readability)*
|
|
||||||
|
|
||||||
# Available layouts
|
|
||||||
## Centered
|
|
||||||
|
|
||||||
Centered layout align text to the center of the current block.
|
|
||||||
|
|
||||||
#### Style
|
|
||||||
The ``Centered`` layout uses the `.centered` css class to center the text.
|
|
||||||
|
|
||||||
#### Properties
|
|
||||||
* ``style`` Added css style to the div (defaults to none)
|
|
||||||
|
|
||||||
## Split
|
|
||||||
|
|
||||||
#### 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
|
|
||||||
* ``style`` Added css style to the div (defaults to none)
|
|
|
@ -231,61 +231,3 @@ impl<'a> DocumentAccessors<'a> for dyn Document<'a> + '_ {
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod tests {
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! validate_document {
|
|
||||||
($container:expr, $idx:expr,) => {};
|
|
||||||
($container:expr, $idx:expr, $t:ty; $($tail:tt)*) => {{
|
|
||||||
let elem = &$container[$idx];
|
|
||||||
assert!(elem.downcast_ref::<$t>().is_some(), "Invalid element at index {}, expected {}, got: {elem:#?}", $idx, stringify!($t));
|
|
||||||
|
|
||||||
validate_document!($container, ($idx+1), $($tail)*);
|
|
||||||
}};
|
|
||||||
($container:expr, $idx:expr, $t:ty { $($field:ident == $value:expr),* }; $($tail:tt)*) => {{
|
|
||||||
let elem = &$container[$idx];
|
|
||||||
assert!(elem.downcast_ref::<$t>().is_some(), "Invalid element at index {}, expected {}, got: {elem:#?}", $idx, stringify!($t));
|
|
||||||
|
|
||||||
$(
|
|
||||||
let val = &elem.downcast_ref::<$t>().unwrap().$field;
|
|
||||||
assert!(*val == $value, "Invalid field {} for {} at index {}, expected {:#?}, found {:#?}",
|
|
||||||
stringify!($field),
|
|
||||||
stringify!($t),
|
|
||||||
$idx,
|
|
||||||
$value,
|
|
||||||
val);
|
|
||||||
)*
|
|
||||||
|
|
||||||
validate_document!($container, ($idx+1), $($tail)*);
|
|
||||||
}};
|
|
||||||
($container:expr, $idx:expr, $t:ty { $($ts:tt)* }; $($tail:tt)*) => {{
|
|
||||||
let elem = &$container[$idx];
|
|
||||||
assert!(elem.downcast_ref::<$t>().is_some(), "Invalid container element at index {}, expected {}", $idx, stringify!($t));
|
|
||||||
|
|
||||||
let contained = elem.as_container().unwrap().contained();
|
|
||||||
validate_document!(contained, 0, $($ts)*);
|
|
||||||
|
|
||||||
validate_document!($container, ($idx+1), $($tail)*);
|
|
||||||
}};
|
|
||||||
($container:expr, $idx:expr, $t:ty { $($field:ident == $value:expr),* } { $($ts:tt)* }; $($tail:tt)*) => {{
|
|
||||||
let elem = &$container[$idx];
|
|
||||||
assert!(elem.downcast_ref::<$t>().is_some(), "Invalid element at index {}, expected {}, got: {elem:#?}", $idx, stringify!($t));
|
|
||||||
|
|
||||||
$(
|
|
||||||
let val = &elem.downcast_ref::<$t>().unwrap().$field;
|
|
||||||
assert!(*val == $value, "Invalid field {} for {} at index {}, expected {:#?}, found {:#?}",
|
|
||||||
stringify!($field),
|
|
||||||
stringify!($t),
|
|
||||||
$idx,
|
|
||||||
$value,
|
|
||||||
val);
|
|
||||||
)*
|
|
||||||
|
|
||||||
let contained = elem.as_container().unwrap().contained();
|
|
||||||
validate_document!(contained, 0, $($ts)*);
|
|
||||||
|
|
||||||
validate_document!($container, ($idx+1), $($tail)*);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -665,7 +665,7 @@ mod tests {
|
||||||
static int INT32_MIN = 0x80000000;
|
static int INT32_MIN = 0x80000000;
|
||||||
```
|
```
|
||||||
%<nml.code.push_block("Lua", "From Lua", "print(\"Hello, World!\")", nil)>%
|
%<nml.code.push_block("Lua", "From Lua", "print(\"Hello, World!\")", nil)>%
|
||||||
``Rust,
|
``Rust
|
||||||
fn fact(n: usize) -> usize
|
fn fact(n: usize) -> usize
|
||||||
{
|
{
|
||||||
match n
|
match n
|
||||||
|
@ -681,6 +681,7 @@ fn fact(n: usize) -> usize
|
||||||
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();
|
||||||
|
|
|
@ -1,20 +1,9 @@
|
||||||
use crate::compiler::compiler::Compiler;
|
use mlua::{Function, Lua};
|
||||||
use crate::document::document::Document;
|
use regex::{Captures, Regex};
|
||||||
use crate::document::element::ElemKind;
|
use crate::{document::document::Document, parser::{parser::Parser, rule::RegexRule, source::{Source, Token}}};
|
||||||
use crate::document::element::Element;
|
use ariadne::{Report, Label, ReportKind};
|
||||||
use crate::parser::parser::Parser;
|
use crate::{compiler::compiler::Compiler, document::element::{ElemKind, Element}};
|
||||||
use crate::parser::rule::RegexRule;
|
use std::{ops::Range, rc::Rc};
|
||||||
use crate::parser::source::Source;
|
|
||||||
use crate::parser::source::Token;
|
|
||||||
use ariadne::Label;
|
|
||||||
use ariadne::Report;
|
|
||||||
use ariadne::ReportKind;
|
|
||||||
use mlua::Function;
|
|
||||||
use mlua::Lua;
|
|
||||||
use regex::Captures;
|
|
||||||
use regex::Regex;
|
|
||||||
use std::ops::Range;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Comment {
|
pub struct Comment {
|
||||||
|
@ -22,21 +11,21 @@ pub struct Comment {
|
||||||
content: String,
|
content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Comment {
|
impl Comment
|
||||||
pub fn new(location: Token, content: String) -> Self {
|
{
|
||||||
Self {
|
pub fn new(location: Token, content: String ) -> Self {
|
||||||
location: location,
|
Self { location: location, content }
|
||||||
content,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Comment {
|
impl Element for Comment
|
||||||
|
{
|
||||||
fn location(&self) -> &Token { &self.location }
|
fn location(&self) -> &Token { &self.location }
|
||||||
fn kind(&self) -> ElemKind { ElemKind::Invisible }
|
fn kind(&self) -> ElemKind { ElemKind::Invisible }
|
||||||
fn element_name(&self) -> &'static str { "Comment" }
|
fn element_name(&self) -> &'static str { "Comment" }
|
||||||
fn to_string(&self) -> String { format!("{self:#?}") }
|
fn to_string(&self) -> String { format!("{self:#?}") }
|
||||||
fn compile(&self, _compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
fn compile(&self, _compiler: &Compiler, _document: &dyn Document)
|
||||||
|
-> Result<String, String> {
|
||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,9 +36,7 @@ pub struct CommentRule {
|
||||||
|
|
||||||
impl CommentRule {
|
impl CommentRule {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self { re: [Regex::new(r"\s*::(.*)").unwrap()] }
|
||||||
re: [Regex::new(r"(?:(?:^|\n)|[^\S\n]+)::(.*)").unwrap()],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,77 +45,40 @@ impl RegexRule for CommentRule {
|
||||||
|
|
||||||
fn regexes(&self) -> &[Regex] { &self.re }
|
fn regexes(&self) -> &[Regex] { &self.re }
|
||||||
|
|
||||||
fn on_regex_match<'a>(
|
fn on_regex_match<'a>(&self, _: usize, parser: &dyn Parser, document: &'a dyn Document, token: Token, matches: Captures)
|
||||||
&self,
|
-> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
||||||
_: usize,
|
|
||||||
parser: &dyn Parser,
|
|
||||||
document: &'a dyn Document,
|
|
||||||
token: Token,
|
|
||||||
matches: Captures,
|
|
||||||
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
|
||||||
let mut reports = vec![];
|
let mut reports = vec![];
|
||||||
|
|
||||||
let content = match matches.get(1) {
|
let content = match matches.get(1)
|
||||||
|
{
|
||||||
None => panic!("Unknown error"),
|
None => panic!("Unknown error"),
|
||||||
Some(comment) => {
|
Some(comment) => {
|
||||||
let trimmed = comment.as_str().trim_start().trim_end().to_string();
|
let trimmed = comment.as_str().trim_start().trim_end().to_string();
|
||||||
if trimmed.is_empty() {
|
if trimmed.is_empty()
|
||||||
|
{
|
||||||
reports.push(
|
reports.push(
|
||||||
Report::build(ReportKind::Warning, token.source(), comment.start())
|
Report::build(ReportKind::Warning, token.source(), comment.start())
|
||||||
.with_message("Empty comment")
|
.with_message("Empty comment")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), comment.range()))
|
Label::new((token.source(), comment.range()))
|
||||||
.with_message("Comment is empty")
|
.with_message("Comment is empty")
|
||||||
.with_color(parser.colors().warning),
|
.with_color(parser.colors().warning))
|
||||||
)
|
.finish());
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trimmed
|
trimmed
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
parser.push(document, Box::new(Comment::new(token.clone(), content)));
|
parser.push(document, Box::new(
|
||||||
|
Comment::new(
|
||||||
|
token.clone(),
|
||||||
|
content
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
return reports;
|
return reports;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::elements::paragraph::Paragraph;
|
|
||||||
use crate::elements::style::Style;
|
|
||||||
use crate::elements::text::Text;
|
|
||||||
use crate::parser::langparser::LangParser;
|
|
||||||
use crate::parser::source::SourceFile;
|
|
||||||
use crate::validate_document;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parser() {
|
|
||||||
let source = Rc::new(SourceFile::with_content(
|
|
||||||
"".to_string(),
|
|
||||||
r#"
|
|
||||||
NOT COMMENT: `std::cmp`
|
|
||||||
:: Commented line
|
|
||||||
COMMENT ::Test
|
|
||||||
"#
|
|
||||||
.to_string(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
let parser = LangParser::default();
|
|
||||||
let doc = parser.parse(source, None);
|
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
|
||||||
Paragraph {
|
|
||||||
Text; Style; Text; Style;
|
|
||||||
Comment { content == "Commented line" };
|
|
||||||
Text; Comment { content == "Test" };
|
|
||||||
};
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,9 +30,9 @@ use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub(crate) enum LayoutToken {
|
pub(crate) enum LayoutToken {
|
||||||
Begin,
|
BEGIN,
|
||||||
Next,
|
NEXT,
|
||||||
End,
|
END,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the type of a layout
|
/// Represents the type of a layout
|
||||||
|
@ -133,9 +133,9 @@ mod default_layouts {
|
||||||
str => format!(r#" style={}"#, Compiler::sanitize(compiler.target(), str)),
|
str => format!(r#" style={}"#, Compiler::sanitize(compiler.target(), str)),
|
||||||
};
|
};
|
||||||
match token {
|
match token {
|
||||||
LayoutToken::Begin => Ok(format!(r#"<div class="centered"{style}>"#)),
|
LayoutToken::BEGIN => Ok(format!(r#"<div class="centered"{style}>"#)),
|
||||||
LayoutToken::Next => panic!(),
|
LayoutToken::NEXT => panic!(),
|
||||||
LayoutToken::End => Ok(r#"</div>"#.to_string()),
|
LayoutToken::END => Ok(r#"</div>"#.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => todo!(""),
|
_ => todo!(""),
|
||||||
|
@ -211,11 +211,11 @@ mod default_layouts {
|
||||||
str => format!(r#" style={}"#, Compiler::sanitize(compiler.target(), str)),
|
str => format!(r#" style={}"#, Compiler::sanitize(compiler.target(), str)),
|
||||||
};
|
};
|
||||||
match token {
|
match token {
|
||||||
LayoutToken::Begin => Ok(format!(
|
LayoutToken::BEGIN => Ok(format!(
|
||||||
r#"<div class="split-container"><div class="split"{style}>"#
|
r#"<div class="split-container"><div class="split"{style}>"#
|
||||||
)),
|
)),
|
||||||
LayoutToken::Next => Ok(format!(r#"</div><div class="split"{style}>"#)),
|
LayoutToken::NEXT => Ok(format!(r#"</div><div class="split"{style}>"#)),
|
||||||
LayoutToken::End => Ok(r#"</div></div>"#.to_string()),
|
LayoutToken::END => Ok(r#"</div></div>"#.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => todo!(""),
|
_ => todo!(""),
|
||||||
|
@ -506,7 +506,7 @@ impl RegexRule for LayoutRule {
|
||||||
location: token.clone(),
|
location: token.clone(),
|
||||||
layout: layout_type.clone(),
|
layout: layout_type.clone(),
|
||||||
id: 0,
|
id: 0,
|
||||||
token: LayoutToken::Begin,
|
token: LayoutToken::BEGIN,
|
||||||
properties,
|
properties,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -583,7 +583,7 @@ impl RegexRule for LayoutRule {
|
||||||
tokens.push(token.clone());
|
tokens.push(token.clone());
|
||||||
(
|
(
|
||||||
tokens.len() - 1,
|
tokens.len() - 1,
|
||||||
LayoutToken::Next,
|
LayoutToken::NEXT,
|
||||||
layout_type.clone(),
|
layout_type.clone(),
|
||||||
properties,
|
properties,
|
||||||
)
|
)
|
||||||
|
@ -646,7 +646,7 @@ impl RegexRule for LayoutRule {
|
||||||
let layout_type = layout_type.clone();
|
let layout_type = layout_type.clone();
|
||||||
let id = tokens.len();
|
let id = tokens.len();
|
||||||
state.stack.pop();
|
state.stack.pop();
|
||||||
(id, LayoutToken::End, layout_type, properties)
|
(id, LayoutToken::END, layout_type, properties)
|
||||||
};
|
};
|
||||||
|
|
||||||
parser.push(
|
parser.push(
|
||||||
|
@ -666,66 +666,3 @@ impl RegexRule for LayoutRule {
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::elements::paragraph::Paragraph;
|
|
||||||
use crate::elements::text::Text;
|
|
||||||
use crate::parser::langparser::LangParser;
|
|
||||||
use crate::parser::source::SourceFile;
|
|
||||||
use crate::validate_document;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parser() {
|
|
||||||
let source = Rc::new(SourceFile::with_content(
|
|
||||||
"".to_string(),
|
|
||||||
r#"
|
|
||||||
#+LAYOUT_BEGIN[style=A] Split
|
|
||||||
A
|
|
||||||
#+LAYOUT_BEGIN[style=B] Centered
|
|
||||||
B
|
|
||||||
#+LAYOUT_END
|
|
||||||
#+LAYOUT_NEXT[style=C]
|
|
||||||
C
|
|
||||||
#+LAYOUT_BEGIN[style=D] Split
|
|
||||||
D
|
|
||||||
#+LAYOUT_NEXT[style=E]
|
|
||||||
E
|
|
||||||
#+LAYOUT_END
|
|
||||||
#+LAYOUT_END
|
|
||||||
"#
|
|
||||||
.to_string(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
let parser = LangParser::default();
|
|
||||||
let doc = parser.parse(source, None);
|
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
|
||||||
Layout { token == LayoutToken::Begin, id == 0 };
|
|
||||||
Paragraph {
|
|
||||||
Text { content == "A" };
|
|
||||||
};
|
|
||||||
Layout { token == LayoutToken::Begin, id == 0 };
|
|
||||||
Paragraph {
|
|
||||||
Text { content == "B" };
|
|
||||||
};
|
|
||||||
Layout { token == LayoutToken::End, id == 1 };
|
|
||||||
Layout { token == LayoutToken::Next, id == 1 };
|
|
||||||
Paragraph {
|
|
||||||
Text { content == "C" };
|
|
||||||
};
|
|
||||||
Layout { token == LayoutToken::Begin, id == 0 };
|
|
||||||
Paragraph {
|
|
||||||
Text { content == "D" };
|
|
||||||
};
|
|
||||||
Layout { token == LayoutToken::Next, id == 1 };
|
|
||||||
Paragraph {
|
|
||||||
Text { content == "E" };
|
|
||||||
};
|
|
||||||
Layout { token == LayoutToken::End, id == 2 };
|
|
||||||
Layout { token == LayoutToken::End, id == 2 };
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use crate::compiler::compiler::Compiler;
|
use crate::compiler::compiler::Compiler;
|
||||||
use crate::compiler::compiler::Target;
|
use crate::compiler::compiler::Target;
|
||||||
use crate::document::document::Document;
|
use crate::document::document::Document;
|
||||||
use crate::document::element::ContainerElement;
|
|
||||||
use crate::document::element::ElemKind;
|
use crate::document::element::ElemKind;
|
||||||
use crate::document::element::Element;
|
use crate::document::element::Element;
|
||||||
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;
|
||||||
use crate::parser::source::Token;
|
use crate::parser::source::Token;
|
||||||
use crate::parser::source::VirtualSource;
|
|
||||||
use crate::parser::util;
|
use crate::parser::util;
|
||||||
use ariadne::Fmt;
|
use ariadne::Fmt;
|
||||||
use ariadne::Label;
|
use ariadne::Label;
|
||||||
|
@ -21,15 +19,21 @@ use regex::Regex;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::paragraph::Paragraph;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Link {
|
pub struct Link {
|
||||||
pub(self) location: Token,
|
location: Token,
|
||||||
/// Display content of link
|
name: String, // Link name
|
||||||
pub(self) display: Paragraph,
|
url: String, // Link url
|
||||||
/// Url of link
|
}
|
||||||
pub(self) url: String,
|
|
||||||
|
impl Link {
|
||||||
|
pub fn new(location: Token, name: String, url: String) -> Self {
|
||||||
|
Self {
|
||||||
|
location: location,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Link {
|
impl Element for Link {
|
||||||
|
@ -37,38 +41,19 @@ impl Element for Link {
|
||||||
fn kind(&self) -> ElemKind { ElemKind::Inline }
|
fn kind(&self) -> ElemKind { ElemKind::Inline }
|
||||||
fn element_name(&self) -> &'static str { "Link" }
|
fn element_name(&self) -> &'static str { "Link" }
|
||||||
fn to_string(&self) -> String { format!("{self:#?}") }
|
fn to_string(&self) -> String { format!("{self:#?}") }
|
||||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||||
match compiler.target() {
|
match compiler.target() {
|
||||||
Target::HTML => {
|
Target::HTML => Ok(format!(
|
||||||
let mut result = format!(
|
"<a href=\"{}\">{}</a>",
|
||||||
"<a href=\"{}\">",
|
Compiler::sanitize(compiler.target(), self.url.as_str()),
|
||||||
Compiler::sanitize(compiler.target(), self.url.as_str())
|
Compiler::sanitize(compiler.target(), self.name.as_str()),
|
||||||
);
|
)),
|
||||||
|
Target::LATEX => Ok(format!(
|
||||||
result += self
|
"\\href{{{}}}{{{}}}",
|
||||||
.display
|
Compiler::sanitize(compiler.target(), self.url.as_str()),
|
||||||
.compile(compiler, document)
|
Compiler::sanitize(compiler.target(), self.name.as_str()),
|
||||||
.as_ref()
|
)),
|
||||||
.map(|r| r.as_str())?;
|
|
||||||
|
|
||||||
result += "</a>";
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
_ => todo!(""),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_container(&self) -> Option<&dyn ContainerElement> { Some(self) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContainerElement for Link {
|
|
||||||
fn contained(&self) -> &Vec<Box<dyn Element>> { &self.display.content }
|
|
||||||
|
|
||||||
fn push(&mut self, elem: Box<dyn Element>) -> Result<(), String> {
|
|
||||||
if elem.downcast_ref::<Link>().is_some() {
|
|
||||||
return Err("Tried to push a link inside of a link".to_string());
|
|
||||||
}
|
|
||||||
self.display.push(elem)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,67 +78,47 @@ impl RegexRule for LinkRule {
|
||||||
&self,
|
&self,
|
||||||
_: usize,
|
_: usize,
|
||||||
parser: &dyn Parser,
|
parser: &dyn Parser,
|
||||||
document: &'a (dyn Document<'a> + 'a),
|
document: &'a dyn Document,
|
||||||
token: Token,
|
token: Token,
|
||||||
matches: Captures,
|
matches: Captures,
|
||||||
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
||||||
let mut reports = vec![];
|
let mut result = vec![];
|
||||||
|
let link_name = match matches.get(1) {
|
||||||
let link_display = match matches.get(1) {
|
Some(name) => {
|
||||||
Some(display) => {
|
if name.as_str().is_empty() {
|
||||||
if display.as_str().is_empty() {
|
result.push(
|
||||||
reports.push(
|
Report::build(ReportKind::Error, token.source(), name.start())
|
||||||
Report::build(ReportKind::Error, token.source(), display.start())
|
|
||||||
.with_message("Empty link name")
|
.with_message("Empty link name")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source().clone(), display.range()))
|
Label::new((token.source().clone(), name.range()))
|
||||||
.with_message("Link name is empty")
|
.with_message("Link name is empty")
|
||||||
.with_color(parser.colors().error),
|
.with_color(parser.colors().error),
|
||||||
)
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
);
|
);
|
||||||
return reports;
|
return result;
|
||||||
}
|
}
|
||||||
let processed = util::process_escaped('\\', "]", display.as_str());
|
// TODO: process into separate document...
|
||||||
if processed.is_empty() {
|
let text_content = util::process_text(document, name.as_str());
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), display.start())
|
if text_content.as_str().is_empty() {
|
||||||
|
result.push(
|
||||||
|
Report::build(ReportKind::Error, token.source(), name.start())
|
||||||
.with_message("Empty link name")
|
.with_message("Empty link name")
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new((token.source(), display.range()))
|
Label::new((token.source(), name.range()))
|
||||||
.with_message(format!(
|
.with_message(format!(
|
||||||
"Link name is empty. Once processed, `{}` yields `{}`",
|
"Link name is empty. Once processed, `{}` yields `{}`",
|
||||||
display.as_str().fg(parser.colors().highlight),
|
name.as_str().fg(parser.colors().highlight),
|
||||||
processed.fg(parser.colors().highlight),
|
text_content.as_str().fg(parser.colors().highlight),
|
||||||
))
|
))
|
||||||
.with_color(parser.colors().error),
|
.with_color(parser.colors().error),
|
||||||
)
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
);
|
);
|
||||||
return reports;
|
return result;
|
||||||
}
|
|
||||||
|
|
||||||
let source = Rc::new(VirtualSource::new(
|
|
||||||
Token::new(display.range(), token.source()),
|
|
||||||
"Link Display".to_string(),
|
|
||||||
processed,
|
|
||||||
));
|
|
||||||
match util::parse_paragraph(parser, source, document) {
|
|
||||||
Err(err) => {
|
|
||||||
reports.push(
|
|
||||||
Report::build(ReportKind::Error, token.source(), display.start())
|
|
||||||
.with_message("Failed to parse link display")
|
|
||||||
.with_label(
|
|
||||||
Label::new((token.source(), display.range()))
|
|
||||||
.with_message(err.to_string())
|
|
||||||
.with_color(parser.colors().error),
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
Ok(paragraph) => *paragraph,
|
|
||||||
}
|
}
|
||||||
|
text_content
|
||||||
}
|
}
|
||||||
_ => panic!("Empty link name"),
|
_ => panic!("Empty link name"),
|
||||||
};
|
};
|
||||||
|
@ -161,7 +126,7 @@ impl RegexRule for LinkRule {
|
||||||
let link_url = match matches.get(2) {
|
let link_url = match matches.get(2) {
|
||||||
Some(url) => {
|
Some(url) => {
|
||||||
if url.as_str().is_empty() {
|
if url.as_str().is_empty() {
|
||||||
reports.push(
|
result.push(
|
||||||
Report::build(ReportKind::Error, token.source(), url.start())
|
Report::build(ReportKind::Error, token.source(), url.start())
|
||||||
.with_message("Empty link url")
|
.with_message("Empty link url")
|
||||||
.with_label(
|
.with_label(
|
||||||
|
@ -171,12 +136,12 @@ impl RegexRule for LinkRule {
|
||||||
)
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
);
|
);
|
||||||
return reports;
|
return result;
|
||||||
}
|
}
|
||||||
let text_content = util::process_text(document, url.as_str());
|
let text_content = util::process_text(document, url.as_str());
|
||||||
|
|
||||||
if text_content.as_str().is_empty() {
|
if text_content.as_str().is_empty() {
|
||||||
reports.push(
|
result.push(
|
||||||
Report::build(ReportKind::Error, token.source(), url.start())
|
Report::build(ReportKind::Error, token.source(), url.start())
|
||||||
.with_message("Empty link url")
|
.with_message("Empty link url")
|
||||||
.with_label(
|
.with_label(
|
||||||
|
@ -190,7 +155,7 @@ impl RegexRule for LinkRule {
|
||||||
)
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
);
|
);
|
||||||
return reports;
|
return result;
|
||||||
}
|
}
|
||||||
text_content
|
text_content
|
||||||
}
|
}
|
||||||
|
@ -199,55 +164,12 @@ impl RegexRule for LinkRule {
|
||||||
|
|
||||||
parser.push(
|
parser.push(
|
||||||
document,
|
document,
|
||||||
Box::new(Link {
|
Box::new(Link::new(token.clone(), link_name, link_url)),
|
||||||
location: token,
|
|
||||||
display: link_display,
|
|
||||||
url: link_url,
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return reports;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::elements::style::Style;
|
|
||||||
use crate::elements::text::Text;
|
|
||||||
use crate::parser::langparser::LangParser;
|
|
||||||
use crate::parser::source::SourceFile;
|
|
||||||
use crate::validate_document;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parser() {
|
|
||||||
let source = Rc::new(SourceFile::with_content(
|
|
||||||
"".to_string(),
|
|
||||||
r#"
|
|
||||||
Some [link](url).
|
|
||||||
[**BOLD link**](another url)
|
|
||||||
"#
|
|
||||||
.to_string(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
let parser = LangParser::default();
|
|
||||||
let doc = parser.parse(source, None);
|
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
|
||||||
Paragraph {
|
|
||||||
Text { content == "Some " };
|
|
||||||
Link { url == "url" } { Text { content == "link" }; };
|
|
||||||
Text { content == "." };
|
|
||||||
Link { url == "another url" } {
|
|
||||||
Style;
|
|
||||||
Text { content == "BOLD link" };
|
|
||||||
Style;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -100,9 +100,6 @@ impl ContainerElement for Paragraph {
|
||||||
if elem.location().source() == self.location().source() {
|
if elem.location().source() == self.location().source() {
|
||||||
self.location.range = self.location.start()..elem.location().end();
|
self.location.range = self.location.start()..elem.location().end();
|
||||||
}
|
}
|
||||||
if elem.kind() == ElemKind::Block {
|
|
||||||
return Err("Attempted to push block element inside a paragraph".to_string());
|
|
||||||
}
|
|
||||||
self.content.push(elem);
|
self.content.push(elem);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -155,47 +152,3 @@ impl Rule for ParagraphRule {
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::elements::paragraph::Paragraph;
|
|
||||||
use crate::elements::text::Text;
|
|
||||||
use crate::parser::langparser::LangParser;
|
|
||||||
use crate::parser::source::SourceFile;
|
|
||||||
use crate::validate_document;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse() {
|
|
||||||
let source = Rc::new(SourceFile::with_content(
|
|
||||||
"".to_string(),
|
|
||||||
r#"
|
|
||||||
First paragraph
|
|
||||||
Second line
|
|
||||||
|
|
||||||
Second paragraph\
|
|
||||||
<- literal \\n
|
|
||||||
|
|
||||||
|
|
||||||
Last paragraph
|
|
||||||
"#
|
|
||||||
.to_string(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
let parser = LangParser::default();
|
|
||||||
let doc = parser.parse(source, None);
|
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
|
||||||
Paragraph {
|
|
||||||
Text { content == "First paragraph Second line" };
|
|
||||||
};
|
|
||||||
Paragraph {
|
|
||||||
Text { content == "Second paragraph\n<- literal \\n" };
|
|
||||||
};
|
|
||||||
Paragraph {
|
|
||||||
Text { content == "Last paragraph " };
|
|
||||||
};
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -222,54 +222,3 @@ impl RegexRule for StyleRule {
|
||||||
// TODO
|
// TODO
|
||||||
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
fn lua_bindings<'lua>(&self, _lua: &'lua Lua) -> Option<Vec<(String, Function<'lua>)>> { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::elements::text::Text;
|
|
||||||
use crate::parser::langparser::LangParser;
|
|
||||||
use crate::parser::source::SourceFile;
|
|
||||||
use crate::validate_document;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parser() {
|
|
||||||
let source = Rc::new(SourceFile::with_content(
|
|
||||||
"".to_string(),
|
|
||||||
r#"
|
|
||||||
Some *style
|
|
||||||
terminated here*
|
|
||||||
|
|
||||||
**BOLD + *italic***
|
|
||||||
__`UNDERLINE+EM`__
|
|
||||||
"#
|
|
||||||
.to_string(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
let parser = LangParser::default();
|
|
||||||
let doc = parser.parse(source, None);
|
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
|
||||||
Paragraph {
|
|
||||||
Text;
|
|
||||||
Style { kind == 1, close == false };
|
|
||||||
Text;
|
|
||||||
Style { kind == 1, close == true };
|
|
||||||
};
|
|
||||||
Paragraph {
|
|
||||||
Style { kind == 0, close == false }; // **
|
|
||||||
Text;
|
|
||||||
Style { kind == 1, close == false }; // *
|
|
||||||
Text;
|
|
||||||
Style { kind == 0, close == true }; // **
|
|
||||||
Style { kind == 1, close == true }; // *
|
|
||||||
|
|
||||||
Style { kind == 2, close == false }; // __
|
|
||||||
Style { kind == 3, close == false }; // `
|
|
||||||
Text;
|
|
||||||
Style { kind == 3, close == true }; // `
|
|
||||||
Style { kind == 2, close == true }; // __
|
|
||||||
};
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -253,7 +253,7 @@ impl TexRule {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Regex::new(r"\$(?:\[((?:\\.|[^\\\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\$)?").unwrap(),
|
Regex::new(r"\$(?:\[((?:\\.|[^\\\\])*?)\])?(?:((?:\\.|[^\\\\])*?)\$)?").unwrap(),
|
||||||
],
|
],
|
||||||
properties: PropertyParser { properties: props },
|
properties: PropertyParser{ properties: props },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,10 +435,8 @@ impl RegexRule for TexRule {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::elements::paragraph::Paragraph;
|
|
||||||
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 super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -448,7 +446,7 @@ mod tests {
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
r#"
|
r#"
|
||||||
$[kind=block, caption=Some\, text\\] 1+1=2 $
|
$[kind=block, caption=Some\, text\\] 1+1=2 $
|
||||||
$|[env=another] Non Math \LaTeX |$
|
$|[env=another] Non Math \LaTeX|$
|
||||||
$[kind=block,env=another] e^{i\pi}=-1$
|
$[kind=block,env=another] e^{i\pi}=-1$
|
||||||
"#
|
"#
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
@ -457,11 +455,19 @@ $[kind=block,env=another] e^{i\pi}=-1$
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(source, None);
|
let doc = parser.parse(source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
let borrow = doc.content().borrow();
|
||||||
Tex { mathmode == true, tex == "1+1=2", env == "main", caption == Some("Some, text\\".to_string()) };
|
let found = borrow
|
||||||
Tex { mathmode == false, tex == "Non Math \\LaTeX", env == "another" };
|
.iter()
|
||||||
Tex { mathmode == true, tex == "e^{i\\pi}=-1", env == "another" };
|
.filter_map(|e| e.downcast_ref::<Tex>())
|
||||||
);
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(found[0].tex, "1+1=2");
|
||||||
|
assert_eq!(found[0].env, "main");
|
||||||
|
assert_eq!(found[0].caption, Some("Some, text\\".to_string()));
|
||||||
|
assert_eq!(found[1].tex, "Non Math \\LaTeX");
|
||||||
|
assert_eq!(found[1].env, "another");
|
||||||
|
assert_eq!(found[2].tex, "e^{i\\pi}=-1");
|
||||||
|
assert_eq!(found[2].env, "another");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -479,12 +485,24 @@ $[env=another] e^{i\pi}=-1$
|
||||||
let parser = LangParser::default();
|
let parser = LangParser::default();
|
||||||
let doc = parser.parse(source, None);
|
let doc = parser.parse(source, None);
|
||||||
|
|
||||||
validate_document!(doc.content().borrow(), 0,
|
let borrow = doc.content().borrow();
|
||||||
Paragraph {
|
let found = borrow
|
||||||
Tex { mathmode == true, tex == "1+1=2", env == "main", caption == Some("Some, text\\".to_string()) };
|
.first()
|
||||||
Tex { mathmode == false, tex == "Non Math \\LaTeX", env == "another" };
|
.unwrap()
|
||||||
Tex { mathmode == true, tex == "e^{i\\pi}=-1", env == "another" };
|
.as_container()
|
||||||
};
|
.unwrap()
|
||||||
);
|
.contained()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|e| e.downcast_ref::<Tex>())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(found[0].tex, "1+1=2");
|
||||||
|
assert_eq!(found[0].env, "main");
|
||||||
|
assert_eq!(found[0].caption, Some("Some, text\\".to_string()));
|
||||||
|
assert_eq!(found[1].tex, "Non Math \\LaTeX");
|
||||||
|
assert_eq!(found[1].env, "another");
|
||||||
|
assert_eq!(found[1].caption, Some("Enclosed ].".to_string()));
|
||||||
|
assert_eq!(found[2].tex, "e^{i\\pi}=-1");
|
||||||
|
assert_eq!(found[2].env, "another");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ use crate::parser::source::Token;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
pub location: Token,
|
pub(self) location: Token,
|
||||||
pub content: String,
|
pub(self) content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Text {
|
impl Text {
|
||||||
|
|
|
@ -147,8 +147,6 @@ pub fn parse_paragraph<'a>(
|
||||||
return Err("Parsed document is empty");
|
return Err("Parsed document is empty");
|
||||||
} else if parsed.last_element::<Paragraph>().is_none() {
|
} else if parsed.last_element::<Paragraph>().is_none() {
|
||||||
return Err("Parsed element is not a paragraph");
|
return Err("Parsed element is not a paragraph");
|
||||||
} else if parser.has_error() {
|
|
||||||
return Err("Parser error");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let paragraph = parsed.content().borrow_mut().pop().unwrap();
|
let paragraph = parsed.content().borrow_mut().pop().unwrap();
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""latex2svg
|
"""latex2svg
|
||||||
-- This version of latex2svg comes with NML and has been modified to work with it only --
|
|
||||||
-- The original version can be found here : https://github.com/Moonbase59/latex2svg --
|
|
||||||
|
|
||||||
Read LaTeX code from stdin and render a SVG using LaTeX, dvisvgm and svgo.
|
Read LaTeX code from stdin and render a SVG using LaTeX, dvisvgm and svgo.
|
||||||
|
|
||||||
|
@ -25,6 +23,38 @@ import re
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from ctypes.util import find_library
|
from ctypes.util import find_library
|
||||||
|
|
||||||
|
default_template = r"""
|
||||||
|
\documentclass[{{ fontsize }}pt,preview]{standalone}
|
||||||
|
{{ preamble }}
|
||||||
|
\begin{document}
|
||||||
|
\begin{preview}
|
||||||
|
{{ code }}
|
||||||
|
\end{preview}
|
||||||
|
\end{document}
|
||||||
|
"""
|
||||||
|
|
||||||
|
default_preamble = r"""
|
||||||
|
\usepackage[utf8x]{inputenc}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage{amsfonts}
|
||||||
|
\usepackage{amssymb}
|
||||||
|
\usepackage{amstext}
|
||||||
|
\usepackage{newtxtext}
|
||||||
|
\usepackage[libertine]{newtxmath}
|
||||||
|
% prevent errors from old font commands
|
||||||
|
\DeclareOldFontCommand{\rm}{\normalfont\rmfamily}{\mathrm}
|
||||||
|
\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf}
|
||||||
|
\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt}
|
||||||
|
\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf}
|
||||||
|
\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit}
|
||||||
|
\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl}
|
||||||
|
\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc}
|
||||||
|
% prevent errors from undefined shortcuts
|
||||||
|
\newcommand{\N}{\mathbb{N}}
|
||||||
|
\newcommand{\R}{\mathbb{R}}
|
||||||
|
\newcommand{\Z}{\mathbb{Z}}
|
||||||
|
"""
|
||||||
|
|
||||||
default_svgo_config = r"""
|
default_svgo_config = r"""
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -54,6 +84,8 @@ svgo_cmd = 'svgo'
|
||||||
|
|
||||||
default_params = {
|
default_params = {
|
||||||
'fontsize': 12, # TeX pt
|
'fontsize': 12, # TeX pt
|
||||||
|
'template': default_template,
|
||||||
|
'preamble': default_preamble,
|
||||||
'latex_cmd': latex_cmd,
|
'latex_cmd': latex_cmd,
|
||||||
'dvisvgm_cmd': dvisvgm_cmd,
|
'dvisvgm_cmd': dvisvgm_cmd,
|
||||||
'svgo_cmd': svgo_cmd,
|
'svgo_cmd': svgo_cmd,
|
||||||
|
@ -205,15 +237,22 @@ def main():
|
||||||
""")
|
""")
|
||||||
parser.add_argument('--version', action='version',
|
parser.add_argument('--version', action='version',
|
||||||
version='%(prog)s {version}'.format(version=__version__))
|
version='%(prog)s {version}'.format(version=__version__))
|
||||||
|
parser.add_argument('--preamble',
|
||||||
|
help="LaTeX preamble code to read from file")
|
||||||
parser.add_argument('--fontsize',
|
parser.add_argument('--fontsize',
|
||||||
help="LaTeX fontsize in pt")
|
help="LaTeX fontsize in pt")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
preamble = default_preamble
|
||||||
|
if args.preamble is not None:
|
||||||
|
with open(args.preamble) as f:
|
||||||
|
preamble = f.read()
|
||||||
fontsize = 12
|
fontsize = 12
|
||||||
if args.fontsize is not None:
|
if args.fontsize is not None:
|
||||||
fontsize = int(args.fontsize)
|
fontsize = int(args.fontsize)
|
||||||
latex = sys.stdin.read()
|
latex = sys.stdin.read()
|
||||||
try:
|
try:
|
||||||
params = default_params.copy()
|
params = default_params.copy()
|
||||||
|
params['preamble'] = preamble
|
||||||
params['fontsize'] = fontsize
|
params['fontsize'] = fontsize
|
||||||
out = latex2svg(latex, params)
|
out = latex2svg(latex, params)
|
||||||
sys.stdout.write(out['svg'])
|
sys.stdout.write(out['svg'])
|
||||||
|
|
Loading…
Reference in a new issue