App-mdee
view release on metacpan or search on metacpan
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
**Step 1: Verify per-line delivery**
Run test to confirm `--print` gets one line at a time with `-G`:
```bash
greple -G -e '(?!)' --all --need=0 \
--print 'sub{ "[" . length($_) . "]" . $_ }' \
-Ilib t/test.md | head -10
```
Expected: Each line prefixed with its length, confirming per-line delivery.
**Step 2: Implement code block state machine**
In `md.pm`, add state variables and code block detection to `colorize()`:
```perl
my $in_code_block = 0;
my $code_fence_re;
sub colorize {
# Code block state tracking
if ($in_code_block) {
if (/^ {0,3}$code_fence_re\s*$/) {
$in_code_block = 0;
return main::color('code_fence', $_);
}
return main::color('code_body', $_);
}
if (/^( {0,3})(`{3,}|~{3,})(.*)/) {
$in_code_block = 1;
my $char = quotemeta(substr($2, 0, 1));
my $len = length($2);
$code_fence_re = qr/${char}{${len},}/;
my $fence_line = "$1$2";
my $lang = $3;
my $result = main::color('code_fence', $fence_line);
$result .= main::color('code_lang', $lang) if length($lang);
$result .= "\n" if /\n$/;
return $result;
}
# (other patterns will be added in subsequent tasks)
$_;
}
```
**Step 3: Add code block colors to __DATA__**
```
option default \
-G --all --need=0 --filestyle=once --color=always \
--cm code_fence=L20 \
--cm code_lang=L18 \
--cm code_body=/L23;E \
-E '(?!)' \
--print &__PACKAGE__::colorize
```
Note: The current mdee `code_block` key uses comma-separated colors for 4 capture groups: `'L20 , L18 , /L23;E , L20'`. In the module, these become separate `--cm` labels: `code_fence` (opening+closing), `code_lang` (language specifier), `code_body` ...
**Step 4: Test code block colorization**
Run: `greple -Mmd -Ilib /Users/utashiro/Git/tecolicom/App-mdee/t/test.md 2>/dev/null | head -30`
Expected: Code blocks should be visibly colored (gray text/background)
**Step 5: Commit**
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: code block colorization with state machine"
```
---
### Task 3: Inline Code and Comment Protection
Add inline code and HTML comment colorization. These must be processed early to protect their content from emphasis/link matching.
**Files:**
- Modify: `lib/App/Greple/md.pm`
**Step 1: Add inline code pattern**
In `colorize()`, after the code block check, add inline code replacement. Use a protection mechanism: replace matched regions with placeholders, process other patterns, then restore.
```perl
# Protection mechanism: replace regions with NUL-delimited placeholders
my @protected;
sub protect {
my $text = shift;
push @protected, $text;
"\x00" . $#protected . "\x00";
}
sub restore {
my $s = shift;
$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($_);
$_;
}
```
Note: The `protect/restore` mechanism ensures inline code like `` `**bold**` `` is not processed as bold. NUL bytes are safe as placeholders since they don't appear in normal text.
**Step 2: Add colors to __DATA__**
Add to `option default`:
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: link/image/image_link with OSC 8 hyperlinks"
```
---
### Task 5: Heading Patterns with Cumulative Coloring
Add h1-h6 heading patterns. Apply AFTER link processing so heading color wraps around link-colored text (cumulative coloring).
**Files:**
- Modify: `lib/App/Greple/md.pm`
**Step 1: Add heading patterns to colorize()**
After link processing, before emphasis:
```perl
# Headings: apply color to entire line (cumulative over links)
# Process h6 first (most #'s) to avoid h1 matching h6 lines
s/^(######+\h+.*)$/main::color('h6', $1)/me;
s/^(#####\h+.*)$/main::color('h5', $1)/me;
s/^(####\h+.*)$/main::color('h4', $1)/me;
s/^(###\h+.*)$/main::color('h3', $1)/me;
s/^(##\h+.*)$/main::color('h2', $1)/me;
s/^(#\h+.*)$/main::color('h1', $1)/me;
```
Note: Process from h6 to h1. Each regex is anchored with `^...$` and `m` flag for multiline. Since links are already colored (and protected), heading color is applied cumulatively on top.
**Step 2: Add heading colors to __DATA__**
Light mode defaults (in `option default` or `option --md-light`):
```
--cm h1=L25DE/<RoyalBlue>=y25 \
--cm h2=L25DE/<RoyalBlue>=y25+y20 \
--cm h3=L25DN/<RoyalBlue>=y25+y30 \
--cm h4=<RoyalBlue>=y25;UD \
--cm h5=<RoyalBlue>=y25;U \
--cm h6=<RoyalBlue>=y25 \
```
Dark mode overrides (in `option --md-dark`):
```
option --md-dark \
--cm h1=L00DE/<RoyalBlue>=y80 \
--cm h2=L00DE/<RoyalBlue>=y80-y15 \
--cm h3=L00DN/<RoyalBlue>=y80-y25 \
--cm h4=<RoyalBlue>=y80;UD \
--cm h5=<RoyalBlue>=y80;U \
--cm h6=<RoyalBlue>=y80 \
```
Note: mdee's `${base}` expansion is done in Bash. For module defaults, the base color is hardcoded. mdee will override all colors via `--cm` passthrough anyway.
**Step 3: Test cumulative heading + link coloring**
Run: `echo '## See [docs](https://example.com) here' | greple -Mmd -Ilib 2>/dev/null | cat -v`
Expected: The line has both h2 color (background) and link color (inside the link text), with OSC 8 sequences preserved.
**Step 4: Commit**
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: heading colorization with cumulative coloring over links"
```
---
### Task 6: Emphasis Patterns (Bold, Italic, Strike)
Add bold, italic, and strikethrough patterns. These are processed after headings.
**Files:**
- Modify: `lib/App/Greple/md.pm`
**Step 1: Add emphasis patterns to colorize()**
After heading processing:
```perl
# Bold: **text** and __text__
s/(?<![\\`])\*\*.*?(?<!\\)\*\*/main::color('bold', $&)/ge;
s/(?<![\\`\w])__.*?(?<!\\)__(?!\w)/main::color('bold', $&)/ge;
# Italic: _text_ and *text*
s/(?<![\\`\w])_(?:(?!_).)+(?<!\\)_(?!\w)/main::color('italic', $&)/ge;
s/(?<![\\`\*])\*(?:(?!\*).)+(?<!\\)\*(?!\*)/main::color('italic', $&)/ge;
# Strikethrough: ~~text~~
s/(?<![\\`])~~.+?(?<!\\)~~/main::color('strike', $&)/ge;
```
Note: These patterns are identical to current mdee patterns. The `(?<![\\`])` lookbehind provides backslash and backtick protection. The protect/restore mechanism handles inline code, so backtick protection in the regex is belt-and-suspenders safety.
**Step 2: Add emphasis colors to __DATA__**
```
--cm bold=<RoyalBlue>=y25;D \
--cm italic=I \
--cm strike=X \
```
**Step 3: Test emphasis inside headings**
Run:
```bash
printf '## This is **bold** in heading\n**standalone bold**\n`**not bold**`\n' \
| greple -Mmd -Ilib 2>/dev/null | cat -v
```
Expected: Bold inside heading gets both heading and bold styling. Inline code content is not bolded.
**Step 4: Commit**
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: bold, italic, and strikethrough patterns"
```
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
```perl
use strict;
use Test::More;
use open qw(:std :encoding(utf-8));
# Test that module loads
use_ok('App::Greple::md');
# Test that greple -Mmd produces output
my $test_md = 't/test.md';
plan skip_all => "greple not found" unless `which greple`;
my $out = `greple -Mmd -Ilib $test_md 2>/dev/null`;
ok(length($out) > 0, "greple -Mmd produces output");
# Output should contain ANSI escape sequences (colored)
like($out, qr/\e\[/, "output contains ANSI color sequences");
# Test that code blocks are protected
my $code_test = '`**not bold**`';
my $code_out = `echo '$code_test' | greple -Mmd -Ilib 2>/dev/null`;
# The **not bold** should NOT have bold ANSI codes applied separately
# Test dark mode
my $dark_out = `greple '-Mmd::config(mode=dark)' -Ilib $test_md 2>/dev/null`;
ok(length($dark_out) > 0, "dark mode produces output");
done_testing;
```
**Step 3: Run tests**
Run: `prove -v t/`
Expected: All tests pass
**Step 4: Commit**
```bash
git add t/01_colorize.t t/test.md
git commit -m "test: add functional tests for colorize"
```
---
### Task 10: Integration Test with mdee's test.md
Verify the module handles all Markdown elements in mdee's comprehensive test file.
**Files:**
- No new files
**Step 1: Full visual test with light mode**
Run: `greple -Mmd -Ilib /Users/utashiro/Git/tecolicom/App-mdee/t/test.md 2>/dev/null | less -R`
Verify:
- [ ] Headers h1-h6 are colored with decreasing prominence
- [ ] Bold text is bold with base color
- [ ] Italic text is italic
- [ ] Strikethrough has strikethrough styling
- [ ] Inline code has background color
- [ ] Code blocks have background color, fence and language colored differently
- [ ] Links show as `[text]` with clickable OSC 8 (on supported terminals)
- [ ] Images show as `![alt]` with clickable OSC 8
- [ ] Blockquote `>` marker is colored
- [ ] Horizontal rules are colored
- [ ] HTML comments are colored
- [ ] Links inside headings have both heading color AND link color
- [ ] Bold inside inline code is NOT processed as bold
**Step 2: Full visual test with dark mode**
Run: `greple '-Mmd::config(mode=dark)' -Ilib /Users/utashiro/Git/tecolicom/App-mdee/t/test.md 2>/dev/null | less -R`
Verify same elements with dark-appropriate colors.
**Step 3: Test --cm override**
Run: `greple -Mmd --cm h1=RD -Ilib /Users/utashiro/Git/tecolicom/App-mdee/t/test.md 2>/dev/null | head -5`
Expected: h1 heading should be red+bold instead of default blue
**Step 4: Fix any issues found and commit**
```bash
git add -A
git commit -m "fix: address integration test findings"
```
---
## Phase 2: mdee Integration
### Task 11: Modify mdee's run_greple to Use -Mmd
Replace mdee's pattern/color building logic with `-Mmd` module invocation.
**Files:**
- Modify: `/Users/utashiro/Git/tecolicom/App-mdee/script/mdee`
**Step 1: Modify run_greple()**
The current `run_greple()` passes many `--cm`/`-E` pairs built by `add_pattern`. Replace with:
```bash
run_greple() {
local -a md_opts=()
# Pass mode via config
md_opts+=("-Mmd::config(mode=${mode})")
# Pass all color overrides from theme
for name in "${!colors[@]}"; do
[[ $name == base ]] && continue
md_opts+=(--cm "${name}=${colors[$name]}")
done
# Pass show visibility (disable hidden fields)
for name in "${!show[@]}"; do
local val=${show[$name]}
[[ ! $val || $val == 0 ]] && md_opts+=(--cm "${name}=")
done
( run in 0.449 second using v1.01-cache-2.11-cpan-f56aa216473 )