App-Greple-md

 view release on metacpan or  search on metacpan

lib/App/Greple/md.pm  view on Meta::CPAN

    return $_[1] unless $config->{osc8};
    my($url, $text) = @_;
    my $escaped = uri_escape_utf8($url, "^\\x20-\\x7e");
    "${OS}${escaped}${OE}${text}${OS}${OE}";
}

#
# Link text inner pattern: backtick spans, backslash escapes, normal chars
#

my $LT = qr/(?:`[^`\n]*+`|\\.|[^`\\\n\]]++)+/;

#
# colorize() - the main function
#
# Receives entire file content in $_ (--begin with -G --filter).
# Processes all patterns with multiline regexes.
#

#
# Pipeline steps as code refs
#

my %colorize = (
    code_blocks => sub {
        s{^( {0,3})(`{3,}|~{3,})(.*)\n((?s:.*?))^( {0,3})\2(\h*)$}{
            my($oi, $fence, $lang, $body, $ci, $trail) = ($1, $2, $3, $4, $5, $6);
            my $result = md_color('code_mark', "$oi$fence");
            $result .= md_color('code_info', $lang) if length($lang);
            $result .= "\n";
            if (length($body)) {
                $result .= join '', map { md_color('code_block', $_) }
                    split /(?<=\n)/, $body;
            }
            $result .= md_color('code_mark', "$ci$fence") . $trail;
            protect($result)
        }mge;
    },
    comments => sub {
        s/(^<!--(?![->])(?s:.*?)-->)/protect(md_color('comment', $1))/mge;
    },
    image_links => sub {
        s{\[!\[($LT)\]\(([^)\n]+)\)\]\(<?([^>)\s\n]+)>?\)}{
            protect(
                osc8($2, md_color('image_link', "!"))
                . osc8($3, md_color('image_link', "[$1]"))
            )
        }ge;
    },
    images => sub {
        s{!\[($LT)\]\(<?([^>)\s\n]+)>?\)}{
            protect(osc8($2, md_color('image', "![$1]")))
        }ge;
    },
    links => sub {
        s{(?<![!\e])\[($LT)\]\(<?([^>)\s\n]+)>?\)}{
            protect(osc8($2, md_color('link', "[$1]")))
        }ge;
    },
    inline_code => sub {
        s/(?<bt>`++)(((?!\g{bt}).)+)(\g{bt})/
            protect(md_color('code_tick', $+{bt}) . md_color('code_inline', $2) . md_color('code_tick', $4))
        /ge;
    },
    headings => sub {
        my $hashed = $config->{hashed};
        for my $n (reverse 1..6) {
            next unless active("h$n");
            my $hdr = '#' x $n;
            s{^($hdr\h+.*)$}{
                my $line = $1;
                $line .= " $hdr"
                    if $hashed->{"h$n"} && $line !~ /\#$/;
                protect(md_color("h$n", restore($line)));
            }mge;
        }
    },
    horizontal_rules => sub {
        s/^([ ]{0,3}(?:[-*_][ ]*){3,})$/protect(md_color('horizontal_rule', $1))/mge;
    },
    bold => sub {
        s/(?<![\\`])\*\*.*?(?<!\\)\*\*/md_color('bold', $&)/ge;
        s/(?<![\\`\w])__.*?(?<!\\)__(?!\w)/md_color('bold', $&)/ge;
    },
    italic => sub {
        s/(?<![\\`\w])_(?:(?!_).)+(?<!\\)_(?!\w)/md_color('italic', $&)/ge;
        s/(?<![\\`\*])\*(?:(?!\*).)+(?<!\\)\*(?!\*)/md_color('italic', $&)/ge;
    },
    strike => sub {
        s/(?<![\\`])~~.+?(?<!\\)~~/md_color('strike', $&)/ge;
    },
    blockquotes => sub {
        s/^(>+\h?)(.*)$/md_color('blockquote', $1) . $2/mge;
    },
);

#
# Pipeline configuration
#

# Always before headings (protection + links)
my @protect_steps = qw(code_blocks comments image_links images links);

# Inline steps controlled by heading_markup
my @inline_steps  = qw(inline_code horizontal_rules bold italic strike);

# Always last
my @final_steps   = qw(blockquotes);

# Step-to-label mapping for active() check (unmapped = always active)
my %step_label = (
    headings         => 'header',
    horizontal_rules => 'horizontal_rule',
    bold             => 'bold',
    italic           => 'italic',
    strike           => 'strike',
    blockquotes      => 'blockquote',
);

sub build_pipeline {
    my $hm = $config->{heading_markup};

lib/App/Greple/md.pm  view on Meta::CPAN

    for my $step (build_pipeline()) {
        my $label = $step_label{$step};
        next if $label && !active($label);
        $colorize{$step}->();
    }

    $_ = restore($_);
    $_;
}

#
# Table formatting
#

sub begin {
    colorize()    if $config->{colorize};
    format_table() if $config->{table};
}

sub format_table {
    my $sep = $config->{rule} ? "\x{2502}" : '|';  # │ or |

    s{(^ {0,3}\|.+\|\n){3,}}{
        my $block = $&;
        my $formatted = call_ansicolumn($block,
            '-s', '|', '-o', $sep, '-t', '--cu=1');
        fix_separator($formatted, $sep);
    }mge;
}

sub call_ansicolumn {
    my ($text, @args) = @_;
    require Command::Run;
    require App::ansicolumn;
    Command::Run->new
        ->command(\&App::ansicolumn::ansicolumn, @args)
        ->with(stdin => $text)
        ->update
        ->data // '';
}

sub fix_separator {
    my ($text, $sep) = @_;
    my $sep_re = $sep eq "\x{2502}" ? "\x{2502}" : '\\|';
    $text =~ s{^$sep_re((?:\h* -+ \h* $sep_re)*\h* -+ \h*)$sep_re$}{
        $sep eq "\x{2502}"
        ? "\x{251C}" . ($1 =~ tr[\x{2502} -][\x{253C}\x{2500}\x{2500}]r) . "\x{2524}"
        : "|" . ($1 =~ tr[ ][-]r) . "|"
    }xmeg;
    $text;
}

1;

__DATA__

option default \
    -G --filter --filestyle=once --color=always \
    --begin &__PACKAGE__::begin

define {CODE_BLOCK}  ^ {0,3}(?<bt>`{3,}+|~{3,}+)(.*)\n((?s:.*?))^ {0,3}(\g{bt})
define {COMMENT}     ^<!--(?![->])(?s:.+?)-->
define {TABLE}       ^ {0,3}([│|├].+[│|┤]\n){3,}
define {LIST_ITEM}   ^\h*(?:[*-]|(?:\d+|#)[.)])\h+.*\n
define {DEFINITION}  (?:\A|\G\n|\n\n).+\n\n?(:\h+.*\n)

option --fold-by \
    -Mtee "&ansifold" --crmode \
        --autoindent='^\h*(?:[*-]|(?:\d+|#)[.)]|:)\h+|^\h+' \
        --smart --width=$<shift> \
    -- \
    --exclude {CODE_BLOCK} \
    --exclude {COMMENT} \
    --exclude {TABLE} \
    --cm N -E {LIST_ITEM} \
    --cm N -E {DEFINITION} \
    --crmode



( run in 1.713 second using v1.01-cache-2.11-cpan-97f6503c9c8 )