App-mdee
view release on metacpan or search on metacpan
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
**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`:
```
--cm inline_code=L15/L23 \
--cm comment=CM+r60 \
```
Note: `comment` color in mdee is `${base}+r60` (base color + reddish). For standalone default, use a hardcoded approximation. mdee will override via `--cm`.
**Step 3: Test inline code protection**
Create test: a line with `` `**not bold**` `` should show inline code color, NOT bold.
Run: `echo '`**not bold**` but **this is bold**' | greple -Mmd -Ilib 2>/dev/null`
Expected: First part colored as inline code, second part not yet colored (bold not implemented yet)
**Step 4: Commit**
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: inline code and comment colorization with protection"
```
---
### Task 4: Link Patterns with OSC 8
Add link, image, and image_link patterns with OSC 8 hyperlink generation.
**Files:**
- Modify: `lib/App/Greple/md.pm`
**Step 1: Add osc8 function and link patterns**
```perl
use URI::Escape;
sub osc8 {
return $_[1] unless $config->{osc8};
my($url, $text) = @_;
my $escaped = uri_escape_utf8($url, "^\\x20-\\x7e");
"\e]8;;${escaped}\e\\${text}\e]8;;\e\\";
}
```
**Step 2: Add link processing to colorize()**
After inline code protection, before headings:
```perl
# Link text inner pattern
my $LT = qr/(?:`[^`\n]*+`|\\.|[^`\\\n\]]++)+/;
# Image links: [](url) â !linked to img + [alt] linked to url
s{\[!\[($LT)\]\(([^)\n]+)\)\]\(<?([^>)\s\n]+)>?\)}{
protect(
osc8($2, main::color('image_link', "!"))
. osc8($3, main::color('image_link', "[$1]"))
)
}ge;
# Images: 
s{!\[($LT)\]\(<?([^>)\s\n]+)>?\)}{
protect(osc8($2, main::color('image', "![$1]")))
}ge;
# Links: [text](url) (not preceded by !)
s{(?<!!) \[($LT)\]\(<?([^>)\s\n]+)>?\)}{
protect(osc8($2, main::color('link', "[$1]")))
}xge;
```
Links are protected so heading colorization can be applied cumulatively on top.
**Step 3: Add link colors to __DATA__**
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
- 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"
```
---
### Task 7: Blockquote and Horizontal Rule
Add remaining elements.
**Files:**
- Modify: `lib/App/Greple/md.pm`
**Step 1: Add blockquote and horizontal rule to colorize()**
```perl
# Blockquote marker: > at start of line
s/^(>+\h?)(.*)$/main::color('blockquote', $1) . $2/me;
# Horizontal rule: 3+ of -, *, or _ (possibly with spaces)
s/^([ ]{0,3}(?:[-*_][ ]*){3,})$/main::color('horizontal_rule', $1)/me;
```
Note: For blockquotes, only the `>` marker is colored (not the content), matching typical Markdown viewer behavior. Content may contain other elements (bold, links) which are already processed.
**Step 2: Add colors to __DATA__**
```
--cm blockquote=<RoyalBlue>=y25;D \
--cm horizontal_rule=L15 \
```
**Step 3: Test**
Run: `printf '> quoted **bold** text\n---\n' | greple -Mmd -Ilib 2>/dev/null | cat -v`
Expected: `>` marker colored, bold within quote colored, `---` colored as horizontal rule.
**Step 4: Commit**
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: blockquote and horizontal rule patterns"
```
---
### Task 8: Complete __DATA__ Color Definitions
Finalize the `__DATA__` section with complete light and dark mode color definitions.
**Files:**
- Modify: `lib/App/Greple/md.pm`
**Step 1: Write complete __DATA__ section**
```
__DATA__
option default \
-G --all --need=0 --filestyle=once --color=always \
--cm code_fence=L20 \
--cm code_lang=L18 \
--cm code_body=/L23;E \
--cm inline_code=L15/L23 \
--cm comment=L15 \
--cm link=CU \
--cm image=CU \
--cm image_link=CU \
--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 \
--cm bold=<RoyalBlue>=y25;D \
--cm italic=I \
--cm strike=X \
--cm blockquote=<RoyalBlue>=y25;D \
--cm horizontal_rule=L15 \
-E '(?!)' \
--print &__PACKAGE__::colorize
option --md-dark \
--cm code_fence=L10 \
--cm code_lang=L12 \
--cm code_body=/L05;E \
--cm inline_code=L12/L05 \
--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 \
--cm bold=<RoyalBlue>=y80;D \
--cm blockquote=<RoyalBlue>=y80;D
```
Note: Dark mode only overrides colors that differ from light. Labels like `italic`, `strike`, `link`, `image`, `image_link`, `horizontal_rule` are the same in both modes, so they only appear in `option default`.
**Step 2: Test light mode**
Run: `greple -Mmd -Ilib /Users/utashiro/Git/tecolicom/App-mdee/t/test.md 2>/dev/null | head -30`
Expected: Colored output with light mode colors
**Step 3: Test dark mode**
Run: `greple -Mmd::config(mode=dark) -Ilib /Users/utashiro/Git/tecolicom/App-mdee/t/test.md 2>/dev/null | head -30`
Expected: Colored output with dark mode colors
**Step 4: Commit**
```bash
git add lib/App/Greple/md.pm
git commit -m "feat: complete light/dark color definitions in __DATA__"
```
---
### Task 9: Add Tests
Add functional tests beyond the compilation test.
**Files:**
- Create: `t/01_colorize.t`
- Create: `t/test.md` (small test markdown file)
**Step 1: Create test markdown file**
```markdown
# Heading 1
## Heading 2 with [link](https://example.com)
Normal text with **bold** and *italic*.
`inline code with **not bold**`
```bash
code block
```
> blockquote
---
~~strikethrough~~
```
**Step 2: Write colorize test**
```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
invoke greple "${md_opts[@]}" \
--filestyle=once --color=always \
"$@"
}
```
Note: The module's `__DATA__` `option default` defines base options (`-G --all --need=0 -E '(?!)' --print &colorize`). mdee only needs to pass mode config and color overrides.
**Step 2: Remove old pattern/color building code**
Remove from mdee:
- `osc8_prologue`, `link_func`, `image_func`, `image_link_func` variables (lines 174-177)
- `greple_opts` array initialization (line 564)
- `add_pattern()` function (lines 566-571)
- The `for _name in ...` loop that builds greple_opts (lines 575-577)
Keep:
- `patterns_default` array (still needed for fold/table patterns)
docs/plans/2026-02-16-greple-md-implementation.md view on Meta::CPAN
**Step 4: Test mdee with -Mmd**
Run: `./script/mdee -f /Users/utashiro/Git/tecolicom/App-mdee/t/test.md`
Expected: Colored output matching previous mdee behavior (approximately)
**Step 5: Test mdee pipeline (fold + table)**
Run: `./script/mdee /Users/utashiro/Git/tecolicom/App-mdee/t/test.md`
Expected: Full pipeline (highlight â fold â table â nup) works
**Step 6: Commit**
```bash
cd /Users/utashiro/Git/tecolicom/App-mdee
git add script/mdee
git commit -m "feat: use App::Greple::md module for highlight processing"
```
---
### Task 12: mdee Cleanup
Remove code that is now handled by the module.
**Files:**
- Modify: `/Users/utashiro/Git/tecolicom/App-mdee/script/mdee`
**Step 1: Remove unused highlighting code**
Remove:
- `osc8_prologue` variable
- `link_func`, `image_func`, `image_link_func` variables
- `add_pattern()` function
- The pattern-to-greple-opts loop
- `greple_opts` array (replaced by `md_opts` in `run_greple`)
Keep:
- `patterns_default` (fold/table patterns: `item_prefix`, `def_pattern`, `autoindent`, `table`, `code_block`, `comment`)
- `pattern` associative array
- All theme/color code (themes still need `code_block`, `inline_code` etc. for `--list-themes` display)
**Step 2: Verify all mdee features still work**
```bash
# Filter mode
./script/mdee -f t/test.md
# Pager mode
./script/mdee -p t/test.md
# Nup mode
./script/mdee t/test.md
# Dark mode
./script/mdee --mode=dark -f t/test.md
# Theme override
./script/mdee --no-theme -f t/test.md
# Show field visibility
./script/mdee --show all= --show bold -f t/test.md
# List themes
./script/mdee --list-themes
```
**Step 3: Commit**
```bash
cd /Users/utashiro/Git/tecolicom/App-mdee
git add script/mdee
git commit -m "refactor: remove highlighting code replaced by App::Greple::md"
```
---
## Key Design Decisions
### Color Label Mapping (mdee â module)
| mdee theme key | Module --cm label(s) | Notes |
|---|---|---|
| `h1`-`h6` | `h1`-`h6` | Direct mapping |
| `bold` | `bold` | Direct |
| `italic` | `italic` | Direct |
| `strike` | `strike` | Direct |
| `comment` | `comment` | Direct |
| `code_block` | `code_fence`, `code_lang`, `code_body` | Split into 3 labels |
| `inline_code` | `inline_code` | Module uses single label |
| `link` | `link` | mdee's `sub{...}` func spec â module's simple color |
| `image` | `image` | Same as link |
| `image_link` | `image_link` | Same as link |
### Protection Mechanism
NUL-byte placeholders protect processed regions (inline code, comments, links) from being matched by later patterns. This replaces the current approach where greple handles pattern independence through separate `-E` entries.
### State Machine vs Multiline Regex
Code blocks use a per-line state machine instead of multiline regex because `-G` mode delivers one line at a time to `--print`. This is simpler and avoids questions about non-`-G` mode behavior with `--print`.
( run in 0.487 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )