From 03b5360ec5c342784f0a2c783eb6699e9bd3c725 Mon Sep 17 00:00:00 2001 From: Kerdonov Date: Wed, 24 Dec 2025 23:25:47 +0200 Subject: [PATCH 1/4] fixed weird block parsing issues --- Cargo.lock | 2 +- Cargo.toml | 2 +- bacon.toml | 1 + src/parser/block.rs | 26 ++++++++++++++------------ src/parser/inline.rs | 4 ++-- src/parser/mod.rs | 3 +++ 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33f2ea2..1873c34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "marginal" -version = "0.1.0" +version = "0.1.1" dependencies = [ "nom", ] diff --git a/Cargo.toml b/Cargo.toml index 5ebb963..9ee771e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "marginal" -version = "0.1.0" +version = "0.1.1" edition = "2024" publish = ["gitea"] diff --git a/bacon.toml b/bacon.toml index ec3d796..57b320e 100644 --- a/bacon.toml +++ b/bacon.toml @@ -66,6 +66,7 @@ command = [ "cargo", "nextest", "run", "--hide-progress-bar", "--failure-output", "final", + "--no-fail-fast", ] need_stdout = true analyzer = "nextest" diff --git a/src/parser/block.rs b/src/parser/block.rs index f1f96ad..07fd23c 100644 --- a/src/parser/block.rs +++ b/src/parser/block.rs @@ -3,6 +3,7 @@ use nom::{ IResult, Parser, branch::alt, bytes::complete::{tag, take_until}, + combinator::peek, multi::{many_m_n, many0, many1}, sequence::{delimited, terminated}, }; @@ -14,6 +15,7 @@ pub fn blocks(input: &str) -> IResult<&str, Vec, MarkdownParseError> { } pub fn block(input: &str) -> IResult<&str, Block, MarkdownParseError> { + //alt((heading_block, code_block, quote_block, paragraph_block)).parse(input) terminated( alt((heading_block, code_block, quote_block, paragraph_block)), tag("\n"), @@ -45,7 +47,7 @@ fn code_block(input: &str) -> IResult<&str, Block, MarkdownParseError> { delimited( tag("```"), (take_until("\n"), tag("\n"), take_until("```\n")), - tag("```\n"), + (tag("```"), peek(tag("\n"))), ) .parse(input) .map(|(rem, (lang, _, code))| { @@ -83,10 +85,10 @@ mod test { #[test] fn single_paragraph() { - let md = "Hello markdown!!"; + let md = "Hello markdown!!\n"; let (rem, block) = paragraph_block(md).unwrap(); - assert_eq!(rem, ""); + assert_eq!(rem, "\n"); assert_eq!( block, Block::Paragraph { @@ -107,7 +109,7 @@ fn main() { "; let (rem, block) = code_block(md).unwrap(); - assert_eq!(rem, ""); + assert_eq!(rem, "\n"); assert_eq!( block, Block::Code { @@ -125,7 +127,7 @@ echo \"hello world\" "; let (rem, block) = code_block(md).unwrap(); - assert_eq!(rem, ""); + assert_eq!(rem, "\n"); assert_eq!( block, Block::Code { @@ -145,11 +147,11 @@ echo hello } #[test] - fn level_1_heading() { - let md = "## Heading2"; + fn level_2_heading() { + let md = "## Heading2\n"; let (rem, block) = heading_block(md).unwrap(); - assert_eq!(rem, ""); + assert_eq!(rem, "\n"); assert_eq!( block, Block::Heading { @@ -163,16 +165,16 @@ echo hello #[test] fn heading_no_space() { - let md = "#heading"; + let md = "#heading\n"; assert!(heading_block(md).is_err()); } #[test] fn level_6_heading() { - let md = "###### Heading6"; + let md = "###### Heading6\n"; let (rem, block) = heading_block(md).unwrap(); - assert_eq!(rem, ""); + assert_eq!(rem, "\n"); assert_eq!( block, Block::Heading { @@ -186,7 +188,7 @@ echo hello #[test] fn no_level_7_heading() { - let md = "####### Heading7"; + let md = "####### Heading7\n"; assert!(heading_block(md).is_err()); } diff --git a/src/parser/inline.rs b/src/parser/inline.rs index 1c4941f..16c68b7 100644 --- a/src/parser/inline.rs +++ b/src/parser/inline.rs @@ -85,10 +85,10 @@ mod test { #[test] fn single_text() { - let md = "hello normal inline"; + let md = "hello normal inline\n"; let (rem, parsed) = text_inline(md).unwrap(); - assert_eq!(rem, ""); + assert_eq!(rem, "\n"); assert_eq!( parsed, Inline::Text { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 1706e49..e4adcc9 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,3 +1,6 @@ +//! A weird markdown parser. Please don't forget to add a newline in the end of a file or it won't +//! work :) + use std::fmt::{Debug, Display}; pub mod block; -- 2.49.1 From 34304546ada03d131334302933c19db1567e2286 Mon Sep 17 00:00:00 2001 From: Kerdonov Date: Fri, 26 Dec 2025 16:36:57 +0200 Subject: [PATCH 2/4] added support for parsing lists --- src/ast.rs | 2 + src/generator/block.rs | 18 ++++++- src/parser/block.rs | 103 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 121 insertions(+), 2 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 3474045..4c241ad 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -31,4 +31,6 @@ pub enum Block { Code { content: String, lang: String }, Quote { inner: Box }, Paragraph { inner: Vec }, + UnorderedList { items: Vec }, + OrderedList { items: Vec }, } diff --git a/src/generator/block.rs b/src/generator/block.rs index 1b520be..0d33b4d 100644 --- a/src/generator/block.rs +++ b/src/generator/block.rs @@ -15,12 +15,28 @@ impl ToHtml for Vec { impl ToHtml for Block { fn to_html(&self) -> String { match self { - Block::Paragraph { inner } => format!("

{}


", inner.to_html()), + Block::Paragraph { inner } => format!("

{}

", inner.to_html()), Block::Heading { inner, level } => { format!("{}", level, inner.to_html(), level) } Block::Code { content, lang: _ } => format!("
{content}
"), Block::Quote { inner } => format!("
{}
", inner.to_html()), + Block::UnorderedList { items } => { + let mut html = "
    ".to_string(); + for item in items { + html.push_str(&format!("
  • {}
  • ", &item.to_html())); + } + html.push_str("
"); + html + } + Block::OrderedList { items } => { + let mut html = "
    ".to_string(); + for item in items { + html.push_str(&format!("
  1. {}
  2. ", &item.to_html())); + } + html.push_str("
"); + html + } } } } diff --git a/src/parser/block.rs b/src/parser/block.rs index 07fd23c..433841a 100644 --- a/src/parser/block.rs +++ b/src/parser/block.rs @@ -17,7 +17,14 @@ pub fn blocks(input: &str) -> IResult<&str, Vec, MarkdownParseError> { pub fn block(input: &str) -> IResult<&str, Block, MarkdownParseError> { //alt((heading_block, code_block, quote_block, paragraph_block)).parse(input) terminated( - alt((heading_block, code_block, quote_block, paragraph_block)), + alt(( + heading_block, + code_block, + quote_block, + paragraph_block, + ordered_list, + unordered_list, + )), tag("\n"), ) .parse(input) @@ -74,6 +81,20 @@ fn quote_block(input: &str) -> IResult<&str, Block, MarkdownParseError> { }) } +fn unordered_list(input: &str) -> IResult<&str, Block, MarkdownParseError> { + many1((tag("- "), block)).parse(input).map(|(rem, v)| { + let items = v.into_iter().map(|(_, b)| b).collect(); + (rem, Block::UnorderedList { items }) + }) +} + +fn ordered_list(input: &str) -> IResult<&str, Block, MarkdownParseError> { + many1((tag("1. "), block)).parse(input).map(|(rem, v)| { + let items = v.into_iter().map(|(_, b)| b).collect(); + (rem, Block::OrderedList { items }) + }) +} + //|-------------------------------------------------------------------------------| //| TESTS TESTS TESTS TESTS TESTS TESTS TESTS TESTS TESTS TESTS TESTS TESTS TESTS | //|-------------------------------------------------------------------------------| @@ -235,4 +256,84 @@ Hello MD ] ); } + + #[test] + fn simple_unordered_list() { + let md = "- a\n- b\n- c\n- b again with some `code`\n\n"; + let (rem, block) = unordered_list(md).unwrap(); + + assert_eq!(rem, "\n"); + assert_eq!( + block, + Block::UnorderedList { + items: vec![ + Block::Paragraph { + inner: vec![Inline::Text { + content: "a".to_string() + }] + }, + Block::Paragraph { + inner: vec![Inline::Text { + content: "b".to_string() + }] + }, + Block::Paragraph { + inner: vec![Inline::Text { + content: "c".to_string() + }] + }, + Block::Paragraph { + inner: vec![ + Inline::Text { + content: "b again with some ".to_string() + }, + Inline::Code { + content: "code".to_string() + } + ] + }, + ] + } + ); + } + + #[test] + fn simple_ordered_list() { + let md = "1. a\n1. b\n1. c\n1. b again with some `code`\n\n"; + let (rem, block) = ordered_list(md).unwrap(); + + assert_eq!(rem, "\n"); + assert_eq!( + block, + Block::OrderedList { + items: vec![ + Block::Paragraph { + inner: vec![Inline::Text { + content: "a".to_string() + }] + }, + Block::Paragraph { + inner: vec![Inline::Text { + content: "b".to_string() + }] + }, + Block::Paragraph { + inner: vec![Inline::Text { + content: "c".to_string() + }] + }, + Block::Paragraph { + inner: vec![ + Inline::Text { + content: "b again with some ".to_string() + }, + Inline::Code { + content: "code".to_string() + } + ] + }, + ] + } + ); + } } -- 2.49.1 From ba598f7e3445f1173d4f22c6d122113b6b22ddb7 Mon Sep 17 00:00:00 2001 From: Kerdonov Date: Fri, 26 Dec 2025 16:38:14 +0200 Subject: [PATCH 3/4] removed broken release workflow --- .../workflows/{cargo-release.yml => cargo-release.yml.notready} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .gitea/workflows/{cargo-release.yml => cargo-release.yml.notready} (100%) diff --git a/.gitea/workflows/cargo-release.yml b/.gitea/workflows/cargo-release.yml.notready similarity index 100% rename from .gitea/workflows/cargo-release.yml rename to .gitea/workflows/cargo-release.yml.notready -- 2.49.1 From b7ca4ac6e395d84146490189ef053be85c492a8d Mon Sep 17 00:00:00 2001 From: Kerdonov Date: Fri, 26 Dec 2025 16:39:10 +0200 Subject: [PATCH 4/4] version 0.1.2 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1873c34..4caf412 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "marginal" -version = "0.1.1" +version = "0.1.2" dependencies = [ "nom", ] diff --git a/Cargo.toml b/Cargo.toml index 9ee771e..d4b710b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "marginal" -version = "0.1.1" +version = "0.1.2" edition = "2024" publish = ["gitea"] -- 2.49.1