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>`{3,}+|~{3,}+)(.*)\n((?s:.*?))^ {0,3}(\g{bt})
```
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 ...
### Text Folding (md module)
Text folding is handled within the `App::Greple::md` module using `-Mtee` to pipe matched regions through `ansifold`. The md module defines `--fold-by` as a greple option in its `__DATA__` section, and `--fold` is dynamically defined in `finalize()` ...
#### Fold Architecture
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**`
- `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;
#
# colorize() - the main function
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 {
s{$SKIP_CODE|(?<!\\)(?<m>\*\*)(?<t>.*?)(?<!\\)\g{m}}{
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 {
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>\*)(?<t>(?:(?!\*).)+)(?<!\\)\g{m}(?!\*)}{
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/^(>+\h?)(.*)$/md_color('blockquote', $1) . $2/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>`{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> \
-- \
( run in 2.744 seconds using v1.01-cache-2.11-cpan-437f7b0c052 )