Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,54 @@ impl<'s> TreeParser<'s> {
lines
};

// Check if this paragraph is actually a multi-line attribute
let (kind, span_start) = {
let mut kind = kind;
let mut span_start = span_start;
if matches!(kind, Kind::Paragraph)
&& lines.len() > 1
&& self.src[lines[0].clone()].starts_with('{')
{
let mut validator = attr::Validator::new();
let mut attr_end = 0;
let mut attr_line_count = 0;
for (i, line) in lines.iter().enumerate() {
let line_str = &self.src[line.clone()];
match validator.parse(line_str) {
Some(0) => break,
Some(len) => {
attr_end = line.start + len;
attr_line_count = i + 1;
break;
}
None => {}
}
}
if attr_end > 0 {
let remaining = &self.src[attr_end..lines.last().unwrap().end];
if remaining.trim().is_empty() {
// Unattached: entire paragraph is the attribute
kind = Kind::Atom(Atom::Attributes);
span_start = span_start.start..attr_end;
} else if attr_line_count < line_count {
// Attached: attribute followed by content, split the block
let attr_span = lines[0].start..attr_end;
let after_attr = &self.src[attr_end..lines[attr_line_count - 1].end];
if after_attr.trim().is_empty() {
self.events.push(Event {
kind: EventKind::Atom(Atom::Attributes),
span: attr_span,
});
self.attr_start =
self.attr_start.or_else(|| Some(self.events.len() - 1));
return attr_line_count;
}
}
}
}
(kind, span_start)
};

// close list if a non list item or a list item of new type appeared
if let Some(OpenList {
ty_start,
Expand Down
2 changes: 0 additions & 2 deletions tests/html-ut/skip
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
38d85f9:multi-line block attributes
6c14561:multi-line block attributes
2fa94d1:bugged left/right quote
e1f5b5e:untrimmed whitespace before linebreak
8423412:heading id conflict with existing id
Expand Down
10 changes: 10 additions & 0 deletions tests/html-ut/ut/attributes.test
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,13 @@ a{a="?a"}
a{a=?a}
<span a="?a">a</span></p>
```

Multi-line block attributes attach to the following block

```
{#id .class
style="color:red"}
A paragraph
.
<p id="id" class="class" style="color:red">A paragraph</p>
```

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test already exists in the the djot_attributes.test file (test_html_ut symlinks it from reference implementation) so no need to add it here.

46 changes: 46 additions & 0 deletions tests/parse_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,52 @@ fn attr_inline_consecutive_invalid() {
);
}

#[test]
fn attr_block_multiline_unattached() {
test_parse!(
concat!(
"{ .a }\n", //
),
(Attributes(attrs![(AttributeKind::Class, "a")]), "{ .a }\n"),
);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case is covered in attr_block_dangling above.

test_parse!(
concat!(
"{ .a\n", //
" }\n", //
),
(Attributes(attrs![(AttributeKind::Class, "a")]), "{ .a\n }"),
);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May also try attributes in nested quotes:

> {#id .class
>   style="color:red"}

This seems to crash now because we get the > as part of the attributes:

panicked at src/lib.rs:2646:30:
should be valid: 12

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May also try non-indented continuation. As per the syntax reference:

Block attributes have the same syntax as inline attributes, but if they don’t fit on one line, subsequent lines must be indented.

So

{.a
 }

should be parsed as attributes, while

{.a
}

should not. Right now they seem to parsed as attributes with or without indentation.

}

#[test]
fn attr_block_multiline_attached() {
test_parse!(
concat!(
"{#id .class\n", //
" style=\"color:red\"}\n", //
"A paragraph\n", //
),
(
Start(
Paragraph,
attrs![
(AttributeKind::Id, "id"),
(AttributeKind::Class, "class"),
(
AttributeKind::Pair {
key: "style".into()
},
"color:red"
),
]
),
"{#id .class\n style=\"color:red\"}\n",
),
(Str("A paragraph".into()), "A paragraph"),
(End(Paragraph), ""),
);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also work with nested blocks:

> {#id .class
>  style="color:red"}
> A paragraph

but also crashes atm.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also not allow non indented continuation, i.e.

{#id .class
style="color:red"}
A paragraph

should not be parsed as attributes.

}

#[test]
fn attr_inline_multiline() {
test_parse!(
Expand Down
Loading