Skip to content

Commit

Permalink
Add a (first) hook, to resolve otherwise unresolved link reference.
Browse files Browse the repository at this point in the history
  • Loading branch information
mkende committed May 17, 2024
1 parent 156f8a7 commit febdb6c
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 4 deletions.
6 changes: 5 additions & 1 deletion .aspelldict
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
personal_ws-1.1 en 180
personal_ws-1.1 en 184
Babelmark
BlockTree
CDATA
CommonMark
Expand Down Expand Up @@ -32,6 +33,7 @@ ProhibitLvalueSubstr
ProhibitMultiplePackages
ProhibitStringyEval
ProhibitUnusedCapture
README
RequireArgUnpacking
RequireConstantVersion
TODO
Expand Down Expand Up @@ -61,6 +63,7 @@ codepoints
colgroup
commonmark
cond
cpanm
del
delim
dest
Expand Down Expand Up @@ -89,6 +92,7 @@ gfm
github
gx
gxc
hashref
hd
helpfull
helpoptions
Expand Down
5 changes: 5 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
Revision history for pmarkdown and the Markdown::Perl module.

1.06 - ??

- Add support for setting processing hooks.
- Add a hook to resolve unresolved link reference.

1.05 - 2024-05-14

[Improve the inline_delimiters option]
Expand Down
48 changes: 46 additions & 2 deletions lib/Markdown/Perl.pm
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ use Carp;
use English;
use Exporter 'import';
use Hash::Util 'lock_keys';
use List::Util 'none';
use List::MoreUtils 'pairwise';
use Markdown::Perl::BlockParser;
use Markdown::Perl::Inlines;
use Markdown::Perl::HTML 'html_escape', 'decode_entities';
use Readonly;
use Scalar::Util 'blessed';

use parent 'Markdown::Perl::Options';

our $VERSION = '1.05'; # Remember to also set the App::pmarkdown version.

our @EXPORT_OK = qw(convert set_options);
our @EXPORT_OK = qw(convert set_options set_mode set_hooks);
our %EXPORT_TAGS = (all => \@EXPORT_OK);

sub new {
Expand All @@ -28,7 +30,8 @@ sub new {
my $this = $class->SUPER::new(
mode => undef,
options => {},
local_options => {});
local_options => {},
hooks => {});
$this->SUPER::set_options(options => @options);
lock_keys(%{$this});

Expand All @@ -47,6 +50,24 @@ sub set_mode {
return;
}

Readonly::Array my @VALID_HOOKS => qw(resolve_link_ref);

sub set_hooks {
my ($this, %hooks) = &_get_this_and_args; ## no critic (ProhibitAmpersandSigils)
while (my ($k, $v) = each %hooks) {
if (none { $_ eq $k } @VALID_HOOKS) {
croak "Invalid hook name: ${k}";
} elsif (!defined $v) {
delete $this->{hooks}{$k};
} elsif (ref $v ne 'CODE') {
carp 'Hook target must be a CODE reference';
} else {
$this->{hooks}{$k} = $v;
}
}
return;
}

# Returns @_, unless the first argument is not blessed as a Markdown::Perl
# object, in which case it returns a default object.
my $default_this = Markdown::Perl->new();
Expand Down Expand Up @@ -280,6 +301,29 @@ Converts the given $md string into HTML. The input string must be a decoded
Unicode string (or an ASCII string) and the output is similarly a decoded
Unicode string.
=head2 set_hooks
$pmarkdown->set_hooks(hook_name => sub { ... }, ...);
Markdown::Perl::set_hooks(hook_name => sub { ... }, ...);
Registers hooks to be called during the processing of a Markdown document. Each
hook must point to a code reference. You can remove a hook by passing C<undef>
as the value associated with a hook.
There is currently a single hook:
=over 4
=item *
C<resolve_link_ref>: this hook will receive a single string an argument,
containing the label of a link reference in case where there was no matching
link definition in the document and it must returns either C<undef> or a
hash-reference containing a C<target> key, pointing to the link destination and
optionally a C<title> key containing the title of the key.
=back
=head1 AUTHOR
Mathias Kende <[email protected]>
Expand Down
7 changes: 6 additions & 1 deletion lib/Markdown/Perl/Inlines.pm
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,12 @@ sub parse_reference_link {
# Returns a hashref with (title and dest) or undef.
sub get_linkref {
my ($that, $linkrefs, $ref) = @_;
return $linkrefs->{$ref};
if (exists $linkrefs->{$ref}) {
return $linkrefs->{$ref};
} elsif (exists $that->{hooks}{resolve_link_ref}) {
return $that->{hooks}{resolve_link_ref}->($ref);
}
return;
}

# This methods remove line break at the beginning and end of lines (inside text
Expand Down
38 changes: 38 additions & 0 deletions t/500-hooks-resolve_link_ref.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use strict;
use warnings;
use utf8;

use Markdown::Perl 'convert', 'set_hooks';
use Test2::V0;

sub hook {
my ($ref) = @_;
if ($ref eq 'foo') {
return { target => 'http://foo' };
} elsif ($ref eq 'bar') {
return { target => 'http://bar', title => 'BAR' };
}
return;
}

set_hooks(resolve_link_ref => \&hook);
is(convert("[text][foo]"), "<p><a href=\"http://foo\">text</a></p>\n", 'resolved in full link reference');
is(convert("[foo][]"), "<p><a href=\"http://foo\">foo</a></p>\n", 'resolved in collapsed link reference');
is(convert("[foo]"), "<p><a href=\"http://foo\">foo</a></p>\n", 'resolved in shortcut link reference');

is(convert("[bar]"), "<p><a href=\"http://bar\" title=\"BAR\">bar</a></p>\n", 'resolved with title');
is(convert("[none]"), "<p>[none]</p>\n", 'not resolved');

is(convert("[foo]\n\n[foo]: http://other"), "<p><a href=\"http://other\">foo</a></p>\n", 'source has precedence');

{
my $p = Markdown::Perl->new();
is($p->convert("[foo]"), "<p>[foo]</p>\n", 'no default hook');
$p->set_hooks(resolve_link_ref => \&hook);
is($p->convert("[foo]"), "<p><a href=\"http://foo\">foo</a></p>\n", 'set_hooks object-oriented');
}

set_hooks(resolve_link_ref => undef);
is(convert("[foo]"), "<p>[foo]</p>\n", 'hook can be removed');

done_testing;

0 comments on commit febdb6c

Please sign in to comment.