Why customise eg?
eg is billed as a tool for quick command-line examples—a faster, more focused alternative to man pages. However, I've found it equally valuable as a foundation for comprehensive CLI cheat sheets. The challenge? As these cheat sheets grow more verbose, they need enhanced styling to remain scannable and legible.
This article chronicles the journey of transforming eg's output through custom substitutions, creating a personalised documentation system that's both functional and visually refined.
The setup
Our environment consists of:
- eg - The examples tool
- bat - Syntax-highlighted pager
- Kitty - GPU-accelerated terminal with the Twilight theme
The Twilight theme provides a muted dark palette that we'll leverage for our styling decisions:
background #141414
foreground #feffd3
color0 #141414 # Black
color2 #afb979 # Green (our base for comments)
color8 #262626 # Bright black/grey
# ... additional colours
Understanding display order
Before diving into customisation, there's a critical behaviour to understand: eg does not provide a configuration option for controlling display order. Files are always shown in this sequence:
- Custom directory files (if they exist)
- Examples directory files (if they exist)
Knowing this, we can strategically organise our content:
[eg-config]
custom-dir = ~/dotfiles/eg/custom # Public cheat sheets (shown first)
examples-dir = ~/dotfiles/_private/eg/examples # Private notes (shown second)
This arrangement means generic, shareable cheat sheets appear first, followed by private, context-specific notes. To visually separate these sections, private files begin with a PRIVATE marker that we'll transform via substitution.
The critical insight: when substitutions run
Here's the key to understanding eg substitutions: they run after colourisation.
The processing pipeline is:
- Colour - ANSI escape sequences are added to the text
- Squeeze - Blank lines are reduced (if enabled)
- Substitutions - Your custom regex patterns are applied
This means if you want to match a heading like ## Basic Syntax, you can't use a simple pattern like ^## (.*)$. By the time your substitution runs, that text has been transformed into:
^[[38;5;245m##^[[0m^[[31m^[[1m Basic Syntax^[[0m
Your patterns must account for these embedded ANSI escape sequences.
Essential debugging: making the invisible visible
To see what you're actually matching, use this command:
eg kitty | cat -v | head -50
This reveals the escape sequences as visible text:
^[[38;5;245m- Start colour 245 (grey)^[[0m- Reset to default^[[1m- Bold^[[3m- Italic
In regex patterns, ^[ becomes \x1b\\[ (the ESC character + literal bracket).
Building substitutions: a chronological journey
Step 1: cleaner comments
The problem: Comments like # Create new tab are visually cluttered. The hash symbol adds noise, and the colour (inherited from code blocks) doesn't distinguish comments from commands.
Inspecting the reality:
eg kitty | grep "# Create new tab" | cat -v
Output:
^[[36m^[[1m^[[0m^[[38;5;143m# Create new tab^[[0m
The text is wrapped in:
- Cyan bold reset sequence
- Code colour 143 (yellowish-green)
- The comment text with hash
- Reset
The solution:
format-standalone-comments = ['^(\\s+)\x1b\\[36m\x1b\\[1m\x1b\\[0m\x1b\\[38;5;143m# ([^\x1b]+)\x1b\\[0m$', '\\1\x1b[3m\x1b[38;5;245m\\2\x1b[0m', True]
This pattern:
- Matches the leading whitespace and escape sequences
- Captures the comment text after the hash (note:
#with space) - Replaces with italic + grey colour (245) + the text
Result: # Create new tab becomes an italicised, grey Create new tab without the hash.
Step 2: inline comments
The problem: Command lines with trailing comments like C-S-t # New tab still show the hash symbol.
The solution:
format-comments-no-hash = ['(\S +)#( +)(.*?)$', '\\1\\2\x1b[3m\x1b[38;5;245m\\3\x1b[0m', True]
This works on the already-colourised text but targets a different pattern—text that has non-whitespace before the hash. It:
- Captures everything before the hash
- Captures the spaces after the hash
- Applies italic grey to the comment portion
Note: The hash itself is in the pattern but not in a capture group, so it's removed.
Result: C-S-t # New tab becomes C-S-t New tab with the comment portion in grey italics.
Step 3: visual hierarchy with h2 separators
The problem: Long cheat sheets with multiple ## Section headings need visual breaks for scannability.
Inspecting a heading:
eg kitty | grep "^##" | cat -v
Output:
^[[38;5;245m##^[[0m^[[31m^[[1m Basic Syntax^[[0m
The solution:
separator-h2 = ['^\x1b\\[38;5;245m(##)\x1b\\[0m\x1b\\[31m\x1b\\[1m([^\x1b]+)\x1b\\[0m$', '\x1b[38;5;245m\\1\x1b[0m\x1b[31m\x1b[1m\\2\x1b[0m\n\x1b[38;5;245m···········································································\x1b[0m', True]
This pattern:
- Captures the pound-coloured
## - Captures the red-bold heading text
- Reconstructs the heading exactly as it was
- Adds a newline and a grey separator line
Result: Each H2 heading gains an underline, creating clear visual sections.
Step 4: markdown bold to ansi bold
The problem: Markdown **text** appears literally rather than as bold text.
The solution:
markdown-bold = ['\*\*([^*]+)\*\*', '\x1b[1m\\1\x1b[0m', True]
This runs early in the alphabetical order (starts with 'm'), so it processes the raw markdown before most colourisation affects it:
\*\*- Matches literal**(escaped asterisks)([^*]+)- Captures the text between asterisks- Replaces with ANSI bold codes
Result: **Always use quotes** becomes actual bold terminal text.
Step 5: visual breaks between sections
The problem: The transition from public to private cheat sheets needs emphasis.
The solution: Two complementary substitutions:
separator-dash = ['^---$', '\x1b[38;5;245m···········································································\x1b[0m', True]
separator-private = ['^PRIVATE$', '\n\n\x1b[38;5;245m╾───────────────────────────────────────────────────────╼ PRIVATE ╾───────────────────────────────────────────────────────╼\x1b[0m\n', True]
The first converts simple --- markers into subtle grey separators. The second transforms the PRIVATE marker into a prominent divider with Unicode box-drawing characters.
Step 6: enhanced backticks
The problem: Inline code wrapped in backticks should stand out more.
The solution: Modify the colour definition itself:
[color]
backticks = '\x1b[38;5;214m\x1b[1m'
Then remove the backticks with a substitution:
remove-backticks = ['`', '', False]
This two-step approach colours backticked text in bold orange (214), then removes the backtick characters themselves.
Substitution execution order
Substitutions run alphabetically by name. This matters when one substitution depends on another's output. Our current order:
format-comments-no-hash- Processes inline commentsformat-standalone-comments- Processes standalone comment linesmarkdown-bold- Converts markdown boldremove-backticks- Strips backtick charactersseparator-dash- Converts---to decorative linesseparator-h2- Adds underlines to H2 headingsseparator-private- Converts PRIVATE marker
Tip
The complete configuration
Here's the final egrc:
[eg-config]
custom-dir = ~/dotfiles/eg/custom
examples-dir = ~/dotfiles/_private/eg/examples
pager-cmd = 'bat -l markdown --color=never --paging=always --style=plain'
squeeze = false
[color]
pound = '\x1b[38;5;245m' # Grey for # symbols
code = '\x1b[38;5;143m' # Yellow-green for code
backticks = '\x1b[38;5;214m\x1b[1m' # Bold orange for inline code
[substitutions]
format-comments-no-hash = ['(\S +)#( +)(.*?)$', '\\1\\2\x1b[3m\x1b[38;5;245m\\3\x1b[0m', True]
format-standalone-comments = ['^(\\s+)\x1b\\[36m\x1b\\[1m\x1b\\[0m\x1b\\[38;5;143m# ([^\x1b]+)\x1b\\[0m$', '\\1\x1b[3m\x1b[38;5;245m\\2\x1b[0m', True]
markdown-bold = ['\*\*([^*]+)\*\*', '\x1b[1m\\1\x1b[0m', True]
remove-backticks = ['`', '', False]
separator-dash = ['^---$', '\x1b[38;5;245m···········································································\x1b[0m', True]
separator-h2 = ['^\x1b\\[38;5;245m(##)\x1b\\[0m\x1b\\[31m\x1b\\[1m([^\x1b]+)\x1b\\[0m$', '\x1b[38;5;245m\\1\x1b[0m\x1b[31m\x1b[1m\\2\x1b[0m\n\x1b[38;5;245m···········································································\x1b[0m', True]
separator-private = ['^PRIVATE$', '\n\n\x1b[38;5;245m╾───────────────────────────────────────────────────────╼ PRIVATE ╾───────────────────────────────────────────────────────╼\x1b[0m\n', True]
Design principles
The resulting output embodies several deliberate choices:
Visual hierarchy through colour
- Grey (245) for secondary information (comments, separators)
- Yellow-green (143) for primary content (commands)
- Orange (214) for emphasis (inline code)
- Red bold for section headings
Typography for scannability
- Italics distinguish comments from commands
- Bold highlights important terms
- Underlines create clear section breaks
Noise reduction
- Hash symbols removed from comments
- Backticks removed from inline code
- Clean, uncluttered presentation
Semantic consistency
- Comments always appear in italic grey
- Code blocks maintain consistent colouring
- Separators use the same visual language
Pattern reference
For creating your own substitutions, here are the common patterns from our configuration:
Matching escape sequences:
\x1b\\[38;5;NNNm # 256-colour N
\x1b\\[0m # Reset
\x1b\\[1m # Bold
\x1b\\[3m # Italic
Capturing text between escapes:
([^\x1b]+) # Any text until next escape sequence
Common capture patterns:
(\\s+) # Whitespace
(\S+) # Non-whitespace
(.*?) # Any text (non-greedy)
Conclusion
What began as a quest for better comment styling evolved into a comprehensive customisation of eg's output. The key insights:
- Substitutions operate on colourised text, not raw markdown
- Use
cat -vto reveal the actual text structure - Alphabetical naming controls execution order
- Strategic colour choices create visual hierarchy
The result is a documentation system that's both highly functional and visually refined—proving that command-line tools can be as carefully designed as any GUI application.
For those wanting to dive deeper into the technical aspects of eg substitutions, including the complete processing pipeline and advanced pattern-matching techniques, a comprehensive guide will follow in a separate article.