App-mdee
view release on metacpan or search on metacpan
enabling cumulative coloring regardless of position. Its position
in the pipeline is controlled by `heading_markup`:
**`build_pipeline()` logic:**
- `heading_markup=0` (default): `@protect_steps`, **headings**, `@inline_steps`, `@final_steps`
- `heading_markup=1` or `all`: `@protect_steps`, `@inline_steps`, **headings**, `@final_steps`
- `heading_markup=bold:italic`: `@protect_steps`, bold, italic, **headings**, inline_code, horizontal_rules, strike, `@final_steps`
When `heading_markup` is a colon-separated step list, specified steps
are moved before headings while remaining inline steps stay after.
This allows selective markup inside headings (e.g., bold but not code).
Links are always processed before headings (in `@protect_steps`), so
OSC 8 hyperlinks in headings remain clickable regardless of
`heading_markup`.
The `colorize()` loop calls `$step->active` and `$step->run`:
```perl
for my $name (build_pipeline()) {
my $step = $colorize{$name};
$step->run if $step->active;
}
```
### Code & Emphasis Color Labels
Code-related and emphasis-related theme keys map directly to module labels:
| Theme Key | Module Label | Description |
|-----------|-------------|-------------|
| `code_mark` | `code_mark` | Fenced code block delimiters (```` ``` ````, `~~~`) |
| `code_tick` | `code_tick` | Inline code backticks (`` ` ``) |
| `code_info` | `code_info` | Fenced code block info string |
| `code_block` | `code_block` | Fenced code block body (with `;E`) |
| `code_inline` | `code_inline` | Inline code body (without `;E`) |
| `emphasis_mark` | `emphasis_mark` | Emphasis markers (`**`, `*`, `__`, `_`, `~~`) |
`emphasis_mark` is the default color for all emphasis markers. Per-type overrides
(`bold_mark`, `italic_mark`, `strike_mark`) can be defined via `--cm` and take
priority when present. This follows the same pattern as `code_tick` / `code_inline`.
```bash
# Light mode
[code_mark]='L20'
[code_tick]='L15/L23'
[code_info]='${base_name}=y70'
[code_block]='/L23;E'
[code_inline]='L00/L23'
[emphasis_mark]='L18'
# Dark mode
[code_mark]='L10'
[code_tick]='L15/L05'
[code_info]='L10'
[code_block]='/L05;E'
[code_inline]='L25/L05'
[emphasis_mark]='L10'
```
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
- `foldlist` config parameter (default: 0) enables/disables folding
- `foldwidth` config parameter (default: 80) controls the fold width
- `finalize()` defines `--fold` as `--fold-by $foldwidth` via `$mod->setopt()` (for direct command-line use)
- When `foldlist=1` in config, `finalize()` adds `--fold-by $foldwidth` to the module's default option
- mdee passes `foldlist=1,foldwidth=$width` in config when fold is enabled
- The `--fold-by` option in `__DATA__` uses `-Mtee "&ansifold"` with `--exclude` patterns for code blocks, HTML comments, and tables
#### Definition List Pattern
`(?:\A|\G\n|\n\n).+\n\n?(:\h+.*\n)`
- `(?:\A|\G\n|\n\n)`: Start of file, or after previous match, or after blank line
- `.+\n`: Term line
- `\n?`: Optional blank line between term and definition
- `(:\h+.*\n)`: Capture group for definition line (only this part is processed)
#### Table Formatting in md Module
Table formatting is handled within the `App::Greple::md` module's `begin()` function, which orchestrates `colorize()` (syntax highlighting) followed by `format_table()`:
```perl
sub begin {
colorize() if $config->{colorize};
format_table() if $config->{table};
}
```
Stage execution is controlled by config flags: `colorize` (default: 1), `table` (default: 1). Fold is not controlled in `begin()` because it operates via greple's pattern matching pipeline (`-Mtee`), not text transformation in the `begin` hook.
`format_table()` detects table blocks via `(^ {0,3}\|.+\|\n){3,}` and processes each block:
```perl
s{(^ {0,3}\|.+\|\n){3,}}{
my $block = $&;
my($right, $center) = parse_separator(\$block);
my @sep_opt;
if ($config->{table_trim}) {
@sep_opt = ('-rs', '\s*\|\s*', '--item-format= %s ', '--table-remove=1,-0', '--padding');
} else {
$_++ for @$right, @$center;
@sep_opt = ('-s', '|');
}
[ show | %! # field visibility ]=
)
show() {
local arg=$2 key val
if [[ $arg == *=* ]]; then
key=${arg%%=*} val=${arg#*=}
else
key=$arg val=1
fi
if [[ $key == all ]]; then
for k in "${show_fields[@]}"; do
show[$k]=$val
done
fi
}
```
- `%!`: Hash type with callback
- Callback receives `($optname, $arg)` - parse key=value from `$arg`
- `all` is special: sets all fields to the given value
- Order matters: `--show all= --show bold` disables all, then enables bold
- Individual key=value is automatically handled by getoptlong.sh
In `run_greple()`, show values are passed to the md module:
```bash
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)`...
URL encoding: `uri_escape_utf8($url, "^\\x20-\\x7e")` â escapes only non-ASCII, preserving `:`, `/`, etc.
#### Code Span Pattern and `(*SKIP)(*FAIL)` Protection
`$CODE` matches code spans (named captures `_bt`, `_content`). `$SKIP_CODE = qr{$CODE (*SKIP)(*FAIL)}` is used as first alternative in link/emphasis substitutions to skip code spans. Named captures required â `$SKIP_CODE` consumes `$1`. Link patter...
#### Link Text Matching Pattern
`$LT = qr/(?:`[^`\n]*+`|\\.|[^`\\\n\]]++)+/` â three branches: backtick-span (allows `]` inside), backslash-escape (`\]`), any other char. Possessive quantifiers prevent backtracking.
### Mode Detection with [Getopt::EX::termcolor](https://metacpan.org/pod/Getopt::EX::termcolor)
Terminal background luminance (0-100) is detected via OSC 11 query. Luminance < 50 â dark mode, ⥠50 â light mode.
### [Getopt::Long::Bash](https://metacpan.org/pod/Getopt::Long::Bash) (getoptlong.sh)
Option parsing uses getoptlong.sh from Getopt::Long::Bash module.
#### OPTS Array Format
```bash
declare -A OPTS=(
[&REQUIRE]=0.7.1 [&USAGE]="$USAGE"
[ option | o : # comment ]=default
)
```
Key format: `option_name | short_opt spec # comment`
- `option_name`: Long option name (becomes variable `$option_name`)
- `short_opt`: Single character short option
- `spec`: Option specification
- `# comment`: Description for help
#### Option Specifications
|Spec|Type|Description|
|-|-|-|
|(empty)|Boolean|Flag, sets variable to 1. `--no-` prefix supported.|
|`!`|Callback|Calls function with same name as option|
|`+`|Incremental|Counter, increases with each use|
|`:`|Required arg|Requires argument value|
|`:=i`|Integer|Requires integer argument|
|`?!`|Optional+Callback|Optional argument with callback|
|`%`|Hash|Hash variable, `--opt key=value`|
|`%!`|Hash+Callback|Hash variable with callback|
|`:>array`|Array append|Appends `--option=value` to named array|
|`@`|Array|Array variable, supports comma-separated values|
#### Special Keys
- `[&REQUIRE]=version`: Minimum getoptlong.sh version
- `[&USAGE]="..."`: Usage message for --help
- If a function with the same name as an option exists, it's called after parsing (callback)
- Invocation: `. getoptlong.sh OPTS "$@"`
## Limitations
### HTML Comments
Only HTML comments starting at the beginning of a line are highlighted. Inline comments are not matched to avoid conflicts with inline code containing comment-like text (e.g., `` `<!-->` ``).
### Emphasis (Bold/Italic)
Emphasis patterns do not span multiple lines. Multi-line bold or italic text is not supported.
`***bold italic***` and `___bold italic___` are supported as a combined pattern (processed before bold and italic, result is `protect()`ed). Other nested forms (e.g., `**bold _italic_**`) are not supported.
### Links
Link patterns do not span multiple lines. The link text and URL must be on the same line.
Link text matching uses `` (?:`[^`\n]*+`|\\.|[^`\\\n\]]++)+ `` to handle:
( run in 0.333 second using v1.01-cache-2.11-cpan-f56aa216473 )