Skip to content

Commit

Permalink
Process a YAML table when it is immediately at the beginning of the f…
Browse files Browse the repository at this point in the history
…ile.

This can provide metadata for the file.
  • Loading branch information
mkende committed Apr 10, 2024
1 parent 07baae7 commit 982fd07
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 1 deletion.
24 changes: 24 additions & 0 deletions Syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,30 @@ with list label,
supported by default. They can be activated with the `use_setext_headings`
option.

### File metadata

The Markdown text can start with a YAML table, at the very beginning of the
content, to specify metadata for the file. This table must start with a line
containing only `---` and ends with a line containing only `---` or `...`. The
YAML content must not be empty and must not contain any blank line.

The YAML content is not part of the rendered HTML, but can be extracted by other
processing systems.

```md
---
foo: bar
baz: bin
...
baz
```

Is rendered as:

```html
<p>baz</p>
```

### Thematic breaks

A thematic break (usually rendered as a horizontal line through the page) can
Expand Down
1 change: 1 addition & 0 deletions cpanfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ requires 'List::Util', '1.45';
requires 'List::MoreUtils';
requires 'Readonly';
requires 'Unicode::CaseFold';
requires 'YAML::Tiny';

on 'test' => sub {
requires 'JSON';
Expand Down
19 changes: 19 additions & 0 deletions lib/Markdown/Perl/BlockParser.pm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use List::MoreUtils 'first_index';
use List::Util 'pairs', 'min';
use Markdown::Perl::HTML 'html_escape', 'decode_entities', 'remove_disallowed_tags';
use Markdown::Perl::Util ':all';
use YAML::Tiny;

our $VERSION = '0.01';

Expand Down Expand Up @@ -136,6 +137,9 @@ sub process {
# Done at a later stage, as escaped characters don’t have their Markdown
# meaning, we need a way to represent that.

# Note: for now, nothing is done with the extracted metadata.
$this->_parse_yaml_metadata() if $this->get_parse_file_metadata eq 'yaml';

while (defined (my $l = $this->next_line())) {
# This field might be set to true at the beginning of the processing, while
# we’re looking at the conditions of the currently open containers.
Expand Down Expand Up @@ -360,6 +364,21 @@ sub _parse_blocks { ## no critic (RequireArgUnpacking)
return;
}

sub _parse_yaml_metadata {
my ($this) = @_;

# At this point, pos(md) is guaranteed to be 0.
if ($this->{md} =~ m/^---\n((?:.+\n)+?)(:?---|\.\.\.)\n/gc) {
my $yaml = eval { YAML::Tiny->read_string($1) };
if ($@) {
pos($this->{md}) = 0;
return;
}
}

return;
}

# https://spec.commonmark.org/0.30/#atx-headings
sub _do_atx_heading {
my ($this) = @_;
Expand Down
36 changes: 35 additions & 1 deletion lib/Markdown/Perl/Options.pm
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,43 @@ sub _word_list {
=head2 Options controlling which top-level blocks are used
=head3 B<parse_file_metadata> I<(enum, default: yaml)>
This option controls whether the parser accepts optional metadata at the
beginning of the file. Currently, it does nothing with these metadata, even when
they are accepted. In the future they might be used to build complete HTML file
instead of just fragment.
The possible values are:
=over 4
=item B<none>
No file metadata is accepted, any structure in the document must be in Markdown.
=item B<yaml> I<(default)>
The parser accepts a YAML table as the very beginning of the document. This
table must start with a line containing just C<---> and ends with another line
containing C<---> or C<...>. The content between these two markers must be valid
a valid, non-empty YAML sequence and it cannot contain any empty line.
=back
=cut

_make_option(
parse_file_metadata => 'yaml',
_enum(qw(none yaml)),
cmark => 'none',
github => 'none',
markdown => 'none',
);

=head3 B<use_fenced_code_blocks> I<(boolean, default: true)>
This options controls whether fenced code blocks are recognised in the document
This option controls whether fenced code blocks are recognised in the document
structure.
=cut
Expand Down
15 changes: 15 additions & 0 deletions t/304-file_metadata.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use strict;
use warnings;
use utf8;

use Markdown::Perl;
use Test2::V0;

my $yaml = Markdown::Perl->new(parse_file_metadata => 'yaml');

is($yaml->convert("---\nfoo: bar\nbaz: bin\n---\ndum\n"), "<p>dum</p>\n", 'yaml_table');
is($yaml->convert("---\nfoo: bar\nbaz: bin\n...\ndum\n"), "<p>dum</p>\n", 'yaml_table_with_dot');
is($yaml->convert("---\nfoo: bar\n\n...\ndum\n"), "<hr />\n<p>foo: bar</p>\n<p>...\ndum</p>\n", 'yaml_with_empty_line');
is($yaml->convert("---\n+ foo\n...\ndum\n"), "<hr />\n<ul>\n<li>foo\n...\ndum</li>\n</ul>\n", 'not_yaml');

done_testing;

0 comments on commit 982fd07

Please sign in to comment.