Skip to content
SonicFieldPublic

About

A small, dependency-free Markdown viewer for the terminal

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

81 Commits

Folders and files

Repository files navigation

simple-md

simple-md is a small, read-only Markdown and reStructuredText viewer for the terminal. It renders documents with colour and text attributes in an alternate-screen pager, then restores the terminal when it exits.

When output is redirected, it writes rendered plain text without terminal control sequences and exits without waiting for input.

It is implemented in C and has no runtime dependencies beyond a POSIX-like system and a UTF-8 terminal with 256-colour support.

Build

make

The resulting executable is ./simple-md. CI is configured to build with both GCC and Clang.

To install it:

make install PREFIX="$HOME/.local"

PREFIX defaults to /usr/local; staged packaging is supported through DESTDIR.

Usage

simple-md README.md
simple-md PEP-0008.rst
simple-md < README.md
cat README.md | simple-md
simple-md --width=100 document.md
agent-command | simple-md

Options:

--width=COLUMNS  Override the detected terminal width (1–10000)
--pager=MODE     Select auto, always, or never (default: auto)
--no-color       Suppress colours and text attributes
--links=MODE     Emit OSC 8 links: auto, always, or never (default: auto)
--format=FORMAT  Parse as auto, markdown, or rst (default: auto)
-h, --help       Show usage
-V, --version    Show the version

Navigation:

Key Action
Up / Down Scroll one line
Page Up / Page Down Scroll one page
Home / End Jump to the beginning or end
Left / Right Pan wide tables and code blocks
/ Search forward
n Go to the next search match, wrapping at the end
N Go to the previous search match, wrapping at the beginning
o Open the heading outline; select with arrows or j/k
h / ? Show help
q / Escape Quit

In automatic mode, documents that fit in the terminal are printed directly; longer documents open in the interactive pager. Use --pager=always when you want interactive features for a short document, or --pager=never to retain the rendered output in terminal scrollback.

Setting the NO_COLOR environment variable has the same effect as --no-color. Pager navigation continues to use terminal control sequences; redirected output never contains them.

Markdown links use OSC 8 hyperlinks on interactive terminals. Use --links=never if a terminal does not support them, or --links=always to override automatic detection. Link targets containing terminal control bytes are rejected rather than emitted.

Search operates on rendered text rather than Markdown punctuation. It is ASCII case-insensitive; non-ASCII UTF-8 bytes are matched exactly. Search results wrap at either end of the document.

Markdown support

We believe the parser is compliant with CommonMark 0.31.2. This belief is based on all 652 examples in the official conformance corpus passing, rather than on an assumption that the implementation is correct. The corpus runs as part of make test; see CommonMark compliance for the evidence, scope, and limitations of the claim.

The supported core includes all six heading levels, setext headings, indented and fenced code blocks, reference links, images, autolinks, raw HTML, entities, and the full CommonMark emphasis and container-block rules.

The terminal renderer additionally provides:

  • Paragraph reflow and hard line breaks
  • Lightweight syntax highlighting for selected programming languages
  • GFM-style pipe tables with alignment and Unicode borders
  • Styled headings, emphasis, code, lists, blockquotes, links, and thematic breaks
  • CJK and emoji display widths
  • Bidirectional text rendering for RTL scripts

Raw HTML is displayed as source in the terminal. Images are represented by their alternative text and link target because a text terminal cannot display the bitmap itself. GFM pipe tables remain an opt-in parser extension used by the reader in addition to the CommonMark core.

Syntax highlighting

simple-md uses small built-in, line-oriented scanners rather than embedding a compiler parser or a large highlighting framework. This keeps the binary self-contained and makes highlighting fast enough for ephemeral Markdown, while accepting that it will not understand every construct in every language.

The supported code-fence labels are:

Family Fence labels
C c, h
C++ cpp, c++, cc, cxx, hpp
Java java
Rust rust, rs
JavaScript js, javascript
TypeScript ts, typescript
Python py, python
Pascal pas, pascal, delphi, honest
Bourne shells sh, bash, zsh, ksh, shell

C, C++, Java, and Rust share a C-family scanner with separate keyword and type tables. JavaScript and TypeScript likewise share a scanner. The Bourne-shell scanner covers their common visible syntax; it does not claim compatibility with unrelated shells such as Fish or PowerShell.

The highlighters colour common keywords, types, strings, numbers, comments, variables, and operators. They do not validate programs or cover every dialect-specific edge case. An unknown fence label still renders as a normal code block, simply without syntax colouring. This limited, graceful model is intentional: simple-md remains simple.

reStructuredText support

The built-in C parser targets useful terminal presentation rather than full Docutils or Sphinx compatibility. It supports ordinary sections, paragraphs, inline styling, embedded links, selected roles, lists, field and definition lists, literal and code blocks, directives, and source-aligned tables. Unknown roles and directives retain visible content where possible.

Files ending in .rst or .rest select this parser automatically. For stdin and unknown extensions, conservative content detection is used and ambiguous input remains Markdown. --format=rst and --format=markdown provide a deterministic override.

The exact supported subset, test strategy, and known limitations are recorded in docs/RST.md.

Tests

make test       # unit suite plus real-PTY CLI checks
make commonmark # all 652 normative CommonMark 0.31.2 examples
make sanitize   # unit tests under AddressSanitizer and UBSan
make analyze    # GCC static analysis; warnings fail the build

The terminal test starts the actual executable in a pseudo-terminal and checks both q and Escape exits, including alternate-screen entry and restoration. Python 3 is needed only for this PTY test. The analysis target requires GCC.

Architecture

input → parser → AST → styled layout → viewport → terminal
  • md_parse and rst_parse produce the shared md_ast block and inline tree.
  • md_render, md_table, and md_highlight build styled display lines.
  • md_viewport handles scrolling, clipping, panning, and BiDi ordering.
  • md_terminal owns raw mode, signal handling, terminal sizing, and key input.
  • term_style, unicode_width, and bidi are bundled support modules.

The extraction history and its reproducible checks are documented in docs/EXTRACTION.md.

Origin and license

simple-md was extracted from SonicField/nbs-framework. The filtered Git history retains the original authorship and commit messages.

Released under the MIT License. See LICENSE.

The bundled MD4C parser is also MIT-licensed; its license is retained in third_party/md4c/LICENSE.md. The CommonMark test corpus license is retained in tests/commonmark/LICENSE.md.

Release notes are in CHANGELOG.md.

About

A small, dependency-free Markdown viewer for the terminal

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages