package Pod::Markdown::Githubert;
use strict;
use warnings;

use Pod::Markdown ();
our @ISA = 'Pod::Markdown';

our $VERSION = '0.05';

sub new {
    my $class = shift;
    my %args = @_;

    my $hl_language = '';
    if (exists $args{hl_language}) {
        $hl_language = delete $args{hl_language};
    }

    my $self = $class->SUPER::new(
        markdown_fragment_format => sub {
            my ($self, $str) = @_;
            $str =~ tr/A-Za-z0-9_\- //cd;
            $str =~ tr/A-Z /a-z-/;
            $str
        },
        html_encode_chars => '\$',
        %args
    );
    $self->accept_targets('highlighter', 'github-markdown');
    $self->hl_language($hl_language);
    $self
}

sub hl_language {
    my $self = shift;
    if (@_) {
        $self->{+__PACKAGE__}{hl_language} = $_[0];
    }
    $self->{+__PACKAGE__}{hl_language}
}

sub format_perldoc_url {
    my $self = shift;
    my ($name, $section) = @_;
    my $prev_url_prefix;
    if (defined $name && $name =~ /\Aperl[a-z0-9]*\z/) {
        $prev_url_prefix = $self->perldoc_url_prefix;
        $self->perldoc_url_prefix('https://perldoc.perl.org/');
    }
    my $url = $self->SUPER::format_perldoc_url($name, $section);
    $self->perldoc_url_prefix($prev_url_prefix) if defined $prev_url_prefix;
    $url
}

sub start_for {
    my $self = shift;
    my ($attr) = @_;
    if ($attr->{target} eq 'highlighter') {
        $self->_new_stack;
        $self->_stack_state->{for_highlighter} = 1;
        return;
    }
    $self->SUPER::start_for(@_)
}

sub end_for {
    my $self = shift;
    my ($attr) = @_;
    if ($self->_stack_state->{for_highlighter}) {
        my $text = $self->_pop_stack_text;
        my %settings =
            map /\A([^=]*)=(.*)\z/s
                ? ($1 => $2)
                : (language => $_),
            split ' ', $text;
        $self->{+__PACKAGE__}{hl_language} = $settings{language} // '';
        return;
    }
    $self->SUPER::end_for(@_)
}

sub _indent_verbatim {
    my $self = shift;
    my ($paragraph) = @_;
    my $min_indent = 'inf';
    while ($paragraph =~ /^( +)/mg) {
        my $n = length $1;
        $min_indent = $n if $n < $min_indent;
    }
    my $rep =
        $min_indent < 'inf'
            ? "{$min_indent}"
            : '+';
    $paragraph =~ s/^ $rep//mg;
    my $fence = '```';
    while ($paragraph =~ /^ *\Q$fence\E *$/m) {
        $fence .= '`';
    }
    my $hl_language = $self->{+__PACKAGE__}{hl_language};
    if ($hl_language !~ /\A[^`\s]\S*\z/) {
        $hl_language = '';
    }
    "$fence$hl_language\n$paragraph\n$fence"
}

sub end_item_number {
    my $self = shift;
    if ($self->_last_string =~ /\S/) {
        return $self->SUPER::end_item_number(@_);
    }
    $self->_end_item($self->_private->{item_number} . '. <!-- -->');
}

sub start_I {
    my $self = shift;
    $self->_save('*');
}

1
__END__

=encoding utf8

=head1 NAME

Pod::Markdown::Githubert - convert POD to Github-flavored Markdown

=head1 SYNOPSIS

=for highlighter language=perl

    use Pod::Markdown::Githubert ();

    my $parser = Pod::Markdown::Githubert->new;
    $parser->output_string(\my $markdown);
    $parser->parse_string_document($pod_string);

    # see Pod::Markdown docs for the full API

=head1 DESCRIPTION

Pod::Markdown::Githubert is a module for converting documents in POD format
(see L<perlpod>) to Github-flavored Markdown. It is a subclass of
L<Pod::Markdown> (which see for API documentation) that adds the following
Github-specific enhancements and fixes:

=over

=item *

Internal links (of the form C<LE<lt>/fooE<gt>>) are converted to something that
hopefully matches how Github generates HTML ids for Markdown headings. In
short, internal links to a section of the current page should just work when
rendered on Github.

=item *

Github-specific Markdown code can be embedded literally using a
C<=for github-markdown> paragraph or
C<=begin github-markdown ... =end github-markdown> section.

In other words, if you want to render e.g. a badge, but only on Github, not all
Markdown renderers, put it in a C<=for github-markdown> paragraph.

=item *

External links to module documentation normally point to
L<https://metacpan.org/>. But that doesn't work for some of the manual pages
included with Perl because they are only generated when perl is built (such as
L<perlapi>), so this module redirects all C<perlXYZ> links to
L<https://perldoc.perl.org/>, which has the full set.

=item *

Verbatim paragraphs are translated to fenced code blocks (surrounded by
C<```>) with normalized indentation (meaning it doesn't matter whether the
paragraph is indented by 1 space, 4 spaces, or 23 spaces in the POD source; it
will generated the same Markdown).

=item *

Code blocks containing C<```> are rendered correctly, as are code blocks in
nested structures (such as list items) even when a numbered list item starts
with a code block.

=item *

Syntax highlighting can be enabled by tagging each code block with its
language. As this module translates a POD document, it keeps a global "current
language" setting, which is applied to every verbatim paragraph. Initially the
"current language" is empty, which just produces ordinary C<```> code blocks,
but this is configurable using the L</hl_language> method; see below.

A C<=for highlighter language=FOO> paragraph sets the "current language" to
I<FOO>. (More specifically, you can put multiple I<KEY=VALUE> options in a
C<=for highlighter> paragraph, but this module only looks at the C<language>
option.) If you only want to set the "current language" to I<FOO>, you can also
write C<=for highlighter FOO> (that is, C<language> is the default option).

The "current language" is applied to all following verbatim paragraphs and
produces C<```FOO> tagged code blocks:

=for highlighter language=pod

    =for highlighter language=perl

        my $dog = "spot";

    ... other stuff ...

        my $car = "cdr";

    =for highlighter language=html

        <p>Hello!</p>

produces the following Markdown code:

=for highlighter language=markdown

    ```perl
    my $dog = "spot";
    ```

    ... other stuff ...

    ```perl
    my $car = "cdr";
    ```

    ```html
    <p>Hello!</p>
    ```

=item *

C<$> characters are escaped in plain text segments because otherwise Github
will try to render any random text that happens to sit between two dollar signs
as TeX-style math.

=item *

Text in italics is rendered using C<*>, not C<_>. This is because Github
displays C<_N_th> verbatim as "_N_th", but C<*N*th> as "I<N>th". The latter is
what we want for POD like C<IE<lt>NE<gt>th>.

=back

=head1 METHODS

=head2 new

Constructor. Takes a list of key-value pairs (like a hash initializer) to
configure the created object. In addition to the constructor arguments
inherited from L<Pod::Markdown>, this module also supports:

=over

=item C<< hl_language => $lang >>

Sets the default (initial) language for syntax highlighting. Equivalent to
calling the L</hl_language> method right after C<new>.

=back

=head2 hl_language

Gets (with no arguments) or sets (with one argument) the current language for
syntax highlighting. You can use this to set the initial language for code
blocks that have no preceding C<=for highlighter language=...> paragraph:

    my $p = Pod::Markdown::Githubert->new;
    $p->hl_language('perl');
    # or alternatively:
    my $p = Pod::Markdown::Githubert->new(hl_language => 'perl');
    ...

Now the next document will be processed by C<$p> as if it had started with
C<=for highlighter language=perl>.

=begin :README

=head1 INSTALLATION

To download and install this module, use your favorite CPAN client, e.g.
L<C<cpan>|cpan>:

=for highlighter language=sh

    cpan Pod::Markdown::Githubert

Or L<C<cpanm>|cpanm>:

    cpanm Pod::Markdown::Githubert

To do it manually, run the following commands (after downloading and unpacking
the tarball):

    perl Makefile.PL
    make
    make test
    make install

=end :README

=head1 SEE ALSO

L<Pod::Markdown>, L<perlpod>

=head1 AUTHOR

Lukas Mai, C<< <lmai at web.de> >>

=head1 COPYRIGHT & LICENSE

Copyright 2023-2024 Lukas Mai.

This module is free software; you can redistribute it and/or modify it under
the terms of either: the GNU General Public License as published by the Free
Software Foundation; or the Artistic License.

See L<https://dev.perl.org/licenses/> for more information.

=cut
