Compare commits

...

24 Commits

Author SHA1 Message Date
Trivernis 0f2bd16d28
Merge pull request #23 from Trivernis/develop
Develop
3 years ago
trivernis 3eb11e03e8
Add chromium requirement to README
closes #21

Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
trivernis 659ca55e2c
Merge branch 'main' into develop 3 years ago
trivernis 7da9f5b338
Bump version
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
Trivernis 83734aa530
Merge pull request #22 from silentbat/main
add style.css to init
3 years ago
trivernis c7694d39e0
Change path of temporary file to be absolute
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
trivernis 0495855d91
Fix problems with creating temp file for pdf rendering
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
trivernis cdbfe8c195
Update dependencies
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
trivernis 0e608e255a
Add fetch feature to headless_chromium dep
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
silentbat 995a3b0582
add style.css to init
when calling `snekdown init`, you should get a default (empty) style.css file now
3 years ago
trivernis 8181119f4a
Update README badge links
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
Trivernis 0fda78bb96
Merge pull request #20 from Trivernis/develop
Reference anchor and improved README
3 years ago
Trivernis 3c9f41ce8c Update issue templates 3 years ago
trivernis 6ebc7cc766
Update Copyright in Source files
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
trivernis 1e46274003
Add reference anchors and update README
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
trivernis e1e63cc35a
Update README
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
Trivernis fd23a49e49
Merge pull request #19 from Trivernis/develop
Fix windows lineending
3 years ago
trivernis f709465eba
Fix windows lineending
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
Trivernis 31c4711881
Merge pull request #18 from Trivernis/develop
Fix mathjax include
3 years ago
trivernis 4c0d4c560c
Fix mathjax include
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
Trivernis ccad1547ae
Merge pull request #17 from Trivernis/develop
Changes and fixes to whitespace behaviour
3 years ago
trivernis e50d73a880
Fix quote text alignment
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
trivernis d5df0b6f05
Change whitespace behaviour
A single linebreak will be ignored in plain text while a double line
break will be converted to a single linebreak. All following
linebreaks are taken as-is and rendered as normal linebreaks.
(Fixes #13)

Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago
trivernis 661a6e5a85
Add flag to print to stdout
Signed-off-by: trivernis <trivernis@protonmail.com>
3 years ago

@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Write...
2. Render...
3. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Arch Linux]
- Architecture: [e.g. x86_64, ARM]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEATURE]"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

@ -30,4 +30,13 @@ jobs:
run: cargo build --verbose --all-features
- name: Run tests
run: cargo test --verbose --all-features
run: cargo test --verbose --all-features
- name: Test init
run: cargo run -- init
- name: Test HTML
run: cargo run -- render README.md README.html --format html
- name: Test PDF
run: cargo run --all-features -- render README.md README.pdf --format pdf

750
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -1,9 +1,9 @@
[package]
name = "snekdown"
version = "0.32.2"
version = "0.33.4"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
license-file = "LICENSE"
license = "GPL-3.0"
readme = "README.md"
description = "A parser for the custom snekdown markdown syntax"
repository = "https://github.com/Trivernis/snekdown"
@ -21,7 +21,7 @@ path = "src/main.rs"
pdf = ["headless_chrome", "failure"]
[dependencies]
charred = "0.3.5"
charred = "0.3.6"
asciimath-rs = "0.5.7"
bibliographix = "0.6.0"
crossbeam-utils = "0.7.2"
@ -53,5 +53,5 @@ sha2 = "0.9.2"
config = "0.10.1"
rsass = "0.16.0"
headless_chrome = { version = "0.9.0", optional = true }
headless_chrome = { version = "0.9.0", optional = true, features = ["fetch"] }
failure = { version = "0.1.8", optional = true }

