mirror of
https://github.com/TxtDot/dalet-rs.git
synced 2024-11-27 02:46:23 +03:00
feat: formatter
TODO: fix tags with optional body formatter
This commit is contained in:
parent
dfe50cd0f4
commit
74f42aa314
12 changed files with 281 additions and 71 deletions
|
@ -1,6 +1,6 @@
|
||||||
use dalet::{
|
use dalet::{
|
||||||
daletpack::*,
|
daletpack::*,
|
||||||
typed::{Hl, TNArg, Tag::*},
|
typed::{Hl, TNullArg, Tag::*},
|
||||||
};
|
};
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
@ -41,7 +41,7 @@ fn main() {
|
||||||
]
|
]
|
||||||
.into()),
|
.into()),
|
||||||
Br,
|
Br,
|
||||||
Code("Hello world".into(), TNArg::Null),
|
Code("Hello world".into(), TNullArg::Null),
|
||||||
Br,
|
Br,
|
||||||
Ul(vec![
|
Ul(vec![
|
||||||
El("abc".into()),
|
El("abc".into()),
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
# {~n text} - n is number of minimum spaces to add after trimming with indent
|
# {~n text} - n is number of minimum spaces to add after trimming with indent
|
||||||
# for each line
|
# for each line
|
||||||
#
|
#
|
||||||
# {# text} - input not modified
|
# {#text} - input not modified
|
||||||
|
#
|
||||||
#
|
#
|
||||||
# tag syntax
|
# tag syntax
|
||||||
#
|
#
|
||||||
|
@ -19,6 +20,13 @@
|
||||||
# tag argument
|
# tag argument
|
||||||
#
|
#
|
||||||
# Tags without body and argument also supported
|
# Tags without body and argument also supported
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# custom no tag syntax
|
||||||
|
#
|
||||||
|
# {-text} - paragraph, text indent is trimmed
|
||||||
|
# [[tags]] - element tag with body of multiple tags
|
||||||
|
# text - element tag with text body
|
||||||
|
|
||||||
meta "title": Daleth syntax concept
|
meta "title": Daleth syntax concept
|
||||||
meta "description": This document describes Daleth syntax and some tags
|
meta "description": This document describes Daleth syntax and some tags
|
||||||
|
@ -27,19 +35,19 @@ h1: TxtDot revolution
|
||||||
p: TxtDot is a cool project
|
p: TxtDot is a cool project
|
||||||
|
|
||||||
# If no tag is specified, then the 'el' tag is placed
|
# If no tag is specified, then the 'el' tag is placed
|
||||||
|
|
||||||
This is element
|
This is element
|
||||||
br
|
br
|
||||||
|
|
||||||
# if no tag is specified but a '{}' is present, then the 'p' tag is placed
|
# if no tag is specified but a '{- text}' is present, then the 'p' tag is placed
|
||||||
# '\n' is deleted only in this format. If a break line is needed in a paragraph, use ' \n'.
|
# '\n' is deleted in this format. If a break line is needed in a paragraph, use ' \n'.
|
||||||
{
|
{-
|
||||||
Check Dalet too
|
Check Dalet too
|
||||||
This is one paragraph
|
This is one paragraph
|
||||||
}
|
}
|
||||||
|
|
||||||
{ This is another paragraph }
|
{- This is another paragraph ({- text\}) }
|
||||||
|
|
||||||
# ( ) for argument
|
|
||||||
row "center" [
|
row "center" [
|
||||||
link "https://github.com/txtdot/txtdot": Homepage
|
link "https://github.com/txtdot/txtdot": Homepage
|
||||||
btn "https://example.com/donate" [
|
btn "https://example.com/donate" [
|
||||||
|
@ -51,7 +59,9 @@ row "center" [
|
||||||
|
|
||||||
# [] for multiple tags
|
# [] for multiple tags
|
||||||
row [
|
row [
|
||||||
[
|
# if no tag is specified but a '[[]]' is present, then the 'el' tag
|
||||||
|
# with multiple tags body placed
|
||||||
|
[[
|
||||||
h2: Features
|
h2: Features
|
||||||
|
|
||||||
ul [
|
ul [
|
||||||
|
@ -65,13 +75,12 @@ row [
|
||||||
Some kind of Material Design 3
|
Some kind of Material Design 3
|
||||||
Customization with plugins, see @txtdot/sdk and @txtdot/plugins
|
Customization with plugins, see @txtdot/sdk and @txtdot/plugins
|
||||||
]
|
]
|
||||||
|
]]
|
||||||
|
|
||||||
]
|
[[
|
||||||
|
|
||||||
[
|
|
||||||
h2: Running
|
h2: Running
|
||||||
|
|
||||||
[
|
[[
|
||||||
h3: Dev
|
h3: Dev
|
||||||
|
|
||||||
# {} for multiline strings, indent is automatically trimmed
|
# {} for multiline strings, indent is automatically trimmed
|
||||||
|
@ -87,25 +96,23 @@ row [
|
||||||
|
|
||||||
# {# Text} Text after "`# " not modified
|
# {# Text} Text after "`# " not modified
|
||||||
code "markdown" {# this is codeblock}
|
code "markdown" {# this is codeblock}
|
||||||
]
|
]]
|
||||||
|
|
||||||
[
|
[[
|
||||||
h3: Production
|
h3: Production
|
||||||
|
|
||||||
code {
|
code {
|
||||||
npm install
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
npm run start
|
npm run start
|
||||||
}
|
}
|
||||||
]
|
]]
|
||||||
|
|
||||||
[
|
[[
|
||||||
h3: Docker
|
h3: Docker
|
||||||
|
|
||||||
code: docker compose up -d
|
code: docker compose up -d
|
||||||
]
|
]]
|
||||||
|
|
||||||
]
|
]]
|
||||||
]
|
]
|
||||||
|
|
||||||
# Table has custom format if text used
|
# Table has custom format if text used
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ariadne::{Color, Label, Report, ReportKind, Source};
|
use ariadne::{Color, Label, Report, ReportKind, Source};
|
||||||
use chumsky::Parser;
|
use chumsky::Parser;
|
||||||
use dalet::daleth::lexer::lexer;
|
use dalet::daleth::{format::format, lexer::lexer};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let src_file = "daleth.dlth";
|
let src_file = "daleth.dlth";
|
||||||
|
@ -9,7 +9,10 @@ fn main() {
|
||||||
let parsed = lexer().parse(src);
|
let parsed = lexer().parse(src);
|
||||||
|
|
||||||
match parsed.into_result() {
|
match parsed.into_result() {
|
||||||
Ok(t) => println!("{:#?}", t),
|
Ok(t) => {
|
||||||
|
println!("{:#?}", t);
|
||||||
|
println!("{}", format(&t));
|
||||||
|
}
|
||||||
Err(e) => e.into_iter().for_each(|e| {
|
Err(e) => e.into_iter().for_each(|e| {
|
||||||
Report::build(ReportKind::Error, src_file, e.span().start)
|
Report::build(ReportKind::Error, src_file, e.span().start)
|
||||||
.with_code("Compiler")
|
.with_code("Compiler")
|
||||||
|
@ -23,5 +26,5 @@ fn main() {
|
||||||
.print((src_file, Source::from(&src)))
|
.print((src_file, Source::from(&src)))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}),
|
}),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
136
src/daleth/format.rs
Normal file
136
src/daleth/format.rs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
use super::{
|
||||||
|
lexer::types::{Spanned, Token},
|
||||||
|
utils::set_indent,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn nl_needed<'src>(last2: Option<&Token<'src>>, last1: Option<&Token<'src>>) -> bool {
|
||||||
|
if let Some(last1) = last1 {
|
||||||
|
if *last1 == Token::Br {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if *last1 == Token::Hr {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(last2) = last2 {
|
||||||
|
if *last2 == Token::Img {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format<'src>(spanned_tokens: &Vec<Spanned<Token<'src>>>) -> String {
|
||||||
|
let mut current_indent: usize = 0;
|
||||||
|
let mut formatted = String::new();
|
||||||
|
let len = spanned_tokens.len();
|
||||||
|
|
||||||
|
for i in 0..len {
|
||||||
|
let last2 = {
|
||||||
|
if i < 2 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
spanned_tokens.get(i - 2).map(|t| &t.0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let last1 = {
|
||||||
|
if i < 1 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
spanned_tokens.get(i - 1).map(|t| &t.0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if nl_needed(last2, last1) {
|
||||||
|
formatted.push_str("\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
let spanned_token = &spanned_tokens[i].0;
|
||||||
|
|
||||||
|
let to_push = match spanned_token {
|
||||||
|
Token::LSquare => {
|
||||||
|
current_indent += 1;
|
||||||
|
" [\n".to_owned()
|
||||||
|
}
|
||||||
|
Token::RSquare => {
|
||||||
|
current_indent -= 1;
|
||||||
|
format!("{}\n", set_indent("]", current_indent))
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::NumberArgument(n) => format!("{n}"),
|
||||||
|
Token::TextArgument(t) => format!(" \"{t}\""),
|
||||||
|
Token::TextBody(t) => format!(": {}\n", t.trim()),
|
||||||
|
Token::MLText(t) => format!(
|
||||||
|
" {{\n{}\n{}\n",
|
||||||
|
set_indent(t, current_indent + 1),
|
||||||
|
set_indent("}", current_indent)
|
||||||
|
),
|
||||||
|
Token::MLMSText(n, t) => format!(
|
||||||
|
" {{~{n}\n{}\n{}\n",
|
||||||
|
set_indent(t, current_indent + 1),
|
||||||
|
set_indent("}", current_indent)
|
||||||
|
),
|
||||||
|
Token::RMLText(t) => format!(" {{#{t}}}\n"),
|
||||||
|
Token::Comment(c) => format!("{}\n", set_indent(&format!("# {c}"), current_indent)),
|
||||||
|
|
||||||
|
Token::TextTag(t) => format!("{}\n", set_indent(t, current_indent)),
|
||||||
|
|
||||||
|
Token::El => set_indent("el", current_indent),
|
||||||
|
Token::H => set_indent("h", current_indent),
|
||||||
|
Token::P => set_indent("p", current_indent),
|
||||||
|
Token::Br => set_indent("br", current_indent),
|
||||||
|
Token::Ul => set_indent("ul", current_indent),
|
||||||
|
Token::Ol => set_indent("ol", current_indent),
|
||||||
|
Token::Row => set_indent("row", current_indent),
|
||||||
|
Token::Link => set_indent("link", current_indent),
|
||||||
|
Token::Navlink => set_indent("navlink", current_indent),
|
||||||
|
Token::Btn => set_indent("btn", current_indent),
|
||||||
|
Token::Navbtn => set_indent("navbtn", current_indent),
|
||||||
|
Token::Img => set_indent("img", current_indent),
|
||||||
|
Token::Table => set_indent("table", current_indent),
|
||||||
|
Token::Tcol => set_indent("tcol", current_indent),
|
||||||
|
Token::Tpcol => set_indent("tpcol", current_indent),
|
||||||
|
Token::Hr => set_indent("hr", current_indent),
|
||||||
|
Token::B => set_indent("b", current_indent),
|
||||||
|
Token::I => set_indent("i", current_indent),
|
||||||
|
Token::Bq => set_indent("bq", current_indent),
|
||||||
|
Token::Footlnk => set_indent("footlnk", current_indent),
|
||||||
|
Token::Footn => set_indent("footn", current_indent),
|
||||||
|
Token::A => set_indent("a", current_indent),
|
||||||
|
Token::S => set_indent("s", current_indent),
|
||||||
|
Token::Sup => set_indent("sup", current_indent),
|
||||||
|
Token::Sub => set_indent("sub", current_indent),
|
||||||
|
Token::Disc => set_indent("disc", current_indent),
|
||||||
|
Token::Block => set_indent("block", current_indent),
|
||||||
|
Token::Carousel => set_indent("carousel", current_indent),
|
||||||
|
Token::Code => set_indent("code", current_indent),
|
||||||
|
Token::Pre => set_indent("pre", current_indent),
|
||||||
|
Token::Meta => set_indent("meta", current_indent),
|
||||||
|
|
||||||
|
Token::ElOpen => {
|
||||||
|
let s = set_indent("[[", current_indent);
|
||||||
|
current_indent += 1;
|
||||||
|
format!("{s}\n")
|
||||||
|
}
|
||||||
|
Token::ElClose => {
|
||||||
|
current_indent -= 1;
|
||||||
|
format!("{}\n", set_indent("]]", current_indent))
|
||||||
|
}
|
||||||
|
Token::Paragraph(t) => format!(
|
||||||
|
"{{-\n{}\n{}\n",
|
||||||
|
set_indent(t, current_indent + 1),
|
||||||
|
set_indent("}", current_indent)
|
||||||
|
),
|
||||||
|
|
||||||
|
Token::EmptyLine => "\n".to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
formatted.push_str(&to_push);
|
||||||
|
}
|
||||||
|
|
||||||
|
formatted.trim().to_owned()
|
||||||
|
}
|
|
@ -42,11 +42,10 @@ pub fn lexer<'src>(
|
||||||
.labelled("Tag");
|
.labelled("Tag");
|
||||||
|
|
||||||
let symbol = choice((
|
let symbol = choice((
|
||||||
// just("(").to(Token::LParen).labelled("("),
|
just("[[").to(Token::ElOpen).labelled("[["),
|
||||||
// just(")").to(Token::RParen).labelled(")"),
|
just("]]").to(Token::ElClose).labelled("]]"),
|
||||||
just("[").to(Token::LSquare).labelled("["),
|
just("[").to(Token::LSquare).labelled("["),
|
||||||
just("]").to(Token::RSquare).labelled("]"),
|
just("]").to(Token::RSquare).labelled("]"),
|
||||||
// just(":").to(Token::Colon).labelled(":"),
|
|
||||||
));
|
));
|
||||||
|
|
||||||
let argument = {
|
let argument = {
|
||||||
|
@ -64,7 +63,7 @@ pub fn lexer<'src>(
|
||||||
.or(arg_escape)
|
.or(arg_escape)
|
||||||
.repeated()
|
.repeated()
|
||||||
.to_slice()
|
.to_slice()
|
||||||
.delimited_by(just("\""), just("\""))
|
.delimited_by(just('"'), just('"'))
|
||||||
.map(Token::TextArgument)
|
.map(Token::TextArgument)
|
||||||
.labelled("Text argument");
|
.labelled("Text argument");
|
||||||
|
|
||||||
|
@ -78,8 +77,8 @@ pub fn lexer<'src>(
|
||||||
|
|
||||||
let text = none_of("\n").repeated().to_slice();
|
let text = none_of("\n").repeated().to_slice();
|
||||||
|
|
||||||
let text_body = text
|
let text_body = just(':')
|
||||||
.delimited_by(just(':'), just('\n'))
|
.ignore_then(text)
|
||||||
.map(Token::TextBody)
|
.map(Token::TextBody)
|
||||||
.labelled("One line text body");
|
.labelled("One line text body");
|
||||||
|
|
||||||
|
@ -93,15 +92,12 @@ pub fn lexer<'src>(
|
||||||
.repeated()
|
.repeated()
|
||||||
.labelled("Body of multiline text");
|
.labelled("Body of multiline text");
|
||||||
|
|
||||||
let mlms_n = just("{~")
|
let paragraph = multiline_text_body
|
||||||
.ignore_then(text::int(10).from_str().unwrapped())
|
.clone()
|
||||||
.labelled("Minimum spaces number");
|
.to_slice()
|
||||||
|
.delimited_by(just("{-"), just("}"))
|
||||||
let mlmstext = mlms_n
|
.map(Token::Paragraph)
|
||||||
.then(multiline_text_body.clone().to_slice())
|
.labelled("Paragraph syntax");
|
||||||
.then_ignore(just("}"))
|
|
||||||
.map(|(n, t)| Token::MLMSText(n, t))
|
|
||||||
.labelled("Multi line text with min spaces");
|
|
||||||
|
|
||||||
let mltext = multiline_text_body
|
let mltext = multiline_text_body
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -110,26 +106,40 @@ pub fn lexer<'src>(
|
||||||
.map(Token::MLText)
|
.map(Token::MLText)
|
||||||
.labelled("Multiline text");
|
.labelled("Multiline text");
|
||||||
|
|
||||||
|
let mlmstext = {
|
||||||
|
let mlms_n = just("{~")
|
||||||
|
.ignore_then(text::int(10).from_str().unwrapped())
|
||||||
|
.labelled("Minimum spaces number");
|
||||||
|
|
||||||
|
mlms_n
|
||||||
|
.then(multiline_text_body.clone().to_slice())
|
||||||
|
.then_ignore(just("}"))
|
||||||
|
.map(|(n, t)| Token::MLMSText(n, t))
|
||||||
|
.labelled("Multi line text with min spaces")
|
||||||
|
};
|
||||||
|
|
||||||
let rmltext = multiline_text_body
|
let rmltext = multiline_text_body
|
||||||
.to_slice()
|
.to_slice()
|
||||||
.delimited_by(just("{#"), just('}'))
|
.delimited_by(just("{#"), just('}'))
|
||||||
.map(Token::RMLText)
|
.map(Token::RMLText)
|
||||||
.labelled("Raw multiline text");
|
.labelled("Raw multiline text");
|
||||||
|
|
||||||
choice((mlmstext, mltext, rmltext, text_body, text_tag))
|
choice((paragraph, mlmstext, rmltext, mltext, text_body, text_tag))
|
||||||
};
|
};
|
||||||
|
|
||||||
let comment = none_of("\n")
|
let comment = just('#')
|
||||||
.repeated()
|
.ignore_then(none_of("\n").repeated().to_slice())
|
||||||
.to_slice()
|
|
||||||
.delimited_by(just('#'), just('\n'))
|
|
||||||
.map(Token::Comment);
|
.map(Token::Comment);
|
||||||
|
|
||||||
let token = choice((comment, symbol, tag, argument, textual));
|
let empty_line = text::inline_whitespace()
|
||||||
|
.delimited_by(text::newline(), text::newline())
|
||||||
|
.to(Token::EmptyLine);
|
||||||
|
|
||||||
|
let token = choice((empty_line.clone(), comment, symbol, tag, argument, textual));
|
||||||
|
|
||||||
token
|
token
|
||||||
|
.padded_by(text::whitespace().and_is(empty_line.not()).or_not())
|
||||||
.map_with(|t, e| (t, e.span()))
|
.map_with(|t, e| (t, e.span()))
|
||||||
.padded()
|
|
||||||
.repeated()
|
.repeated()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,14 @@ pub type Spanned<T> = (T, Span);
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Token<'src> {
|
pub enum Token<'src> {
|
||||||
// Symbols
|
// Symbols
|
||||||
/// (
|
|
||||||
// LParen,
|
|
||||||
/// )
|
|
||||||
// RParen,
|
|
||||||
/// [
|
/// [
|
||||||
LSquare,
|
LSquare,
|
||||||
/// ]
|
/// ]
|
||||||
RSquare,
|
RSquare,
|
||||||
/// :
|
/// [[
|
||||||
// Colon,
|
ElOpen,
|
||||||
|
/// ]]
|
||||||
|
ElClose,
|
||||||
|
|
||||||
// Arguments
|
// Arguments
|
||||||
NumberArgument(u8),
|
NumberArgument(u8),
|
||||||
|
@ -31,8 +29,12 @@ pub enum Token<'src> {
|
||||||
RMLText(&'src str),
|
RMLText(&'src str),
|
||||||
|
|
||||||
/// Special
|
/// Special
|
||||||
Comment(&'src str),
|
|
||||||
TextTag(&'src str),
|
TextTag(&'src str),
|
||||||
|
Paragraph(&'src str),
|
||||||
|
|
||||||
|
/// Special removed before parse
|
||||||
|
Comment(&'src str),
|
||||||
|
EmptyLine,
|
||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
El,
|
El,
|
||||||
|
@ -68,8 +70,8 @@ pub enum Token<'src> {
|
||||||
Meta,
|
Meta,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
impl<'src> From<Spanned<Token<'src>>> for Token<'src> {
|
||||||
pub enum Argument<'src> {
|
fn from(value: Spanned<Token<'src>>) -> Self {
|
||||||
Number(u8),
|
value.0
|
||||||
Argument(&'src str),
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
|
pub mod format;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
|
pub mod utils;
|
||||||
|
|
52
src/daleth/utils.rs
Normal file
52
src/daleth/utils.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
pub fn trim_indent(input: &str) -> String {
|
||||||
|
let lines: Vec<&str> = input.lines().collect();
|
||||||
|
|
||||||
|
// Find the minimum indentation of non-empty lines
|
||||||
|
let min_indent = lines
|
||||||
|
.iter()
|
||||||
|
.filter(|line| !line.trim().is_empty())
|
||||||
|
.map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
|
||||||
|
.min()
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
// Trim the leading whitespace from each line by the minimum indentation
|
||||||
|
let trimmed_lines: Vec<&str> = lines
|
||||||
|
.into_iter()
|
||||||
|
.map(|line| {
|
||||||
|
if line.len() < min_indent {
|
||||||
|
line
|
||||||
|
} else {
|
||||||
|
&line[min_indent..]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
trim_newline(&trimmed_lines.join("\n")).to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_indent(input: &str, indent: usize) -> String {
|
||||||
|
prepend_indent(&trim_indent(input), &" ".repeat(indent))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trim_newline<'a>(s: &'a str) -> &'a str {
|
||||||
|
let mut trim_start = 0;
|
||||||
|
|
||||||
|
for start_char in s.chars() {
|
||||||
|
if start_char != '\n' && start_char != '\r' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
trim_start += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&s[(trim_start)..].trim_end()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepend_indent(input: &str, indent: &str) -> String {
|
||||||
|
let lines: Vec<String> = input
|
||||||
|
.lines()
|
||||||
|
.map(|line| format!("{}{}", indent, line))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
lines.join("\n")
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::typed::{
|
use crate::typed::{
|
||||||
Body, Hl, Page, TNArg,
|
Body, Hl, Page, TNullArg,
|
||||||
Tag::{self, *},
|
Tag::{self, *},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,9 +32,7 @@ pub fn parse_gemtext(s: &str) -> Result<Page, GemTextParseError> {
|
||||||
let url = body.next().ok_or(GemTextParseError::InvalidLink)?.trim();
|
let url = body.next().ok_or(GemTextParseError::InvalidLink)?.trim();
|
||||||
|
|
||||||
match body.next() {
|
match body.next() {
|
||||||
Some(label) => page.push(P(
|
Some(label) => page.push(P(vec![Navlink(label.trim().into(), url.into())].into())),
|
||||||
vec![Navlink(label.trim().into(), url.into())].into()
|
|
||||||
)),
|
|
||||||
None => page.push(P(vec![Navlink(Body::Null, url.into())].into())),
|
None => page.push(P(vec![Navlink(Body::Null, url.into())].into())),
|
||||||
};
|
};
|
||||||
} else if line.starts_with("# ") {
|
} else if line.starts_with("# ") {
|
||||||
|
@ -55,7 +53,7 @@ pub fn parse_gemtext(s: &str) -> Result<Page, GemTextParseError> {
|
||||||
page.push(Bq(body.into()));
|
page.push(Bq(body.into()));
|
||||||
} else if line.starts_with("```") {
|
} else if line.starts_with("```") {
|
||||||
if preformatted {
|
if preformatted {
|
||||||
page.push(Code(preformatted_text.join("\n"), TNArg::Null));
|
page.push(Code(preformatted_text.join("\n"), TNullArg::Null));
|
||||||
preformatted_text.clear();
|
preformatted_text.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,13 +70,13 @@ impl TryFrom<DlArgument> for AlignArg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<DlArgument> for TNArg {
|
impl TryFrom<DlArgument> for TNullArg {
|
||||||
type Error = ConversionError;
|
type Error = ConversionError;
|
||||||
|
|
||||||
fn try_from(value: DlArgument) -> Result<Self, Self::Error> {
|
fn try_from(value: DlArgument) -> Result<Self, Self::Error> {
|
||||||
match value {
|
match value {
|
||||||
DlArgument::Text(t) => Ok(TNArg::Text(t)),
|
DlArgument::Text(t) => Ok(TNullArg::Text(t)),
|
||||||
DlArgument::Null => Ok(TNArg::Null),
|
DlArgument::Null => Ok(TNullArg::Null),
|
||||||
_ => Err(ConversionError),
|
_ => Err(ConversionError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,11 +64,11 @@ impl From<AlignArg> for DlArgument {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TNArg> for DlArgument {
|
impl From<TNullArg> for DlArgument {
|
||||||
fn from(item: TNArg) -> DlArgument {
|
fn from(item: TNullArg) -> DlArgument {
|
||||||
match item {
|
match item {
|
||||||
TNArg::Text(s) => s.into(),
|
TNullArg::Text(s) => s.into(),
|
||||||
TNArg::Null => NA,
|
TNullArg::Null => NA,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub enum Tag {
|
||||||
Disc(NNBody),
|
Disc(NNBody),
|
||||||
Block(NNBody, AlignArg),
|
Block(NNBody, AlignArg),
|
||||||
Carousel(Vec<Tag>),
|
Carousel(Vec<Tag>),
|
||||||
Code(TBody, TNArg),
|
Code(TBody, TNullArg),
|
||||||
Pre(TBody),
|
Pre(TBody),
|
||||||
Meta(TBody, TArg),
|
Meta(TBody, TArg),
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ pub enum Arg {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(AutoFrom, Debug, Clone, PartialEq, Eq)]
|
#[derive(AutoFrom, Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum TNArg {
|
pub enum TNullArg {
|
||||||
Text(String),
|
Text(String),
|
||||||
Null,
|
Null,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue