Skip to content

Commit

Permalink
feat: Parse attribute lists for blocks (#164)
Browse files Browse the repository at this point in the history
  • Loading branch information
scouten authored Nov 30, 2024
1 parent 0c78deb commit 0b8804a
Show file tree
Hide file tree
Showing 32 changed files with 1,653 additions and 350 deletions.
16 changes: 15 additions & 1 deletion src/blocks/block.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::slice::Iter;

use crate::{
attributes::Attrlist,
blocks::{
preamble::Preamble, CompoundDelimitedBlock, ContentModel, IsBlock, MacroBlock,
RawDelimitedBlock, SectionBlock, SimpleBlock,
Expand Down Expand Up @@ -62,7 +63,10 @@ impl<'src> Block<'src> {
// If it does contain any of those markers, we fall through to the more costly
// tests below which can more accurately classify the upcoming block.
if let Some(first_char) = source.chars().next() {
if !matches!(first_char, '.' | '#' | '=' | '/' | '-' | '+' | '*' | '_') {
if !matches!(
first_char,
'.' | '#' | '=' | '/' | '-' | '+' | '*' | '_' | '['
) {
let first_line = source.take_line();
if !first_line.item.contains("::") {
if let Some(MatchedItem {
Expand Down Expand Up @@ -248,6 +252,16 @@ impl<'src> IsBlock<'src> for Block<'src> {
Self::CompoundDelimited(b) => b.title(),
}
}

fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
match self {
Self::Simple(b) => b.attrlist(),
Self::Macro(b) => b.attrlist(),
Self::Section(b) => b.attrlist(),
Self::RawDelimited(b) => b.attrlist(),
Self::CompoundDelimited(b) => b.attrlist(),
}
}
}

impl<'src> HasSpan<'src> for Block<'src> {
Expand Down
7 changes: 7 additions & 0 deletions src/blocks/compound_delimited.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::slice::Iter;

use crate::{
attributes::Attrlist,
blocks::{parse_utils::parse_blocks_until, preamble::Preamble, Block, ContentModel, IsBlock},
span::MatchedItem,
strings::CowStr,
Expand All @@ -24,6 +25,7 @@ pub struct CompoundDelimitedBlock<'src> {
context: CowStr<'src>,
source: Span<'src>,
title: Option<Span<'src>>,
attrlist: Option<Attrlist<'src>>,
}

impl<'src> CompoundDelimitedBlock<'src> {
Expand Down Expand Up @@ -110,6 +112,7 @@ impl<'src> CompoundDelimitedBlock<'src> {
context: context.into(),
source,
title: preamble.title,
attrlist: preamble.attrlist.clone(),
},
after: closing_delimiter.after,
}),
Expand All @@ -134,6 +137,10 @@ impl<'src> IsBlock<'src> for CompoundDelimitedBlock<'src> {
fn title(&'src self) -> Option<Span<'src>> {
self.title
}

fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
self.attrlist.as_ref()
}
}

impl<'src> HasSpan<'src> for CompoundDelimitedBlock<'src> {
Expand Down
5 changes: 4 additions & 1 deletion src/blocks/is_block.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{fmt::Debug, slice::Iter};

use crate::{blocks::Block, strings::CowStr, HasSpan, Span};
use crate::{attributes::Attrlist, blocks::Block, strings::CowStr, HasSpan, Span};

/// Block elements form the main structure of an AsciiDoc document, starting
/// with the document itself.
Expand Down Expand Up @@ -45,6 +45,9 @@ pub trait IsBlock<'src>: HasSpan<'src> + Clone + Debug + Eq + PartialEq {

/// Returns the title for this block, if present.
fn title(&'src self) -> Option<Span<'src>>;

/// Returns the attribute list for this block, if present.
fn attrlist(&'src self) -> Option<&'src Attrlist<'src>>;
}

/// The content model of a block determines what kind of content the block can
Expand Down
11 changes: 11 additions & 0 deletions src/blocks/macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct MacroBlock<'src> {
macro_attrlist: Attrlist<'src>,
source: Span<'src>,
title: Option<Span<'src>>,
attrlist: Option<Attrlist<'src>>,
}

impl<'src> MacroBlock<'src> {
Expand Down Expand Up @@ -89,6 +90,7 @@ impl<'src> MacroBlock<'src> {
macro_attrlist: macro_attrlist.item.item,
source,
title: preamble.title,
attrlist: preamble.attrlist.clone(),
},

after: line.after.discard_empty_lines(),
Expand All @@ -111,6 +113,11 @@ impl<'src> MacroBlock<'src> {
///
/// IMPORTANT: This is the list of attributes _within_ the macro block
/// definition itself.
///
/// See also [`attrlist()`] for attributes that can be defined before the
/// macro invocation.
///
/// [`attrlist()`]: Self::attrlist()
pub fn macro_attrlist(&'src self) -> &'src Attrlist<'src> {
&self.macro_attrlist
}
Expand All @@ -133,6 +140,10 @@ impl<'src> IsBlock<'src> for MacroBlock<'src> {
fn title(&'src self) -> Option<Span<'src>> {
self.title
}

fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
self.attrlist.as_ref()
}
}

impl<'src> HasSpan<'src> for MacroBlock<'src> {
Expand Down
62 changes: 58 additions & 4 deletions src/blocks/preamble.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
attributes::Attrlist,
span::MatchedItem,
warnings::{MatchAndWarnings, Warning},
Span,
};
Expand All @@ -12,7 +13,6 @@ pub(crate) struct Preamble<'src> {
pub(crate) title: Option<Span<'src>>,

/// The block's attribute list, if any.
#[allow(dead_code)] // TEMPORARY while building
pub(crate) attrlist: Option<Attrlist<'src>>,

/// The source span as understood when the preamble content was first
Expand All @@ -33,7 +33,7 @@ impl<'src> Preamble<'src> {

/// Parse the title and attribute list for a block, if any.
pub(crate) fn parse(source: Span<'src>) -> MatchAndWarnings<'src, Self> {
let warnings: Vec<Warning<'src>> = vec![];
let mut warnings: Vec<Warning<'src>> = vec![];
let source = source.discard_empty_lines();

// Does this block have a title?
Expand All @@ -50,12 +50,29 @@ impl<'src> Preamble<'src> {
(None, source)
};

// TO DO: Does this block have an attribute list?
// Does this block have an attribute list?
let (attrlist, block_start) = if let Some(MatchAndWarnings {
item:
MatchedItem {
item: attrlist,
after: block_start,
},
warnings: mut attrlist_warnings,
}) = parse_maybe_attrlist_line(block_start)
{
if !attrlist_warnings.is_empty() {
warnings.append(&mut attrlist_warnings);
}

(Some(attrlist), block_start)
} else {
(None, block_start)
};

MatchAndWarnings {
item: Self {
title,
attrlist: None, // temporary
attrlist,
source,
block_start,
},
Expand All @@ -68,3 +85,40 @@ impl<'src> Preamble<'src> {
self.title.is_none() && self.attrlist.is_none()
}
}

fn parse_maybe_attrlist_line(
source: Span<'_>,
) -> Option<MatchAndWarnings<'_, MatchedItem<'_, Attrlist<'_>>>> {
let first_char = source.chars().next()?;
if first_char != '[' {
return None;
}

let MatchedItem {
item: line,
after: block_start,
} = source.take_normalized_line();

if !line.ends_with(']') {
return None;
}

// Drop opening and closing braces now that we know they are there.
let attrlist_src = line.slice(1..line.len() - 1);

let MatchAndWarnings {
item: MatchedItem {
item: attrlist,
after: _,
},
warnings,
} = Attrlist::parse(attrlist_src);

Some(MatchAndWarnings {
item: MatchedItem {
item: attrlist,
after: block_start,
},
warnings,
})
}
7 changes: 7 additions & 0 deletions src/blocks/raw_delimited.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::slice::Iter;

use crate::{
attributes::Attrlist,
blocks::{preamble::Preamble, ContentModel, IsBlock},
span::MatchedItem,
strings::CowStr,
Expand All @@ -26,6 +27,7 @@ pub struct RawDelimitedBlock<'src> {
context: CowStr<'src>,
source: Span<'src>,
title: Option<Span<'src>>,
attrlist: Option<Attrlist<'src>>,
}

impl<'src> RawDelimitedBlock<'src> {
Expand Down Expand Up @@ -90,6 +92,7 @@ impl<'src> RawDelimitedBlock<'src> {
context: context.into(),
source: preamble.source.trim_remainder(line.after),
title: preamble.title,
attrlist: preamble.attrlist.clone(),
},
after: line.after,
}),
Expand Down Expand Up @@ -134,6 +137,10 @@ impl<'src> IsBlock<'src> for RawDelimitedBlock<'src> {
fn title(&self) -> Option<Span<'src>> {
self.title
}

fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
self.attrlist.as_ref()
}
}

impl<'src> HasSpan<'src> for RawDelimitedBlock<'src> {
Expand Down
7 changes: 7 additions & 0 deletions src/blocks/section.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::slice::Iter;

use crate::{
attributes::Attrlist,
blocks::{parse_utils::parse_blocks_until, preamble::Preamble, Block, ContentModel, IsBlock},
span::MatchedItem,
strings::CowStr,
Expand All @@ -22,6 +23,7 @@ pub struct SectionBlock<'src> {
blocks: Vec<Block<'src>>,
source: Span<'src>,
title: Option<Span<'src>>,
attrlist: Option<Attrlist<'src>>,
}

impl<'src> SectionBlock<'src> {
Expand All @@ -45,6 +47,7 @@ impl<'src> SectionBlock<'src> {
blocks: blocks.item,
source,
title: preamble.title,
attrlist: preamble.attrlist.clone(),
},
after: blocks.after,
},
Expand Down Expand Up @@ -87,6 +90,10 @@ impl<'src> IsBlock<'src> for SectionBlock<'src> {
fn title(&'src self) -> Option<Span<'src>> {
self.title
}

fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
self.attrlist.as_ref()
}
}

impl<'src> HasSpan<'src> for SectionBlock<'src> {
Expand Down
8 changes: 8 additions & 0 deletions src/blocks/simple.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
attributes::Attrlist,
blocks::{preamble::Preamble, ContentModel, IsBlock},
inlines::Inline,
span::MatchedItem,
Expand All @@ -13,6 +14,7 @@ pub struct SimpleBlock<'src> {
inline: Inline<'src>,
source: Span<'src>,
title: Option<Span<'src>>,
attrlist: Option<Attrlist<'src>>,
}

impl<'src> SimpleBlock<'src> {
Expand All @@ -24,6 +26,7 @@ impl<'src> SimpleBlock<'src> {
inline: inline.item,
source: preamble.source.trim_remainder(inline.after),
title: preamble.title,
attrlist: preamble.attrlist.clone(),
},
after: inline.after.discard_empty_lines(),
})
Expand All @@ -38,6 +41,7 @@ impl<'src> SimpleBlock<'src> {
inline: inline.item,
source,
title: None,
attrlist: None,
},
after: inline.after.discard_empty_lines(),
})
Expand All @@ -61,6 +65,10 @@ impl<'src> IsBlock<'src> for SimpleBlock<'src> {
fn title(&self) -> Option<Span<'src>> {
self.title
}

fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
self.attrlist.as_ref()
}
}

impl<'src> HasSpan<'src> for SimpleBlock<'src> {
Expand Down
6 changes: 6 additions & 0 deletions src/document/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::slice::Iter;

use crate::{
attributes::Attrlist,
blocks::{parse_utils::parse_blocks_until, Block, ContentModel, IsBlock},
document::Header,
strings::CowStr,
Expand Down Expand Up @@ -100,6 +101,11 @@ impl<'src> IsBlock<'src> for Document<'src> {
// Document title is reflected in the Header.
None
}

fn attrlist(&'src self) -> Option<&'src Attrlist<'src>> {
// Document attributes are reflected in the Header.
None
}
}

impl<'src> HasSpan<'src> for Document<'src> {
Expand Down
Loading

0 comments on commit 0b8804a

Please sign in to comment.