From febdb6c07f8e6887350dc0282ea153a6817fa9ae Mon Sep 17 00:00:00 2001 From: Mathias Kende Date: Fri, 17 May 2024 22:07:54 +0200 Subject: [PATCH] Add a (first) hook, to resolve otherwise unresolved link reference. --- .aspelldict | 6 ++++- Changes | 5 ++++ lib/Markdown/Perl.pm | 48 ++++++++++++++++++++++++++++++++-- lib/Markdown/Perl/Inlines.pm | 7 ++++- t/500-hooks-resolve_link_ref.t | 38 +++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 t/500-hooks-resolve_link_ref.t diff --git a/.aspelldict b/.aspelldict index b844731..43794ec 100644 --- a/.aspelldict +++ b/.aspelldict @@ -1,4 +1,5 @@ -personal_ws-1.1 en 180 +personal_ws-1.1 en 184 +Babelmark BlockTree CDATA CommonMark @@ -32,6 +33,7 @@ ProhibitLvalueSubstr ProhibitMultiplePackages ProhibitStringyEval ProhibitUnusedCapture +README RequireArgUnpacking RequireConstantVersion TODO @@ -61,6 +63,7 @@ codepoints colgroup commonmark cond +cpanm del delim dest @@ -89,6 +92,7 @@ gfm github gx gxc +hashref hd helpfull helpoptions diff --git a/Changes b/Changes index 77583fd..8516e39 100644 --- a/Changes +++ b/Changes @@ -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] diff --git a/lib/Markdown/Perl.pm b/lib/Markdown/Perl.pm index d078030..5a95523 100644 --- a/lib/Markdown/Perl.pm +++ b/lib/Markdown/Perl.pm @@ -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 { @@ -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}); @@ -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(); @@ -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 +as the value associated with a hook. + +There is currently a single hook: + +=over 4 + +=item * + +C: 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 or a +hash-reference containing a C key, pointing to the link destination and +optionally a C key containing the title of the key. + +=back + =head1 AUTHOR Mathias Kende <mathias@cpan.org> diff --git a/lib/Markdown/Perl/Inlines.pm b/lib/Markdown/Perl/Inlines.pm index 82d4116..a419a18 100644 --- a/lib/Markdown/Perl/Inlines.pm +++ b/lib/Markdown/Perl/Inlines.pm @@ -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 diff --git a/t/500-hooks-resolve_link_ref.t b/t/500-hooks-resolve_link_ref.t new file mode 100644 index 0000000..96c2cc8 --- /dev/null +++ b/t/500-hooks-resolve_link_ref.t @@ -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;