diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ff15abd..b332999 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -81,7 +81,7 @@ jobs:
run: |
rustup update nightly
rustup default nightly
- cargo install cargo-afl
+ cargo install afl
- name: "Fuzz"
run: |
echo core | sudo tee /proc/sys/kernel/core_pattern
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index fb43acd..0000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,104 +0,0 @@
-name: release
-
-on:
- push:
- tags: ["[0-9]+.[0-9]+.[0-9]+*"]
-
-permissions:
- contents: write
-
-env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
-jobs:
- create:
- name: create release
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- ref: ${{ github.ref_name }}
- - name: verify version matches
- shell: bash
- run: grep -q 'version = "${{ github.ref_name }}"' Cargo.toml || { echo version mismatch >&2 && exit 1; }
- - name: create release
- run: gh release create ${{ github.ref_name }} --draft --verify-tag --title "Release ${{ github.ref_name }}"
-
- build:
- name: build
- needs: ['create']
- runs-on: ${{ matrix.os }}
- strategy:
- fail-fast: false
- matrix:
- include:
- - os: ubuntu-latest
- target: x86_64-unknown-linux-gnu
- - os: ubuntu-latest
- target: i686-unknown-linux-musl
- - os: macos-latest
- target: x86_64-apple-darwin
- - os: macos-latest
- target: aarch64-apple-darwin
- - os: windows-latest
- target: x86_64-pc-windows-msvc
- - os: windows-latest
- target: i686-pc-windows-msvc
-
- steps:
- - name: checkout
- uses: actions/checkout@v4
- with:
- ref: ${{ github.ref_name }}
-
- - name: install rust
- shell: bash
- run: |
- rustup update stable
- rustup target add ${{ matrix.target }}
-
- - name: build
- shell: bash
- run: |
- if [ -n "${{ matrix.linker }}" ]; then
- export RUSTFLAGS="-Clinker=${{ matrix.linker }}"
- fi
- cargo build --verbose --release --target ${{ matrix.target }}
- find .
- bin="target/${{ matrix.target }}/release/jotdown"
- [ "${{ matrix.os }}" = "windows-latest" ] && bin="$bin.exe"
- echo "BIN=$bin" >> $GITHUB_ENV
-
- - name: strip
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
- run: strip $BIN
-
- - name: set archive name
- shell: bash
- run: echo "ARCHIVE=jotdown-${{ github.ref_name }}-${{ matrix.target }}" >> $GITHUB_ENV
-
- - name: init archive dir
- shell: bash
- run: |
- mkdir "$ARCHIVE"/
- cp "$BIN" "$ARCHIVE"/
- cp {COPYING,CHANGELOG.md,README.md} "$ARCHIVE"/
-
- - name: archive (win)
- if: ${{ startsWith(matrix.os, 'windows') }}
- shell: bash
- run: |
- 7z a "$ARCHIVE.zip" "$ARCHIVE"
- echo "ASSET=$ARCHIVE.zip" >> $GITHUB_ENV
-
- - name: archive (unix)
- if: ${{ ! startsWith(matrix.os, 'windows') }}
- shell: bash
- run: |
- tar czf "$ARCHIVE.tar.gz" "$ARCHIVE"
- echo "ASSET=$ARCHIVE.tar.gz" >> $GITHUB_ENV
-
- - name: Upload release archive
- shell: bash
- run: |
- gh release upload ${{ github.ref_name }} ${{ env.ASSET }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 808e00c..005ee7c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,24 +1,3 @@
-## [0.3.2](https://github.com/hellux/jotdown/releases/tag/0.3.2) - 2023-09-06
-
-### Changed
-
-- Alphabetic list markers can only be one character long.
-
-## [0.3.1](https://github.com/hellux/jotdown/releases/tag/0.3.1) - 2023-08-05
-
-### Changed
-
-- Block parser performance improved, up to 15% faster.
-- Last `unsafe` block removed (#5).
-
-### Fixed
-
-- Do not require indent for continuing footnotes.
-- Transfer classes from reference definitions to links.
-- Allow line breaks in reference links (still match reference label).
-- Remove excess newline after raw blocks.
-- HTML renderer: fix missing `
` tags after ordered lists (#44).
-
## [0.3.0](https://github.com/hellux/jotdown/releases/tag/0.3.0) - 2023-05-16
### Added
diff --git a/Cargo.lock b/Cargo.lock
index d430cbb..c27d81a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -162,29 +162,6 @@ dependencies = [
"itertools",
]
-[[package]]
-name = "databake"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82175d72e69414ceafbe2b49686794d3a8bed846e0d50267355f83ea8fdd953a"
-dependencies = [
- "databake-derive",
- "proc-macro2",
- "quote",
-]
-
-[[package]]
-name = "databake-derive"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.16",
- "synstructure",
-]
-
[[package]]
name = "either"
version = "1.8.1"
@@ -286,14 +263,11 @@ dependencies = [
[[package]]
name = "jotdown"
-version = "0.3.2"
-dependencies = [
- "databake",
-]
+version = "0.3.0"
[[package]]
name = "jotdown_wasm"
-version = "0.3.2"
+version = "0.3.0"
dependencies = [
"git2",
"jotdown",
@@ -396,9 +370,9 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
name = "proc-macro2"
-version = "1.0.79"
+version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
dependencies = [
"unicode-ident",
]
@@ -459,7 +433,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
- "syn 1.0.107",
+ "syn",
]
[[package]]
@@ -484,28 +458,6 @@ dependencies = [
"unicode-ident",
]
-[[package]]
-name = "syn"
-version = "2.0.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "synstructure"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.16",
-]
-
[[package]]
name = "test-html-ref"
version = "0.1.0"
@@ -621,7 +573,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 1.0.107",
+ "syn",
"wasm-bindgen-shared",
]
@@ -643,7 +595,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
- "syn 1.0.107",
+ "syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
diff --git a/Cargo.toml b/Cargo.toml
index 012cf4e..2fe74dd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,7 @@
name = "jotdown"
description = "A parser for the Djot markup language"
authors = ["Noah Hellman "]
-version = "0.3.2"
+version = "0.3.0"
license = "MIT"
edition = "2021"
keywords = ["djot", "markup"]
@@ -35,14 +35,10 @@ exclude = [
[[bin]]
name = "jotdown"
-required-features = ["html", "parser"]
+required-features = ["html"]
doc = false
[features]
default = ["html"]
html = [] # html renderer and minimal cli binary
deterministic = [] # for stable fuzzing
-parser = []
-
-[dependencies]
-databake = { version = "0.1.7", features = ["derive"] }
diff --git a/examples/jotdown_wasm/Cargo.toml b/examples/jotdown_wasm/Cargo.toml
index 3593138..5f0c3fb 100644
--- a/examples/jotdown_wasm/Cargo.toml
+++ b/examples/jotdown_wasm/Cargo.toml
@@ -3,7 +3,7 @@ name = "jotdown_wasm"
description = "Web demo of Jotdown"
authors = ["Noah Hellman "]
license = "MIT"
-version = "0.3.2"
+version = "0.3.0"
edition = "2021"
homepage = "https://hllmn.net/projects/jotdown"
repository = "https://github.com/hellux/jotdown"
diff --git a/src/attr.rs b/src/attr.rs
index ffe178e..3bc4e86 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -1,17 +1,13 @@
-use databake::Bake;
-
use crate::CowStr;
-use std::{borrow::Cow, fmt};
+use std::fmt;
/// Parse attributes, assumed to be valid.
-#[cfg(feature = "parser")]
pub(crate) fn parse(src: &str) -> Attributes {
let mut a = Attributes::new();
a.parse(src);
a
}
-#[cfg(feature = "parser")]
pub fn valid(src: &str) -> usize {
use State::*;
@@ -35,8 +31,7 @@ pub fn valid(src: &str) -> usize {
/// Stores an attribute value that supports backslash escapes of ASCII punctuation upon displaying,
/// without allocating.
-#[derive(Clone, Debug, Eq, PartialEq, Bake)]
-#[databake(path = jotdown)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AttributeValue<'s> {
raw: CowStr<'s>,
}
@@ -123,9 +118,8 @@ impl<'s> Iterator for AttributeValueParts<'s> {
// Attributes are relatively rare, we choose to pay 8 bytes always and sometimes an extra
// indirection instead of always 24 bytes.
#[allow(clippy::box_vec)]
-#[derive(Clone, PartialEq, Eq, Default, Bake)]
-#[databake(path = jotdown)]
-pub struct Attributes<'s>(pub Option)]>>);
+#[derive(Clone, PartialEq, Eq, Default)]
+pub struct Attributes<'s>(Option)>>>);
impl<'s> Attributes<'s> {
/// Create an empty collection.
@@ -135,13 +129,11 @@ impl<'s> Attributes<'s> {
}
#[must_use]
- #[cfg(feature = "parser")]
pub(crate) fn take(&mut self) -> Self {
Self(self.0.take())
}
/// Parse and append attributes, assumed to be valid.
- #[cfg(feature = "parser")]
pub(crate) fn parse(&mut self, input: &'s str) {
let mut parser = Parser::new(self.take());
parser.parse(input);
@@ -149,13 +141,12 @@ impl<'s> Attributes<'s> {
}
/// Combine all attributes from both objects, prioritizing self on conflicts.
- #[cfg(feature = "parser")]
pub(crate) fn union(&mut self, other: Self) {
if let Some(attrs0) = &mut self.0 {
if let Some(mut attrs1) = other.0 {
- for (key, val) in attrs1.to_mut().drain(..) {
+ for (key, val) in attrs1.drain(..) {
if key == "class" || !attrs0.iter().any(|(k, _)| *k == key) {
- attrs0.to_mut().push((key, val));
+ attrs0.push((key, val));
}
}
}
@@ -179,7 +170,7 @@ impl<'s> Attributes<'s> {
let attrs = self.0.as_mut().unwrap();
if let Some(i) = attrs.iter().position(|(k, _)| *k == key) {
- let prev = &mut attrs.to_mut()[i].1;
+ let prev = &mut attrs[i].1;
if key == "class" {
match val.raw {
CowStr::Borrowed(s) => prev.extend(s),
@@ -193,7 +184,7 @@ impl<'s> Attributes<'s> {
i
} else {
let i = attrs.len();
- attrs.to_mut().push((key, val));
+ attrs.push((key, val));
i
}
}
@@ -247,12 +238,10 @@ impl<'s> std::fmt::Debug for Attributes<'s> {
}
#[derive(Clone)]
-#[cfg(feature = "parser")]
pub struct Validator {
state: State,
}
-#[cfg(feature = "parser")]
impl Validator {
pub fn new() -> Self {
Self {
@@ -285,14 +274,12 @@ impl Validator {
///
/// Input is assumed to contain a valid series of attribute sets, the attributes are added as they
/// are encountered.
-#[cfg(feature = "parser")]
pub struct Parser<'s> {
attrs: Attributes<'s>,
i_prev: usize,
state: State,
}
-#[cfg(feature = "parser")]
impl<'s> Parser<'s> {
pub fn new(attrs: Attributes<'s>) -> Self {
Self {
@@ -323,7 +310,7 @@ impl<'s> Parser<'s> {
Identifier => self.attrs.insert("id", content.into()),
Key => self.i_prev = self.attrs.insert_pos(content, "".into()),
Value | ValueQuoted | ValueContinued => {
- self.attrs.0.as_mut().unwrap().to_mut()[self.i_prev]
+ self.attrs.0.as_mut().unwrap()[self.i_prev]
.1
.extend(&content[usize::from(matches!(st, ValueQuoted))..]);
}
@@ -351,7 +338,6 @@ impl<'s> Parser<'s> {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[cfg(feature = "parser")]
enum State {
Start,
Whitespace,
@@ -371,7 +357,6 @@ enum State {
Invalid,
}
-#[cfg(feature = "parser")]
impl State {
fn step(self, c: u8) -> State {
use State::*;
@@ -414,12 +399,11 @@ impl State {
}
}
-#[cfg(feature = "parser")]
pub fn is_name(c: u8) -> bool {
c.is_ascii_alphanumeric() || matches!(c, b':' | b'_' | b'-')
}
-#[cfg(all(test, feature = "parser"))]
+#[cfg(test)]
mod test {
macro_rules! test_attr {
($src:expr $(,$($av:expr),* $(,)?)?) => {
diff --git a/src/block.rs b/src/block.rs
index c28363e..ba8410c 100644
--- a/src/block.rs
+++ b/src/block.rs
@@ -353,7 +353,7 @@ impl<'s> TreeParser<'s> {
span_end: Range,
mut lines: &mut [Range],
) {
- if let Kind::Fenced { indent, spec, .. } = k {
+ if let Kind::Fenced { indent, .. } = k {
for line in lines.iter_mut() {
let indent_line = self.src.as_bytes()[line.clone()]
.iter()
@@ -361,14 +361,6 @@ impl<'s> TreeParser<'s> {
.count();
line.start += (*indent).min(indent_line);
}
-
- // trim ending whitespace of raw block
- if spec.starts_with('=') {
- let l = lines.len();
- if l > 0 {
- lines[l - 1] = self.trim_end(lines[l - 1].clone());
- }
- }
} else {
// trim starting whitespace of each inline
for line in lines.iter_mut() {
@@ -1019,22 +1011,21 @@ impl<'s> IdentifiedBlock<'s> {
let numbering = if first.is_ascii_digit() {
Decimal
- } else if is_roman_lower_digit(first) {
- RomanLower
- } else if is_roman_upper_digit(first) {
- RomanUpper
} else if first.is_ascii_lowercase() {
AlphaLower
} else if first.is_ascii_uppercase() {
AlphaUpper
+ } else if is_roman_lower_digit(first) {
+ RomanLower
+ } else if is_roman_upper_digit(first) {
+ RomanUpper
} else {
return None;
};
let max_len = match numbering {
- AlphaLower | AlphaUpper => 1,
Decimal => 19,
- RomanLower | RomanUpper => 13,
+ AlphaLower | AlphaUpper | RomanLower | RomanUpper => 13,
};
let chars_num = chars.clone();
@@ -1066,6 +1057,17 @@ impl<'s> IdentifiedBlock<'s> {
};
let len_style = usize::from(start_paren) + 1;
+ let chars_num = std::iter::once(first).chain(chars_num.take(len_num - 1));
+ let numbering = if matches!(numbering, AlphaLower)
+ && chars_num.clone().all(is_roman_lower_digit)
+ {
+ RomanLower
+ } else if matches!(numbering, AlphaUpper) && chars_num.clone().all(is_roman_upper_digit) {
+ RomanUpper
+ } else {
+ numbering
+ };
+
if chars.next().map_or(true, |c| c.is_ascii_whitespace()) {
Some((numbering, style, len_num + len_style))
} else {
@@ -3119,6 +3121,16 @@ mod test {
"I.",
1
);
+ test_block!(
+ "IJ. abc\n",
+ Kind::ListItem {
+ indent: 0,
+ ty: Ordered(AlphaUpper, Period),
+ last_blankline: false,
+ },
+ "IJ.",
+ 1
+ );
test_block!(
"(a) abc\n",
Kind::ListItem {
diff --git a/src/html.rs b/src/html.rs
index dd91fdf..cbefb10 100644
--- a/src/html.rs
+++ b/src/html.rs
@@ -295,13 +295,17 @@ impl<'s> Writer<'s> {
}
match c {
Container::Blockquote => out.write_str("")?,
- Container::List { kind, .. } => {
+ Container::List {
+ kind: ListKind::Unordered | ListKind::Task,
+ ..
+ } => {
self.list_tightness.pop();
- match kind {
- ListKind::Unordered | ListKind::Task => out.write_str("")?,
- ListKind::Ordered { .. } => out.write_str("")?,
- }
+ out.write_str("")?;
}
+ Container::List {
+ kind: ListKind::Ordered { .. },
+ ..
+ } => out.write_str("")?,
Container::ListItem | Container::TaskListItem { .. } => {
out.write_str("")?;
}
diff --git a/src/inline.rs b/src/inline.rs
index f1c71c5..cb92020 100644
--- a/src/inline.rs
+++ b/src/inline.rs
@@ -732,36 +732,41 @@ impl<'s> Parser<'s> {
image,
} => {
let span_spec = self.events[e_opener].span.end..self.input.span.start;
- let multiline_spec =
+ let multiline =
self.events[e_opener].span.start < self.input.span_line.start;
let spec: CowStr = if span_spec.is_empty() && !inline {
+ let span_spec = self.events[event_span].span.end
+ ..self.events[e_opener - 1].span.start;
let events_text = self
.events
.iter()
.skip(event_span + 1)
.take(e_opener - event_span - 2);
- let mut spec = String::new();
- let mut span = 0..0;
- for ev in events_text.filter(|ev| {
- matches!(ev.kind, EventKind::Str | EventKind::Atom(..))
- && !matches!(ev.kind, EventKind::Atom(Escape))
- }) {
- if matches!(ev.kind, EventKind::Atom(Softbreak | Hardbreak)) {
- spec.push_str(&self.input.src[span.clone()]);
- spec.push(' ');
- span = ev.span.end..ev.span.end;
- } else if span.end == ev.span.start {
- span.end = ev.span.end;
- } else {
- spec.push_str(&self.input.src[span.clone()]);
- span = ev.span.clone();
+ if multiline
+ || events_text.clone().any(|ev| {
+ !matches!(ev.kind, EventKind::Str | EventKind::Atom(..))
+ })
+ {
+ let mut spec = String::new();
+ let mut span = 0..0;
+ for ev in events_text.filter(|ev| {
+ matches!(ev.kind, EventKind::Str | EventKind::Atom(..))
+ }) {
+ if span.end == ev.span.start {
+ span.end = ev.span.end;
+ } else {
+ spec.push_str(&self.input.src[span.clone()]);
+ span = ev.span.clone();
+ }
}
+ spec.push_str(&self.input.src[span]);
+ spec.into()
+ } else {
+ self.input.src[span_spec].into()
}
- spec.push_str(&self.input.src[span]);
- spec.into()
- } else if multiline_spec {
+ } else if multiline {
let mut spec = String::new();
let mut first_part = true;
let mut span =
diff --git a/src/lib.rs b/src/lib.rs
index ae87054..3d0754e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -49,25 +49,19 @@
#![allow(clippy::blocks_in_if_conditions)]
use std::fmt;
-#[cfg(feature = "parser")]
use std::fmt::Write as FmtWrite;
use std::io;
-#[cfg(feature = "parser")]
use std::ops::Range;
#[cfg(feature = "html")]
pub mod html;
mod attr;
-#[cfg(feature = "parser")]
mod block;
-#[cfg(feature = "parser")]
mod inline;
-#[cfg(feature = "parser")]
mod lex;
pub use attr::{AttributeValue, AttributeValueParts, Attributes};
-use databake::Bake;
type CowStr<'s> = std::borrow::Cow<'s, str>;
@@ -203,8 +197,7 @@ impl<'s> AsRef> for &Event<'s> {
/// multiple events. [`Container`] elements are represented by a [`Event::Start`] followed by
/// events representing its content, and finally a [`Event::End`]. Atomic elements without any
/// inside elements are represented by a single event.
-#[derive(Debug, Clone, PartialEq, Eq, Bake)]
-#[databake(path = jotdown)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event<'s> {
/// Start of a container.
Start(Container<'s>, Attributes<'s>),
@@ -251,8 +244,7 @@ pub enum Event<'s> {
/// - inline, may only contain inline elements,
/// - block leaf, may only contain inline elements,
/// - block container, may contain any block-level elements.
-#[derive(Debug, Clone, PartialEq, Eq, Bake)]
-#[databake(path = jotdown)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Container<'s> {
/// A blockquote element.
Blockquote,
@@ -406,8 +398,7 @@ impl<'s> Container<'s> {
}
/// Alignment of a table column.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
-#[databake(path = jotdown)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Alignment {
Unspecified,
Left,
@@ -416,8 +407,7 @@ pub enum Alignment {
}
/// The type of an inline span link.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
-#[databake(path = jotdown)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpanLinkType {
/// E.g. `[text](url)`
Inline,
@@ -428,8 +418,7 @@ pub enum SpanLinkType {
}
/// The type of an inline link.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
-#[databake(path = jotdown)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LinkType {
/// E.g. `[text](url)`.
Span(SpanLinkType),
@@ -440,8 +429,7 @@ pub enum LinkType {
}
/// The type of a list.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
-#[databake(path = jotdown)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ListKind {
/// A bullet list.
Unordered,
@@ -456,8 +444,7 @@ pub enum ListKind {
}
/// Numbering type of an ordered list.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
-#[databake(path = jotdown)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OrderedListNumbering {
/// Decimal numbering, e.g. `1)`.
Decimal,
@@ -472,8 +459,7 @@ pub enum OrderedListNumbering {
}
/// Style of an ordered list.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Bake)]
-#[databake(path = jotdown)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OrderedListStyle {
/// Number is followed by a period, e.g. `1.`.
Period,
@@ -484,7 +470,6 @@ pub enum OrderedListStyle {
}
impl OrderedListNumbering {
- #[cfg(feature = "parser")]
fn parse_number(self, n: &str) -> u64 {
match self {
Self::Decimal => n.parse().unwrap(),
@@ -539,7 +524,6 @@ impl OrderedListNumbering {
}
impl OrderedListStyle {
- #[cfg(feature = "parser")]
fn number(self, marker: &str) -> &str {
&marker[usize::from(matches!(self, Self::ParenParen))..marker.len() - 1]
}
@@ -550,9 +534,9 @@ type Map = std::collections::HashMap;
#[cfg(feature = "deterministic")]
type Map = std::collections::BTreeMap;
-#[cfg(all(not(feature = "deterministic"), feature = "parser"))]
+#[cfg(not(feature = "deterministic"))]
type Set = std::collections::HashSet;
-#[cfg(all(feature = "deterministic", feature = "parser"))]
+#[cfg(feature = "deterministic")]
type Set = std::collections::BTreeSet;
/// A parser that generates [`Event`]s from a Djot document.
@@ -564,7 +548,6 @@ type Set = std::collections::BTreeSet;
///
/// It is possible to clone the parser to e.g. avoid performing the block parsing multiple times.
#[derive(Clone)]
-#[cfg(feature = "parser")]
pub struct Parser<'s> {
src: &'s str,
@@ -589,7 +572,6 @@ pub struct Parser<'s> {
}
#[derive(Clone)]
-#[cfg(feature = "parser")]
struct Heading {
/// Location of heading in src.
location: u32,
@@ -603,7 +585,6 @@ struct Heading {
/// Because of potential future references, an initial pass is required to obtain all definitions.
#[derive(Clone)]
-#[cfg(feature = "parser")]
struct PrePass<'s> {
/// Link definitions and their attributes.
link_definitions: Map<&'s str, (CowStr<'s>, attr::Attributes<'s>)>,
@@ -613,61 +594,53 @@ struct PrePass<'s> {
headings_lex: Vec,
}
-#[cfg(feature = "parser")]
impl<'s> PrePass<'s> {
#[must_use]
fn new(
src: &'s str,
- mut blocks: std::slice::Iter>,
+ blocks: std::slice::Iter>,
inline_parser: &mut inline::Parser<'s>,
) -> Self {
let mut link_definitions = Map::new();
let mut headings: Vec = Vec::new();
let mut used_ids: Set = Set::new();
+ let mut blocks = blocks.peekable();
+
let mut attr_prev: Option> = None;
while let Some(e) = blocks.next() {
match e.kind {
block::EventKind::Enter(block::Node::Leaf(block::Leaf::LinkDefinition {
label,
})) => {
+ fn next_is_inline(
+ bs: &mut std::iter::Peekable>,
+ ) -> bool {
+ matches!(bs.peek().map(|e| &e.kind), Some(block::EventKind::Inline))
+ }
+
// All link definition tags have to be obtained initially, as references can
// appear before the definition.
let attrs = attr_prev
.as_ref()
.map_or_else(Attributes::new, |sp| attr::parse(&src[sp.clone()]));
- let url = if let Some(block::Event {
- kind: block::EventKind::Inline,
- span,
- }) = blocks.next()
- {
- let start =
- src[span.clone()].trim_matches(|c: char| c.is_ascii_whitespace());
- if let Some(block::Event {
- kind: block::EventKind::Inline,
- span,
- }) = blocks.next()
- {
+ let url = if !next_is_inline(&mut blocks) {
+ "".into()
+ } else {
+ let start = src[blocks.next().as_ref().unwrap().span.clone()]
+ .trim_matches(|c: char| c.is_ascii_whitespace());
+ if !next_is_inline(&mut blocks) {
+ start.into()
+ } else {
let mut url = start.to_string();
- url.push_str(
- src[span.clone()].trim_matches(|c: char| c.is_ascii_whitespace()),
- );
- while let Some(block::Event {
- kind: block::EventKind::Inline,
- span,
- }) = blocks.next()
- {
+ while next_is_inline(&mut blocks) {
url.push_str(
- src[span.clone()]
+ src[blocks.next().as_ref().unwrap().span.clone()]
.trim_matches(|c: char| c.is_ascii_whitespace()),
);
}
- url.into() // owned
- } else {
- start.into() // borrowed
+ url.into()
}
- } else {
- "".into() // static
};
link_definitions.insert(label, (url, attrs));
}
@@ -802,7 +775,6 @@ impl<'s> PrePass<'s> {
}
}
-#[cfg(feature = "parser")]
impl<'s> Parser<'s> {
#[must_use]
pub fn new(src: &'s str) -> Self {
@@ -1186,7 +1158,6 @@ impl<'s> Parser<'s> {
}
}
-#[cfg(feature = "parser")]
impl<'s> Iterator for Parser<'s> {
type Item = Event<'s>;
@@ -1199,12 +1170,10 @@ impl<'s> Iterator for Parser<'s> {
/// event within the input.
///
/// See the documentation of [`Parser::into_offset_iter`] for more information.
-#[cfg(feature = "parser")]
pub struct OffsetIter<'s> {
parser: Parser<'s>,
}
-#[cfg(feature = "parser")]
impl<'s> Iterator for OffsetIter<'s> {
type Item = (Event<'s>, Range);
@@ -1214,7 +1183,6 @@ impl<'s> Iterator for OffsetIter<'s> {
}
#[cfg(test)]
-#[cfg(feature = "parser")]
mod test {
use super::Attributes;
use super::Container::*;
@@ -1559,39 +1527,7 @@ mod test {
test_parse!(
"``` =html\n\n```",
Start(RawBlock { format: "html" }, Attributes::new()),
- Str("".into()),
- End(RawBlock { format: "html" }),
- );
- }
-
- #[test]
- fn raw_block_whitespace() {
- test_parse!(
- concat!(
- "```=html\n", //
- "\n", //
- "\n", //
- "```\n", //
- "\n", //
- "paragraph\n", //
- "\n", //
- "```=html\n", //
- "\n", //
- "\n", //
- "```\n", //
- ),
- Start(RawBlock { format: "html" }, Attributes::new()),
- Str("\n".into()),
- Str("".into()),
- End(RawBlock { format: "html" }),
- Blankline,
- Start(Paragraph, Attributes::new()),
- Str("paragraph".into()),
- End(Paragraph),
- Blankline,
- Start(RawBlock { format: "html" }, Attributes::new()),
- Str("\n".into()),
- Str("".into()),
+ Str("\n".into()),
End(RawBlock { format: "html" }),
);
}
@@ -1754,46 +1690,6 @@ mod test {
);
}
- #[test]
- fn link_reference_multiline_empty() {
- test_parse!(
- concat!(
- "> [a\n", //
- "> b][]\n", //
- "> [a\\\n", //
- "> b][]\n", //
- "\n", //
- "[a b]: url\n", //
- ),
- Start(Blockquote, Attributes::new()),
- Start(Paragraph, Attributes::new()),
- Start(
- Link("url".into(), LinkType::Span(SpanLinkType::Reference)),
- Attributes::new()
- ),
- Str("a".into()),
- Softbreak,
- Str("b".into()),
- End(Link("url".into(), LinkType::Span(SpanLinkType::Reference))),
- Softbreak,
- Start(
- Link("url".into(), LinkType::Span(SpanLinkType::Reference)),
- Attributes::new()
- ),
- Str("a".into()),
- Escape,
- Hardbreak,
- Str("b".into()),
- End(Link("url".into(), LinkType::Span(SpanLinkType::Reference))),
- End(Paragraph),
- End(Blockquote),
- Blankline,
- Start(LinkDefinition { label: "a b" }, Attributes::new()),
- Str("url".into()),
- End(LinkDefinition { label: "a b" }),
- );
- }
-
#[test]
fn link_definition_multiline() {
test_parse!(
diff --git a/tests/html-ref/ref.rs b/tests/html-ref/ref.rs
deleted file mode 100644
index 8b13789..0000000
--- a/tests/html-ref/ref.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/tests/html-ut/ut/footnotes.rs b/tests/html-ut/ut/footnotes.rs
deleted file mode 100644
index 19a1eb5..0000000
--- a/tests/html-ut/ut/footnotes.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-use crate::compare;
-
-// Footnote references may appear within a footnote.
-#[test]
-fn test_1c8325a() {
- let src = r##"[^a]
-
-[^a]: a[^b][^c]
-[^b]: b
-"##;
- let expected = r##"1
-
-"##;
- compare!(src, expected);
-}
-
-// Footnote references in unreferenced footnotes are ignored.
-#[test]
-fn test_9eab5c8() {
- let src = r##"para
-
-[^a]: a[^b][^c]
-[^b]: b
-"##;
- let expected = r##"para
-"##;
- compare!(src, expected);
-}
-
-// Footnotes may appear within footnotes.
-#[test]
-fn test_041f54c() {
- let src = r##"[^b]
-[^a]
-
-[^a]: [^b]: inner
-"##;
- let expected = r##"1
-2
-
-
-
--
-
inner↩︎︎
-
--
-
↩︎︎
-
-
-
-"##;
- compare!(src, expected);
-}
diff --git a/tests/html-ut/ut/lists.rs b/tests/html-ut/ut/lists.rs
deleted file mode 100644
index 6230f6a..0000000
--- a/tests/html-ut/ut/lists.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use crate::compare;
-
-#[test]
-fn test_fefa2dc() {
- let src = r##"1. item
-
-para
-"##;
- let expected = r##"
--
-item
-
-
-para
-"##;
- compare!(src, expected);
-}
-
-// Only single letter alphabetic list markers.
-#[test]
-fn test_2a0aa95() {
- let src = r##"word. Continuing paragraph.
-"##;
- let expected = r##"word. Continuing paragraph.
-"##;
- compare!(src, expected);
-}
diff --git a/tests/html-ut/ut/lists.test b/tests/html-ut/ut/lists.test
deleted file mode 100644
index 1f613b6..0000000
--- a/tests/html-ut/ut/lists.test
+++ /dev/null
@@ -1,20 +0,0 @@
-```
-1. item
-
-para
-.
-
--
-item
-
-
-para
-```
-
-Only single letter alphabetic list markers.
-
-```
-word. Continuing paragraph.
-.
-word. Continuing paragraph.
-```
diff --git a/tests/html-ut/ut/mod.rs b/tests/html-ut/ut/mod.rs
deleted file mode 100644
index 6fea8e3..0000000
--- a/tests/html-ut/ut/mod.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-mod footnotes;
-mod lists;
-mod raw_blocks;
diff --git a/tests/html-ut/ut/raw_blocks.rs b/tests/html-ut/ut/raw_blocks.rs
deleted file mode 100644
index 6f7c8ee..0000000
--- a/tests/html-ut/ut/raw_blocks.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use crate::compare;
-
-#[test]
-fn test_bf9dbab() {
- let src = r##"```=html
-
-
-```
-
-paragraph
-
-```=html
-
-
-```
-"##;
- let expected = r##"
-
-paragraph
-
-
-"##;
- compare!(src, expected);
-}
diff --git a/tests/html-ut/ut/raw_blocks.test b/tests/html-ut/ut/raw_blocks.test
deleted file mode 100644
index 0c6d97f..0000000
--- a/tests/html-ut/ut/raw_blocks.test
+++ /dev/null
@@ -1,19 +0,0 @@
-````
-```=html
-
-
-```
-
-paragraph
-
-```=html
-
-
-```
-.
-
-
-paragraph
-
-
-````