@ -1,465 +1,101 @@
# ![](https://i.imgur.com/FpdXqiT.png) Snekdown - More than just Markdown ![](https://img.shields.io/discord/729250668162056313)
<p align="center">
<img src="https://i.imgur.com/FpdXqiT.png">
</p>
<h1 align="center">Snekdown</h1>
<p align="center">
<i>More than just Markdown</i>
</p>
<p align="center">
<a href="https://github.com/Trivernis/snekdown/actions">
<img src="https://img.shields.io/github/workflow/status/trivernis/snekdown/Build%20and%20Test/main?style=for-the-badge">
</a>
<a href="https://crates.io/crates/snekdown">
<img src="https://img.shields.io/crates/v/snekdown?style=for-the-badge">
</a>
<a href="https://aur.archlinux.org/packages/snekdown">
<img src="https://img.shields.io/aur/version/snekdown?style=for-the-badge">
</a>
<a href="https://discord.gg/vGAXW9nxUv">
<img src="https://img.shields.io/discord/729250668162056313?style=for-the-badge">
</a>
<br/>
<br/>
<a href="https://trivernis.net/snekdown/">Documentation</a> |
<a href="https://github.com/Trivernis/snekdown/releases">Releases</a>
</p>
- - -
## Description
This projects goal is to implement a fast markdown parser with an extended syntax fitted
for my needs.
## Installation
You need a working rust installation, for example by using [rustup](http://rustup.rs).
```sh
cargo install snekdown
```
With pdf rendering
```sh
cargo install snekdown --features pdf
```
## Usage
```
snekdown 0.30.5
USAGE:
snekdown <SUBCOMMAND>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
SUBCOMMANDS:
clear-cache Clears the cache directory
help Prints this message or the help of the given subcommand(s)
render Parse and render the document
watch Watch the document and its imports and render on change
```
### Rendering
```
Parse and render the document
USAGE:
snekdown render [OPTIONS] <input> <output>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-f, --format <format> the output format [default: html]
ARGS:
<input> Path to the input file
<output> Path for the output file
```
### Watching
```
Watch the document and its imports and render on change
USAGE:
snekdown watch [OPTIONS] <input> <output>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
--debounce <debounce> The amount of time in milliseconds to wait after changes before rendering [default:
500]
-f, --format <format> the output format [default: html]
ARGS:
<input> Path to the input file
<output> Path for the output file
```
## Syntax
### Images
```md
Simple Syntax
!(url)
Extended syntax with a description
![description](url)
Extended syntax with metadata to specify the size
![description](url)[metadata]
Extended syntax with metadata and no description
!(url)[metadata]
```
When generating the html file the images are base64 embedded. To turn off this behaviour
set the config parameter `embed-external` to `false`.
### Quotes
```md
Simple (default) Syntax
> This is a quote
Multiline
> This is a
> Multiline Quote
Quote with metadata (e.g. Author)
[author=Trivernis year=2020 display='{{author}} - {{year}}']> This is a quote with metadata
```
### Imports
Imports can be used to import a different document to be attached to the main document.
Imports are parsed via multithreading.
```md
<[path]
<[document.md]
<[style.css][type=stylesheet]
```
The parser differentiates four different types of imported files.
- `document` - The default import which is just another snekdown document
- `stylesheet` - CSS Stylesheets that are inclued when rendering
- `bibliography` - A file including bibliography
- `config`/`manifest` - A config file that contains metadata
If no type is provided the parser guesses the type of file from the extension.
### Tables
Tables MUST start with a pipe character `|`
```md
Standalone header:
| header | header | header
Header with rows
| header | header | header
|--------|--------|-------
| row | row | row
```
### Placeholders
Placeholders can be used to insert special elements in a specific place.
Placeholders are always case insensitive.
```md
Insert the table of contents
[[TOC]]
Insert the bibliography list
[[BIB]]
Insert the glossary
[[GLS]]
Insert the current date
[[date]]
Insert the current time
[[time]]
```
### Metadata
Additional metadata can be provided for some elements.
```md
String value
[key = value]
String value
[key = "String value"]
## Core Features
Integer value
[key = 123]
- Imports
- Bibliography & Glossary
- AsciiMath
- Placeholders
- Advanced Images
Float value
[key = 1.23]
## Prerequisites
Boolean
[key]
- Google Chrome/Chromium (for PDF rendering)
Boolean
[key = false]
Placeholder
[key = [[placeholder]]]
```
Metadata can also be defined in a separate toml file with simple key-value pairs.
The file `Manifest.toml` will always be included by default.
Example:
```toml
# document metadata
[metadata]
# language setting of the document
language = 'en'
# author of the document
author = 'author'
# Title of the document
title = 'title'
# A short description for the document preview
description = '''
Description
'''
# Keywords to find the document
keywords = ['HTML', 'Snekdown']
# features used in the document
[features]
# if external sources (images, stylesheets, MathJax)
# should be embedded into the document (default: true)
embed_external = true
# If SmartArrows should be used (default: true)
smart_arrows = true
include_mathjax = true
[imports]
# those files won't get imported
ignored_imports = []
# stylesheets that should be included
included_stylesheets = ['style.css']
# bibliography that should be included
included_bibliography = ['Bibliography.toml']
# glossary that sould be included
included_glossaries = ['Glossary.toml']
# settings related to pdf rendering
[pdf]
# If the header and footer of the pdf should be displayed (default: true)
display_header_footer = true
# PDF header template of each page (default: '<div></div>')
header_template = '<div></div>'
# PDF footer template of each page (default: see chromium_pdf assets)
footer_template = '''
<div style="font-size: 10px; text-align: center; width: 100%;">
<span class="pageNumber"></span>/<span class="totalPages"></span>
</div>'''
# The scale at which the website is rendered into pdf.
page_scale = 1.0
# margin of the pdf document
[pdf.margin]
# Top margin of the pdf. Should be between 0 and 1. (default: 0.5)
top = 0.5
# Bottom margin of the pdf. Should be between 0 and 1. (default: 0.5)
bottom = 0.5
# Left margin of the pdf. Should be between 0 and 1.
left = 0
# Right margin of the pdf. Should be between 0 and 1.
right = 0
# image settings
[images]
# Force convert images to the specified format.
# Supported formats are png, jpeg, gif, bmp, (ico needs size <= 256), avif, pnm
# (default: keep original)
format = "png"
# the max width for the images.
# if an image is larger than that it get's resized.
# (default: none)
max_width = 700
# the max width for the images.
# if an image is larger than that it get's resized.
# (default: none)
max_height = 500
# Visual adjustments
[style]
# how bibliography references should be displayed
bib_ref_display = '{{number}}'
# the chosen theme for the document
# one of: GithubLight, SolarizedLight, OceanLight, SolarizedDark, OceanDark, MagicDark
theme = 'GithubLight'
# custom metadata
# String -> String Mappings
[custom_attributes]
custom_key1 = "Custom Value"
```
The `[Section]` keys are not relevant as the structure gets flattened before the values are read.
#### Usage
```
Hide a section (including subsections) in the TOC
#[toc-hidden] Section
Set the size of an image
!(url)[width = 42%, height=auto, brightness=10, contrast=1.2, huerotate=180, invert, grayscale]
Set the source of a quote
[author=Me date=[[date]] display="{{author}} - {{date}}"]> It's me
Set options for placeholders
[[toc]][ordered]
```
### Centered Text
```
|| These two lines
|| are centered
```
## Installation
### Inline
```md
*Italic*
**Bold**
~~Striked~~
_Underlined_
^Superscript^
`Monospace`
:Emoji:
§[#0C0]Colored text§[] §[red] red §[]
```
### Binaries
## Bibliography
You can download prebuilt binaries on the [Releases](https://github.com/Trivernis/snekdown/releases) Page.
Bibliography entries can be defined and referenced anywhere in the document.
Definition:
```md
[SD_BOOK]:[type=book, author=Snek, title = "Snekdown Book" date="20.08.2020", publisher=Snek]
[SD_GITHUB]: https://github.com/trivernis/snekdown
```
### Arch Linux
Usage:
```
There is a book about snekdown[^book] and a github repo[^github].
```
Snekdown is available in [the AUR](https://aur.archlinux.org/packages/snekdown).
Entries can also be defined in a separate toml file with the following data layout:
```toml
# snekdown.toml
[BIB_KEY]
key = "value"
### Cargo
[SD_BOOK]
type = "book"
author = "Snek"
title = "Snekdown Book"
date = "20.08.2020"
publisher = "Snek"
You need a working rust installation, for example by using [rustup](http://rustup.rs).
[SD_GITHUB]
type = "website"
url = "https://github.com/trivernis/snekdown"
```sh
cargo install snekdown
```
The valid types for entries and required fields can be found on in the [bibliographix README](https://github.com/Trivernis/bibliographix#bibliography-types-and-fields).
Bibliography entries are not rendered. To render a list of used bibliography insert the
`bib` placeholder at the place you want it to be rendered.
## Glossary
Glossary entries are to be defined in a `glossary.toml` file or any other toml file
that is imported as type `glossary`.
The definition of glossary entries has to follow the following structure
```toml
[SHORT]
long = "Long Form"
description = "The description of the entry"
With pdf rendering
# Example
[HTML]
long = "Hypertext Markup Language"
description = "The markup language of the web"
```sh
cargo install snekdown --features pdf
```
Those glossary entries can be referenced in the snekdown file as follows:
```md
~HTML is widely used for websites.
The format ~HTML is not considered a programming language by some definitions.
~~HTML
```
## Usage
The first occurence of the glossary entry (`~HTML`) always uses the long form.
The second will always be the short form. The long form can be enforced by using two
(`~~HTML`) tildes.
The glossary list can be inserted with the `[[GLS]]` placeholder.
Use `snekdown help` and `snekdown <subcommand> --help` for more information.
## Math
### Rendering
Snekdown allows the embedding of [AsciiMath](http://asciimath.org/):
The AsciiMath parser is provided in the [asciimath-rs](https://github.com/Trivernis/asciimath-rs) crate
`snekdown render <input> <output>`
```
inline math $$ a^2 + b^2 = c^2 $$
### Watching
Block Math
$$$
A = [[1, 2],[3,4]]
$$$
```
`snekdown watch <input> <output>`
The expression get's converted into MathML which is then converted by MathJax when loaded in
the browser.
## Smart Arrows
## Editors
Snekdown automatically renders the sequences `-->`, `==>`, `<--`, `<==`, `<-->`, `<==>` as
their respective unicode arrows (similar to [markdown-it-smartarrows](https://github.com/adam-p/markdown-it-smartarrows)).
This behavior can be turned off by setting the config parameter `smart-arrows` to `false`
(the config needs to be imported before the arrows are used for that to work).
I've created a [VisualStudio Code extension](https://marketplace.visualstudio.com/items?itemName=trivernis.snekdown) for Snekdown.
This extension provides a preview of snekdown files, exports and other commands similar to the
cli. The source code can be found [here](https://github.com/Trivernis/snekdown-vscode-extension).
## Roadmap
The end goal is to have a markup language with features similar to LaTeX.
### Short Term
- [x] Checkboxes
- [x] Emojis (\:emoji:)
- [x] Colors
@ -471,9 +107,19 @@ The end goal is to have a markup language with features similar to LaTeX.
- [x] Chromium based pdf rendering
- [x] Custom Stylesheets
- [x] Smart arrows
- [ ] Custom Elements via templates (50%)
- [ ] Cross References
- [ ] Figures
- [ ] EPUB Rendering
- [ ] Text sizes
- [ ] Title pages
- [ ] Title pages
### Long Term
- Rewrite of the whole parsing process
- Custom Elements via templates
## License
This project is licensed under GPL 3.0. See LICENSE for more information.

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
pub mod tokens;
use crate::format::PlaceholderTemplate;
@ -188,6 +194,7 @@ pub enum Inline {
CharacterCode(CharacterCode),
LineBreak,
Arrow(Arrow),
Anchor(Anchor),
}
#[derive(Clone, Debug)]
@ -665,6 +672,15 @@ impl Quote {
pub fn add_text(&mut self, text: TextLine) {
self.text.push(text)
}
/// Strips a single linebreak from the end of the quote
pub fn strip_linebreak(&mut self) {
if let Some(last) = self.text.last_mut() {
if let Some(Inline::LineBreak) = last.subtext.last() {
last.subtext.pop();
}
}
}
}
impl ImportAnchor {

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
#![allow(unused)]
pub(crate) const BACKSLASH: char = '\\';
@ -33,6 +39,7 @@ pub(crate) const L_BRACE: char = '}';
pub(crate) const PERCENT: char = '%';
pub(crate) const COMMA: char = ',';
pub(crate) const MATH: char = '$';
pub(crate) const DOLLAR: char = '$';
pub(crate) const AMPERSAND: char = '&';
pub(crate) const QUESTION_MARK: char = '?';
@ -82,6 +89,18 @@ pub(crate) const CHARACTER_STOP: char = SEMICOLON;
pub(crate) const GLOSSARY_REF_START: char = TILDE;
// Reference Anchors
pub(crate) const ANCHOR_START: &'static [char] = &[R_BRACKET, QUESTION_MARK];
pub(crate) const ANCHOR_STOP: char = L_BRACKET;
// References
pub(crate) const REF_START: &'static [char] = &[R_BRACKET, DOLLAR];
pub(crate) const REF_STOP: char = L_BRACKET;
pub(crate) const REF_DESC_START: char = R_PARENTH;
pub(crate) const REF_DESC_STOP: char = L_PARENTH;
// Arrows
pub(crate) const A_RIGHT_ARROW: &'static [char] = &['-', '-', '>'];
@ -130,6 +149,8 @@ pub(crate) const INLINE_SPECIAL_SEQUENCES: &'static [&'static [char]] = &[
A_RIGHT_ARROW,
A_LEFT_ARROW,
A_LEFT_RIGHT_ARROW,
ANCHOR_START,
REF_START,
];
pub(crate) const LIST_SPECIAL_CHARS: [char; 14] = [

@ -1,3 +1,9 @@
/*!
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
body {
background-color: $body-background;
overflow-x: hidden;
@ -99,6 +105,8 @@ table tr td:first-child, table tr th:first-child {
blockquote {
margin-left: 0;
padding-top: 0.2em;
padding-bottom: 0.2em;
background-color: rgba(0, 0, 0, 0);
}

@ -1,3 +1,9 @@
/*!
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
$background-color: #1e1d2c;
$background-color-variant-1: lighten($background-color, 7%);
$background-color-variant-2: lighten($background-color, 14%);

@ -1,3 +1,9 @@
/*!
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
$background-color: darken(#2b303b, 8%);
$background-color-variant-1: lighten($background-color, 7%);
$background-color-variant-2: lighten($background-color, 14%);

@ -1,3 +1,9 @@
/*!
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
$background-color: darken(#002b36, 5%);
$background-color-variant-1: lighten($background-color, 7%);
$background-color-variant-2: lighten($background-color, 14%);

@ -1,3 +1,9 @@
/*!
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
$background-color: #FFF;
$background-color-variant-1: darken($background-color, 7%);
$background-color-variant-2: darken($background-color, 14%);

@ -1,3 +1,9 @@
/*!
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
$background-color: #FFF;
$background-color-variant-1: darken($background-color, 7%);
$background-color-variant-2: darken($background-color, 14%);

@ -1,3 +1,9 @@
/*!
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
$background-color: #fff8f0;
$background-color-variant-1: darken($background-color, 4%);
$background-color-variant-2: darken($background-color, 8%);

@ -1,3 +1,9 @@
<!--
~ Snekdown - Custom Markdown flavour and parser
~ Copyright (C) 2021 Trivernis
~ See LICENSE for more information.
-->
<div style="font-size: 10px; text-align: center; width: 100%;">
<span class="pageNumber"></span>/<span class="totalPages"></span>
</div>

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::elements::Document;
use crate::format::chromium_pdf::result::{PdfRenderingError, PdfRenderingResult};
use crate::format::html::html_writer::HTMLWriter;
@ -6,7 +12,8 @@ use crate::settings::Settings;
use crate::utils::caching::CacheStorage;
use bibliographix::Mutex;
use headless_chrome::protocol::page::PrintToPdfOptions;
use headless_chrome::{Browser, LaunchOptionsBuilder, Tab};
use headless_chrome::{Browser, Tab};
use std::env;
use std::fs;
use std::fs::OpenOptions;
use std::io::BufWriter;
@ -20,8 +27,14 @@ pub mod result;
/// Renders the document to pdf and returns the resulting bytes
pub fn render_to_pdf(document: Document) -> PdfRenderingResult<Vec<u8>> {
let cache = CacheStorage::new();
let mut file_path = PathBuf::from(format!("tmp-document.html"));
let mut file_path = PathBuf::from("tmp-document.html");
file_path = cache.get_file_path(&file_path);
if !file_path.parent().map(|p| p.exists()).unwrap_or(false) {
file_path = env::current_dir()?;
file_path.push(PathBuf::from(".tmp-document.html"))
}
let config = document.config.clone();
let mathjax = config.lock().features.include_mathjax;
@ -44,7 +57,7 @@ pub fn render_to_pdf(document: Document) -> PdfRenderingResult<Vec<u8>> {
}
});
let browser = Browser::new(LaunchOptionsBuilder::default().build().unwrap())?;
let browser = Browser::default()?;
let tab = browser.wait_for_initial_tab()?;
handle.join().unwrap()?;
tab.navigate_to(format!("file:///{}", file_path.to_string_lossy()).as_str())?;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use std::error::Error;
use std::fmt::{self, Display};
use std::io;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::settings::style_settings::Theme;
use std::io;
use std::io::Write;

@ -1,2 +1,8 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
pub mod html_writer;
pub mod to_html;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::elements::*;
use crate::format::html::html_writer::HTMLWriter;
use crate::format::style::{get_code_theme_for_theme, get_css_for_theme};
@ -62,6 +68,7 @@ impl ToHtml for Inline {
Inline::CharacterCode(code) => code.to_html(writer),
Inline::GlossaryReference(gloss) => gloss.lock().to_html(writer),
Inline::Arrow(a) => a.to_html(writer),
Inline::Anchor(a) => a.to_html(writer),
}
}
}
@ -111,9 +118,7 @@ impl ToHtml for Document {
writer.write("<!DOCTYPE html>".to_string())?;
writer.write("<html lang=\"".to_string())?;
writer.write_attribute(metadata.language)?;
writer.write("\"><head ".to_string())?;
writer.write(path)?;
writer.write("/>".to_string())?;
writer.write("\"><head>".to_string())?;
writer.write("<meta charset=\"UTF-8\">".to_string())?;
if let Some(author) = metadata.author {
@ -154,16 +159,17 @@ impl ToHtml for Document {
writer.write(style)?;
writer.write("</style>".to_string())?;
if self.config.lock().features.include_mathjax {
writer.write(format!(
"<script id=\"MathJax-script\" type=\"text/javascript\" async src={}></script>",
MATHJAX_URL
))?;
}
for stylesheet in &self.stylesheets {
let mut stylesheet = stylesheet.lock();
let data = std::mem::replace(&mut stylesheet.data, None);
if let Some(data) = data {
if self.config.lock().features.include_mathjax {
writer.write(format!(
"<script id=\"MathJax-script\" type=\"text/javascript\" async src={}></script>",
MATHJAX_URL
))?;
}
writer.write("<style>".to_string())?;
writer.write(minify(String::from_utf8(data).unwrap().as_str()))?;
writer.write("</style>".to_string())?;
@ -251,7 +257,7 @@ impl ToHtml for Paragraph {
}
if self.elements.len() > 1 {
for element in &self.elements[1..] {
writer.write("<br/>".to_string())?;
writer.write(" ".to_string())?;
element.to_html(writer)?;
}
}

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use regex::Regex;
use std::collections::HashMap;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::settings::style_settings::Theme;
use std::time::Instant;
use syntect::highlighting::ThemeSet;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
pub mod elements;
pub mod format;
pub mod parser;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use colored::Colorize;
use env_logger::Env;
use log::{Level, LevelFilter};
@ -10,7 +16,7 @@ use snekdown::settings::Settings;
use snekdown::utils::caching::CacheStorage;
use snekdown::Parser;
use std::fs::{File, OpenOptions};
use std::io::{BufWriter, Write};
use std::io::{stdout, BufWriter, Write};
use std::path::PathBuf;
use std::process::exit;
use std::sync::mpsc::channel;
@ -48,7 +54,11 @@ struct RenderOptions {
/// Path for the output file
#[structopt(parse(from_os_str))]
output: PathBuf,
output: Option<PathBuf>,
/// If the output should be written to stdout instead of the output file
#[structopt(long = "stdout")]
stdout: bool,
/// the output format
#[structopt(short, long, default_value = "html")]
@ -118,6 +128,7 @@ fn init() {
let manifest_path = PathBuf::from("Manifest.toml");
let bibliography_path = PathBuf::from("Bibliography.toml");
let glossary_path = PathBuf::from("Glossary.toml");
let css_path = PathBuf::from("style.css");
if !manifest_path.exists() {
let mut file = OpenOptions::new()
@ -135,6 +146,9 @@ fn init() {
if !glossary_path.exists() {
File::create("Glossary.toml".to_string()).unwrap();
}
if !css_path.exists() {
File::create("style.css".to_string()).unwrap();
}
}
/// Watches a file with all of its imports and renders on change
@ -165,6 +179,7 @@ fn render(opt: &RenderOptions) -> Parser {
exit(1)
}
let start = Instant::now();
let mut parser = Parser::with_defaults(ParserOptions::default().add_path(opt.input.clone()));
@ -173,16 +188,24 @@ fn render(opt: &RenderOptions) -> Parser {
log::info!("Parsing + Processing took: {:?}", start.elapsed());
let start_render = Instant::now();
let file = OpenOptions::new()
.read(true)
.write(true)
.truncate(true)
.create(true)
.open(&opt.output)
.unwrap();
let writer = BufWriter::new(file);
if let Some(output) = &opt.output {
let file = OpenOptions::new()
.read(true)
.write(true)
.truncate(true)
.create(true)
.open(output)
.unwrap();
render_format(opt, document, BufWriter::new(file));
} else {
if !opt.stdout {
log::error!("No output file specified");
exit(1)
}
render_format(opt, document, BufWriter::new(stdout()));
}
render_format(opt, document, writer);
log::info!("Rendering took: {:?}", start_render.elapsed());
log::info!("Total: {:?}", start.elapsed());
@ -190,7 +213,7 @@ fn render(opt: &RenderOptions) -> Parser {
}
#[cfg(not(feature = "pdf"))]
fn render_format(opt: &RenderOptions, document: Document, writer: BufWriter<File>) {
fn render_format<W: Write + 'static>(opt: &RenderOptions, document: Document, writer: W) {
match opt.format.as_str() {
"html" => render_html(document, writer),
_ => log::error!("Unknown format {}", opt.format),
@ -198,7 +221,7 @@ fn render_format(opt: &RenderOptions, document: Document, writer: BufWriter<File
}
#[cfg(feature = "pdf")]
fn render_format(opt: &RenderOptions, document: Document, writer: BufWriter<File>) {
fn render_format<W: Write + 'static>(opt: &RenderOptions, document: Document, writer: W) {
match opt.format.as_str() {
"html" => render_html(document, writer),
"pdf" => render_pdf(document, writer),
@ -206,14 +229,14 @@ fn render_format(opt: &RenderOptions, document: Document, writer: BufWriter<File
}
}
fn render_html(document: Document, writer: BufWriter<File>) {
fn render_html<W: Write + 'static>(document: Document, writer: W) {
let mut writer = HTMLWriter::new(Box::new(writer), document.config.lock().style.theme.clone());
document.to_html(&mut writer).unwrap();
writer.flush().unwrap();
}
#[cfg(feature = "pdf")]
fn render_pdf(document: Document, mut writer: BufWriter<File>) {
fn render_pdf<W: Write + 'static>(document: Document, mut writer: W) {
use snekdown::format::chromium_pdf::render_to_pdf;
let result = render_to_pdf(document).expect("Failed to render pdf!");

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use super::ParseResult;
use crate::elements::tokens::*;
use crate::elements::{
@ -165,6 +171,7 @@ impl ParseBlock for Parser {
fn parse_quote(&mut self) -> ParseResult<Quote> {
let start_index = self.ctm.get_index();
self.ctm.seek_whitespace();
let metadata = if let Ok(meta) = self.parse_inline_metadata() {
Some(meta)
} else {
@ -190,6 +197,8 @@ impl ParseBlock for Parser {
break;
}
}
quote.strip_linebreak();
if quote.text.len() == 0 {
return Err(self.ctm.rewind_with_error(start_index).into());
}
@ -199,11 +208,12 @@ impl ParseBlock for Parser {
/// Parses a paragraph
fn parse_paragraph(&mut self) -> ParseResult<Paragraph> {
self.ctm.seek_whitespace();
let mut paragraph = Paragraph::new();
while let Ok(token) = self.parse_line() {
paragraph.add_element(token);
while let Ok(element) = self.parse_line() {
paragraph.add_element(element);
let start_index = self.ctm.get_index();
if self.ctm.check_any_sequence(&BLOCK_SPECIAL_CHARS)
|| self.ctm.check_any(&self.block_break_at)
{

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use super::{ParseError, ParseResult};
use crate::elements::tokens::*;
use crate::elements::BibReference;
@ -39,6 +45,7 @@ pub(crate) trait ParseInline {
fn parse_template(&mut self) -> ParseResult<Template>;
fn parse_character_code(&mut self) -> ParseResult<CharacterCode>;
fn parse_arrow(&mut self) -> ParseResult<Arrow>;
fn parse_anchor(&mut self) -> ParseResult<Anchor>;
}
impl ParseInline for Parser {
@ -127,6 +134,9 @@ impl ParseInline for Parser {
} else if let Ok(arrow) = self.parse_arrow() {
log::trace!("Inline::Arrow {:?}", arrow);
Ok(Inline::Arrow(arrow))
} else if let Ok(anchor) = self.parse_anchor() {
log::trace!("Inline::Anchor {:?}", anchor);
Ok(Inline::Anchor(anchor))
} else {
let plain = self.parse_plain()?;
log::trace!("Inline::Plain {}", plain.value);
@ -680,4 +690,22 @@ impl ParseInline for Parser {
Err(self.ctm.err().into())
}
}
/// Parses an anchor elements
fn parse_anchor(&mut self) -> ParseResult<Anchor> {
let start_index = self.ctm.get_index();
self.ctm.assert_sequence(&ANCHOR_START, Some(start_index))?;
self.ctm.seek_one()?;
let key = self.ctm.get_string_until_any_or_rewind(
&[ANCHOR_STOP],
&INLINE_WHITESPACE,
start_index,
)?;
self.ctm.try_seek();
Ok(Anchor {
inner: Box::new(Line::Text(TextLine::new())),
key,
})
}
}

@ -1,7 +1,14 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use super::ParseResult;
use crate::elements::tokens::*;
use crate::elements::Inline::LineBreak;
use crate::elements::{BibEntry, Metadata};
use crate::elements::{Cell, Centered, Header, Inline, Line, ListItem, Row, Ruler, TextLine};
use crate::elements::{Cell, Centered, Header, Line, ListItem, Row, Ruler, TextLine};
use crate::parser::inline::ParseInline;
use crate::Parser;
use bibliographix::bibliography::bibliography_entry::BibliographyEntry;
@ -16,6 +23,7 @@ pub(crate) trait ParseLine {
fn parse_row(&mut self) -> ParseResult<Row>;
fn parse_centered(&mut self) -> ParseResult<Centered>;
fn parse_ruler(&mut self) -> ParseResult<Ruler>;
fn parse_paragraph_break(&mut self) -> ParseResult<TextLine>;
fn parse_text_line(&mut self) -> ParseResult<TextLine>;
fn parse_bib_entry(&mut self) -> ParseResult<BibEntry>;
}
@ -36,6 +44,9 @@ impl ParseLine for Parser {
} else if let Ok(bib) = self.parse_bib_entry() {
log::trace!("Line::BibEntry");
Ok(Line::BibEntry(bib))
} else if let Ok(text) = self.parse_paragraph_break() {
log::trace!("Line::LineBreak");
Ok(Line::Text(text))
} else if let Ok(text) = self.parse_text_line() {
log::trace!("Line::Text");
Ok(Line::Text(text))
@ -166,6 +177,8 @@ impl ParseLine for Parser {
/// Parses a line of text
fn parse_text_line(&mut self) -> ParseResult<TextLine> {
let mut text = TextLine::new();
let start_index = self.ctm.get_index();
while let Ok(subtext) = self.parse_inline() {
text.add_subtext(subtext);
if self.ctm.check_eof() || self.ctm.check_any(&self.inline_break_at) {
@ -173,21 +186,36 @@ impl ParseLine for Parser {
}
}
// add a linebreak when encountering \n\n
if self.ctm.check_char(&LB) {
if let Ok(_) = self.ctm.seek_one() {
if self.ctm.check_char(&LB) {
text.add_subtext(Inline::LineBreak)
}
self.ctm.try_seek();
if self.ctm.check_char(&LB) {
text.add_subtext(LineBreak);
self.ctm.try_seek();
}
}
if text.subtext.len() > 0 {
Ok(text)
} else {
Err(self.ctm.err().into())
Err(self.ctm.rewind_with_error(start_index).into())
}
}
/// Parses a paragraph break
fn parse_paragraph_break(&mut self) -> ParseResult<TextLine> {
let start_index = self.ctm.get_index();
self.ctm.assert_char(&LB, Some(start_index))?;
self.ctm.seek_one()?;
let mut line = TextLine::new();
line.subtext.push(LineBreak);
Ok(line)
}
fn parse_bib_entry(&mut self) -> ParseResult<BibEntry> {
let start_index = self.ctm.get_index();
self.ctm.seek_any(&INLINE_WHITESPACE)?;
@ -239,6 +267,7 @@ impl ParseLine for Parser {
}
}
};
self.ctm.seek_whitespace();
self.options
.document

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
pub(crate) mod block;
pub(crate) mod inline;
pub(crate) mod line;
@ -101,6 +107,7 @@ impl Parser {
pub fn with_defaults(options: ParserOptions) -> Self {
let text = if let Some(path) = &options.path {
let mut text = read_to_string(&path).unwrap();
text = text.replace("\r\n", "\n");
if text.chars().last() != Some('\n') {
text.push('\n');
}
@ -136,6 +143,11 @@ impl Parser {
/// Returns a string of the current position in the file
pub(crate) fn get_position_string(&self) -> String {
let char_index = self.ctm.get_index();
self.get_position_string_for_index(char_index)
}
/// Returns a string of the given index position in the file
fn get_position_string_for_index(&self, char_index: usize) -> String {
let text = self.ctm.get_text();
let mut text_unil = text[..char_index].to_vec();
let line_number = text_unil.iter().filter(|c| c == &&LB).count();
@ -180,10 +192,10 @@ impl Parser {
let anchor = Arc::new(RwLock::new(ImportAnchor::new()));
let anchor_clone = Arc::clone(&anchor);
let wg = self.wg.clone();
let mut chid_parser = self.create_child(path.clone());
let mut child_parser = self.create_child(path.clone());
let _ = thread::spawn(move || {
let document = chid_parser.parse();
let document = child_parser.parse();
anchor_clone.write().unwrap().set_document(document);
drop(wg);
@ -332,7 +344,18 @@ impl Parser {
if self.ctm.check_eof() {
break;
}
eprintln!("{}", err);
match err {
ParseError::TapeError(t) => {
log::error!(
"Parse Error: {}\n\t--> {}\n",
t,
self.get_position_string_for_index(t.get_index())
)
}
_ => {
log::error!("{}", err)
}
}
break;
}
}

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::elements::{Anchor, BoldText, ItalicText, Line, List, ListItem, PlainText, TextLine};
use crate::elements::{Inline, Url};
use bibliographix::bibliography::bib_types::article::Article;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::elements::{
Anchor, BoldText, Inline, ItalicText, Line, List, ListItem, PlainText, TextLine,
};

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
pub mod bibliography;
pub mod glossary;
pub mod placeholders;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::elements::*;
use crate::references::bibliography::create_bib_list;
use chrono::prelude::*;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::elements::{Block, Element, Inline, Line, ListItem};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::elements::{Metadata, MetadataValue};
use crate::settings::feature_settings::FeatureSettings;
use crate::settings::image_settings::ImageSettings;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use platform_dirs::{AppDirs, AppUI};
use sha2::Digest;
use std::fs;
@ -29,6 +35,7 @@ impl CacheStorage {
if let Some(extension) = path.extension() {
file_name.set_extension(extension);
}
log::trace!("Cache path is {:?}", path);
return self.location.join(PathBuf::from(file_name));
}

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::utils::caching::CacheStorage;
use indicatif::{ProgressBar, ProgressStyle};
use parking_lot::Mutex;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use crate::elements::Metadata;
use crate::utils::caching::CacheStorage;
use crate::utils::downloads::download_path;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
#[macro_export]
macro_rules! plain_text {
($e:expr) => {

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
pub mod caching;
pub mod downloads;
pub mod image_converting;

@ -1,3 +1,9 @@
/*
* Snekdown - Custom Markdown flavour and parser
* Copyright (C) 2021 Trivernis
* See LICENSE for more information.
*/
use regex::Regex;
#[macro_export]
macro_rules! parse {

Loading…
Cancel
Save