App-mdee
view release on metacpan or search on metacpan
```
The `code_block` label includes `;E` (erase line) for full-width background on fenced code blocks. `code_tick` has background color matching `code_inline` for visual continuity, with dimmer foreground. `code_inline` has explicit foreground (`L00`/`L2...
Inline code backticks are displayed as `` `content´ `` using `code_tick` color for the markers. Multi-backtick delimiters (``` `` ```, ```` ``` ````, etc.) are collapsed to a single pair, with optional surrounding spaces stripped per CommonMark. The...
Regex patterns used by the md module:
Fenced code blocks ([CommonMark](https://spec.commonmark.org/0.31.2/#fenced-code-blocks)):
```
^ {0,3}(?<bt>(?<fc>[`~])\g{fc}{2,}+)(.*)\n((?s:.*?))^ {0,3}\g{bt}\g{fc}*+\h*$
```
The closing fence is the same character as the opening one and at least as long, followed only by spaces or tabs; it is shown as written. The `code_blocks` step uses the same rule with numbered captures. A fence that is never closed is not treated ...
ATX headings ([CommonMark](https://spec.commonmark.org/0.31.2/#atx-headings)), for each level `$hdr` from `######` down to `#`: `^( {0,3}$hdr(?:\h+.*)?)$`. Up to 3 spaces of indentation are allowed, and a line of `#`s alone is an empty heading; `#fo...
Inline code ([CommonMark Code Spans](https://spec.commonmark.org/0.31.2/#code-spans)) uses the `$CODE` pattern, which matches both single and multi-backtick spans. For multi-backtick (2+), optional leading/trailing spaces are stripped per CommonMark ...
### Front Matter
for name in "${!show[@]}"; do
[[ $name == all ]] && continue
md_opts+=(--show "${name}=${show[$name]}")
done
```
The md module's `active()` function checks show flags and skips regex substitutions entirely for disabled fields.
### Emphasis Patterns (CommonMark)
Bold/italic/strike patterns use `$SKIP_CODE` as first alternative to skip code spans. Named captures `(?<m>...)` (markers) and `(?<t>...)` (content) with `\g{m}` backreference prevent mixing (`**...__`).
- `(?<![\\\w])` / `(?!\w)`: Word boundaries for `_`/`__` (prevents `foo_bar_baz`)
- `(?<!\*)` / `(?!\*)`: Distinguishes `*italic*` from `**bold**`
- `flanking($delim)`: returns the opener and closer patterns of `*` or `**`, left-flanking and right-flanking as in CommonMark (so `L*=50, a*=68` is not italic, while `2*3*4` is), or by the markdown-cjk-friendly rules with `cjk_friendly=1`; see "Flan...
- `mark_color($type, $text)`: Uses `${type}_mark` if in colormap, falls back to `emphasis_mark`
- `/p` flag with `${^MATCH}`: Safe alternative to `$&` (used by `$SKIP_CODE`)
### OSC 8 Hyperlinks
Links are converted to OSC 8 terminal hyperlinks. Three types (in processing order): `image_link` (`[](url)`), `image` (``), `link` (`[text](url)`). Each is colored, wrapped in OSC 8, and protected. Disable with `config(osc8=0)`...
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
$s =~ s/\x00(\d+)\x00/$protected[$1]/g;
$s;
}
sub colorize {
@protected = ();
# ... code block handling (return early) ...
# Inline code: protect from further processing
s/((`++)(?:(?!\g{-2}).)+\g{-2})/protect(main::color('inline_code', $1))/ge;
# HTML comments: protect from further processing
s/(^<!--(?![->]).*?-->)/protect(main::color('comment', $1))/gme;
# (links, headings, emphasis in later tasks)
$_ = restore($_);
$_;
}
```
lib/App/Greple/md.pm view on Meta::CPAN
#
my $LT = qr/(?:`[^`\n]*+`|\\.|[^`\\\n\]]++)+/;
# Code span pattern (both single and multi-backtick).
# Captures: _bt (backtick delimiter), _content (code body).
# Used directly in inline_code step and as basis for $SKIP_CODE.
my $CODE = qr{(?x)
(?<_bt> `++ ) # opening backtick(s)
(?<_content>
(?: (?! \g{_bt} ) . )+? # content (not containing same-length backticks)
)
\g{_bt} # closing backtick(s) matching opener
};
# Skip code spans in link/image patterns.
# Used as the first alternative in s{$SKIP_CODE|<link pattern>}{...}ge
# so that code spans are matched and skipped, preventing link/image
# patterns from matching inside them.
my $SKIP_CODE = qr{$CODE (*SKIP)(*FAIL)}x;
# Delimiters of * and ** emphasis (CommonMark 0.31.2, 6.2): an opener
# must be left-flanking and a closer right-flanking, so that
lib/App/Greple/md.pm view on Meta::CPAN
protect(md_color("h$n", restore($line)));
}mge;
}
}),
horizontal_rules => Step(horizontal_rule => sub {
s/^([ ]{0,3}(?:[-*_][ ]*){3,})$/protect(md_color('horizontal_rule', $1))/mge;
}),
bold_italic => Step(bold => sub {
s{$SKIP_CODE|(?<!\\)(?<m>\*\*\*)(?<t>.*?)(?<!\\)\g{m}}{
protect(mark_color('bold', $+{m}) . md_color('bold', md_color('italic', $+{t})) . mark_color('bold', $+{m}))
}gep;
s{$SKIP_CODE|(?<![\\\w])(?<m>___)(?<t>.*?)(?<!\\)\g{m}(?!\w)}{
protect(mark_color('bold', $+{m}) . md_color('bold', md_color('italic', $+{t})) . mark_color('bold', $+{m}))
}gep;
}),
bold => Step(bold => sub {
my($open, $close) = flanking('**');
# no opener inside, so that a closer pairs with the nearest opener
s{$SKIP_CODE|(?<!\\)(?<m>$open)(?<t>(?:(?!$open).)*?)(?<!\\)$close}{
mark_color('bold', $+{m}) . md_color('bold', $+{t}) . mark_color('bold', $+{m})
}gep;
s{$SKIP_CODE|(?<![\\\w])(?<m>__)(?<t>.*?)(?<!\\)\g{m}(?!\w)}{
mark_color('bold', $+{m}) . md_color('bold', $+{t}) . mark_color('bold', $+{m})
}gep;
}),
italic => Step(italic => sub {
my($open, $close) = flanking('*');
s{$SKIP_CODE|(?<![\\\w])(?<m>_)(?<t>(?:(?!_).)+)(?<!\\)\g{m}(?!\w)}{
mark_color('italic', $+{m}) . md_color('italic', $+{t}) . mark_color('italic', $+{m})
}gep;
s{$SKIP_CODE|(?<![\\*])(?<m>$open)(?<t>(?:(?!\*).)+)(?<!\\)$close(?!\*)}{
mark_color('italic', $+{m}) . md_color('italic', $+{t}) . mark_color('italic', $+{m})
}gep;
}),
strike => Step(strike => sub {
s{$SKIP_CODE|(?<!\\)(?<m>~~)(?<t>.+?)(?<!\\)\g{m}}{
mark_color('strike', $+{m}) . md_color('strike', $+{t}) . mark_color('strike', $+{m})
}gep;
}),
blockquotes => Step(blockquote => sub {
s/^( {0,3})((?:>\h?)+)(.*)$/$1 . md_color('blockquote', $2) . $3/mge;
}),
);
#
lib/App/Greple/md.pm view on Meta::CPAN
}
1;
__DATA__
option default \
-G --filter --filestyle=once --color=always \
--begin &__PACKAGE__::begin
define {CODE_BLOCK} ^ {0,3}(?<bt>(?<fc>[`~])\g{fc}{2,}+)(.*)\n((?s:.*?))^ {0,3}\g{bt}\g{fc}*+\h*$
define {COMMENT} ^<!--(?![->])(?s:.+?)-->
define {SGR} (?:\e\[[\d:;]*[mK])*
define {SGR_END} (?:\e\[[0;]*[mK])*
define {LIST_MARKER} (?:[*+-]|(?:\d+|#)[.)])
define {LIST_ITEM} ^\h*{LIST_MARKER}\h+.*\n
define {QUOTE_LINE} ^ {0,3}(?:{SGR}>\h?)+.*\n
# The prefix of a folded line's continuation lines: the quote markers
# (named group, repeated by ansifold --keepindent=named), then the indent,
# a list marker or a definition colon, which become spaces of the same
# width. Every line matches (all parts may be empty), so that every line
lib/App/Greple/md/widen.pm view on Meta::CPAN
This software is released under the MIT License.
L<https://opensource.org/licenses/MIT>
=cut
# Where the text must stay as written: fenced code with a language, code
# spans, and the addresses of links.
my $SKIP = qr{
(?:
^ [ ]{0,3} (?<_fence> (?<_fc>[`~]) \g{_fc}{2,}+ ) \h* \S .* \n
(?s:.*?)
^ [ ]{0,3} \g{_fence} \g{_fc}*+ \h* $
|
(?<_bt> `++ ) (?: (?! \g{_bt} ) . )+? \g{_bt}
|
\] \( [^)\n]* \)
|
< [a-zA-Z][\w+.-]*: [^>\s]* >
)
(*SKIP)(*FAIL)
}xm;
sub class {
my $spec = shift;
( run in 0.832 second using v1.01-cache-2.11-cpan-99fa8243a42 )