• Stars
    star
    834
  • Rank 54,676 (Top 2 %)
  • Language
    Rust
  • License
    MIT License
  • Created over 5 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A library to display rich (Markdown) snippets and texts in a rust terminal application

MIT Latest Version docs Chat on Miaou

A CLI utilities library leveraging Markdown to format terminal rendering, allowing separation of structure, data and skin.

Based on crossterm so works on most terminals (even on windows).

text

The goal isn't to display any markdown text with its various extensions (a terminal isn't really fit for that). The goal is rather to improve the display of texts in a terminal application when we want both the text and the skin to be easily configured.

Termimad also includes a few utilities helping efficient managing of events and user input in a multithread application.

Wrapping, table balancing, and scrolling are essential features of Termimad.

A text or a table can be displayed in an a priori unknown part of the screen, scrollable if desired, with a dynamically discovered width.

For example this markdown:

|:-:|:-:|-
|**feature**|**supported**|**details**|
|-:|:-:|-
| tables | yes | pipe based, with or without alignments
| italic, bold | yes | star based |
| inline code | yes | `with backquotes` (it works in tables too)
| code bloc | yes |with tabs or code fences
| syntax coloring | no |
| crossed text |  ~~not yet~~ | wait... now it works `~~like this~~`
| horizontal rule | yes | Use 3 or more dashes (`---`)
| lists | yes|* unordered lists supported
|  | |* ordered lists *not* supported
| quotes |  yes |> What a wonderful time to be alive!
| links | no | (but your terminal already handles raw URLs)
|-

will give different results depending on the width:

table

table

table

Usage

[dependencies]
termimad = "0.20"

With the default skin:

termimad::print_inline("**some** *nested **style*** and `some(code)`");

or

print!("{}", termimad::inline("**some** *nested **style*** and `some(code)`"));

Result:

simple example

Inline snippets with a custom skin:

Inline snippets are one line or less.

let mut skin = MadSkin::default();
skin.bold.set_fg(Yellow);
skin.print_inline("*Hey* **World!** Here's `some(code)`");
skin.paragraph.set_fgbg(Magenta, rgb(30, 30, 40));
skin.italic.add_attr(Underlined);
println!("\nand now {}\n", skin.inline("a little *too much* **style!** (and `some(code)` too)"));

Result:

too much style

Texts

Texts can be several lines. Tables and code blocks are automatically aligned, justified and consistently wrapped.

skin.print_text("# title\n* a list item\n* another item");

Scrollable TextView in a raw terminal:

scrollable

The code for this example is in examples/scrollable. To read the whole text just do

cargo run --example scrollable

Templates

In order to separate the rendering format from the content, you may want to have some constant markdown and fill some placeholders with dynamic items.

The format! macro is not always a good solution for that because you may not be sure the content is free of characters which may mess the markdown.

A solution is to use one of the templating functions or macros.

A template is to markdown what a prepared statement is to SQL: interpreted once and preventing the content to be interpreted as parts of the structure.

Inline Templates

Example:

mad_print_inline!(
    &skin,
    "**$0 formula:** *$1*", // the markdown template, interpreted once
    "Disk",  // fills $0
    "2*π*r", // fills $1. Note that the stars don't mess the markdown
);

mad_print_inline

Main difference with using skin.print_inline(format!( ... )) to build some markdown and parse it:

  • the markdown parsing and template building are done only once (using once_cell internally)
  • the given values aren't interpreted as markdown fragments and don't impact the style
  • arguments can be omited, repeated, given in any order
  • no support for fmt parameters or arguments other than &str (in the current version)

Inline templates are especially convenient combined with automated expansion or ellipsis, for filling a field in a terminal application.

You'll find more examples and advice in the inline-template example.

Text Template

When you want to fill a multi-line area, for example the help page of your terminal application, you may use a text template.

A template defines placeholders as ${name} which you may fill when using it.

For example

let text_template = TextTemplate::from(r#"
    # ${app-name} v${app-version}
    It is *very* ${adj}.
    "#);
let mut expander = text_template.expander();
expander
    .set("app-name", "MyApp")
    .set("adj", "pretty")
    .set("app-version", "42.5.3");
skin.print_expander(expander);

This would render like this:

text_template_01

The values you set with set aren't parsed as markdown, so they may freely contain stars or backquotes.

A template is reusable and can be defined from a text content or any string.

By using sub-templates, you may handle repetitions. They're handy for lists or tables.

For example

let text_template = TextTemplate::from(r#"
    |:-:|:-:|:-:|
    |**name**|**path**|**description**|
    |-:|:-:|:-|
    ${module-rows
    |**${module-name}**|`${app-version}/${module-key}`|${module-description}|
    }
    |-|-|-|
    "#);
let mut expander = text_template.expander();
expander
    .set("app-version", "2");
expander.sub("module-rows")
    .set("module-name", "lazy-regex")
    .set("module-key", "lrex")
    .set("module-description", "eases regexes");
expander.sub("module-rows")
    .set("module-name", "termimad")
    .set("module-key", "tmd")
    .set_md("module-description", "do things on *terminal*");
skin.print_expander(expander);

to get

text_template_02

On this example, you can note that

  • sub("module-rows") gets an expander for the sub template called module-rows
  • set_md can be used when you want to insert not just a raw uninterpreted string but some inline markdown.
  • you don't need to fill global placeholders again (here ${app-version}).

If you want to insert a block of code, you may use set_lines which applies the line style to all passed lines.

For example

let text_template = TextTemplate::from(r#"
    ## Example of a code block
        ${some-function}
    "#);
let mut expander = text_template.expander();
expander.set_lines("some-function", r#"
    fun test(a rational) {
    irate(a)
    }
    "#);
skin.print_expander(expander);

to get

text_template_03

You'll find more text template functions in the documentation and in the example (run cargo run --example text-template).

You may also be interested in OwningTemplateExpander: an alternative expander owning the values which may be handy when you build them while iterating in sub templates.

Asking questions

A frequent need in CLI apps is to ask the user to select an answer. The Question API and the ask! macros cover most basic needs.

Here's an example of asking with a default choice (that you get by hitting enter) and a returned value:

let choice = ask!(skin, "Do you want to drink something ?", ('n') {
    ('w', "I'd like some **w**ater, please.") => {
        mad_print_inline!(skin, "*Wait a minute, please, I'll fetch some.*\n");
        Some("water")
    }
    ('b', "Could I get a **b**eer glass ?") => {
        mad_print_inline!(skin, "We have no glass, so here's a *bottle*.\n");
        Some("beer")
    }
    ('n', "*No*, thank you.") => {
        None
    }
});
dbg!(choice);

ask example

Advices to get started

  • Start by reading the examples (in /examples): they cover almost the whole API, including templates, how to use an alternate screen or scroll the page, etc.
  • The render-input-markdown example lets you type some markdown in a text area and see it rendered below
  • Be careful that some colors aren't displayable on all terminals. The default color set of your application should not include arbitrary RGB colors.
  • The event / event-source part of Termimad is currently tailored for a short number of applications. If you use it or want to use it, please come and tell me so that your needs are taken into account!
  • If your goal is to format some CLI application output, for example a few tables, have a look at lfs which is one of the simplest possible uses
  • If a feature is missing, or you don't know how to use some part, come and ping me on my chat during West European hours.

Open-source applications using termimad

  • broot is a file manager and uses termimad for its help screen, status information and event management

  • lfs is a linux utility displaying file systems. Termimad templates are used to show the data in tables

  • SafeCloset is a secret safe. Its TUI uses Termimad a lot, especially inputs

  • Rhit is a nginx log analyzer. Termimad templates are used to show the data in tables

  • bacon is a background Rust compiler. It uses Termimad for display and event management

  • lapin is a terminal game. It uses Termimad for display and event management

  • backdown is a file deduplicator. It uses Termimad to print on screen and ask questions

  • Humility is a debugger for embedded systems. It uses Termimad to print Markdown documentation with the humility doc subcommand

If you're the author of another application using Termimad, please tell me.

Crossterm compatibility

Crossterm is a 0.x library which means its API isn't frozen. And it does change sometimes so libraries based on Crossterm can't always use its last version.

Crossterm 0.23.x is reexported by Termimad 0.22.x so you don't have to declare the import yourself. You may use crossterm as termimad::crossterm.

More Repositories

1

broot

A new way to see and navigate directory trees : https://dystroy.org/broot
Rust
9,958
star
2

bacon

background rust code check
Rust
1,399
star
3

rhit

A nginx log explorer
Rust
806
star
4

dysk

A linux utility to get information on filesystems, like df but better
Rust
780
star
5

miaou

A chat server with OAuth2 authentication, persistent and searchable history, video and audio, markdown formatting, private and public rooms, stars, votes, embedded games, and many other features
JavaScript
536
star
6

lazy-regex

lazy static regular expressions checked at compile time
Rust
172
star
7

JSON.prune

A pruning version of JSON.stringify, allowing for example to stringify window or any big or recursive object
JavaScript
159
star
8

backdown

A deduplicator
Rust
94
star
9

safecloset

Cross-platform Secure TUI Secret Locker
Rust
81
star
10

hu.js

A very light JavaScript library for SVG
JavaScript
71
star
11

resc

A task orchestrator using redis, written in rust
Rust
63
star
12

clap-help

A more compact help renderer for clap terminal applications
Rust
56
star
13

whalespotter

Find those big fat files and folders
Rust
53
star
14

lapin

A game I built under direction of my kids
Rust
49
star
15

codesort

codesort sorts code
Rust
43
star
16

glassbench

A micro-benchmark framework to use with cargo bench
Rust
40
star
17

mazter

Mazes in your terminal
Rust
35
star
18

bet

Build and evaluate binary expression trees
Rust
33
star
19

tzdetect.js

A JavaScript library to detect the timezone using Moment.js
JavaScript
31
star
20

clima

A minimal viewer for Termimad
Rust
30
star
21

nvim-bacon

bacon's companion for neovim
Lua
29
star
22

deser-hjson

A Serde 1.0 compatible Rust deserializer for Hjson
Rust
27
star
23

minimad

Rust Markdown Parser
Rust
23
star
24

crokey

Rust
22
star
25

snow

Make it snow on your web site
JavaScript
21
star
26

coolor

tiny color conversion library for TUI application builders
Rust
17
star
27

wasm-tictactoe

Example of a 100% pure Rust, framework free, wasm application
Rust
15
star
28

lfs-core

rust crate to get information on mounted disks on linux
Rust
15
star
29

splitty

A Rust string splitter taking quotes into account
Rust
15
star
30

fediback

A Mastodon account backuper
Rust
14
star
31

Chrall

Extension Chrome pour Mounty Hall
JavaScript
14
star
32

print_key

an utility printing the key combinations you try on your terminal
Rust
14
star
33

JSON.parseMore

A not eval based JSON parser handling NaN and infinities
JavaScript
12
star
34

csv2svg

Rust
12
star
35

starry

A tool to collect github stars counts
Rust
12
star
36

terminal-light

Tells you whether the terminal is "dark" or "light"
Rust
11
star
37

patine

A minimalist color scheme for vim
Vim Script
11
star
38

SpaceBullet

JavaScript
11
star
39

terminal-clipboard

A cross-platform text only clipboard facade for terminal applications written in Rust
Rust
10
star
40

umask

A utility to deal with unix access permission modes
Rust
9
star
41

bounded-cache

A fast in memory cache forgetting the least recently accessed entry when the maximal number of entries is reached.
JavaScript
8
star
42

dys-config

Personal configuration files, for easier sharing between all my computers
Vim Script
7
star
43

squale

Small utility to scale images
Rust
6
star
44

miaou.gpt

A chat bot for Miaou, using the OpenAPI API
JavaScript
6
star
45

cli-log

a simple file logging facility, convenient for Rust terminal applications
Rust
5
star
46

skillrep

A small experiment at recomputing Stack Exchange reputation for evaluating skills
Go
5
star
47

braldop

Extensions du jeu Braldahim
JavaScript
5
star
48

keybindings-example

Rust
4
star
49

locmess

draws an histogram of the lengths of your LOC
Rust
4
star
50

bench-config-deserializers

A benchmark of Rust/serde deserializers on configuration files
Rust
4
star
51

anolog

access log anonymizer
Rust
3
star
52

termux-clipboard

Rust
3
star
53

yaml-patcher

An utility to change values in YAML files
Rust
3
star
54

char_reader

A buffered char reader for rust, not breaking on wild contents
Rust
3
star
55

kidding

Rust
2
star
56

strict

Rust
2
star
57

jacquerie

Similar to jQuery but without the burden of history
JavaScript
2
star
58

weblowercaser

A small program to change all file names and internal links in lowercase
Go
2
star
59

dom-doll

A nano Javascript lib to enchant the HTML DOM
JavaScript
2
star
60

mastodys-settings

stylesheet of the mastodon.dystroy.org Mastodon instance
CSS
2
star
61

secular

no diacr!
Rust
2
star
62

miaou.flying-toasters

No description or website provided
JavaScript
2
star
63

Canop

2
star
64

file-archive

A simple archiver storing files in a date based tree
JavaScript
1
star
65

dysp

Rust
1
star
66

albumin

Simple Hierarchical Album Generator
JavaScript
1
star
67

proc-status

a crate giving you information about a process, for example yours
Rust
1
star
68

groumf

Fast search and replace on strings or dom elements
JavaScript
1
star
69

domus

A DOM library that I hope to be able to forget soon enough (don't use it)
Rust
1
star
70

war-js

A repo for the SO JS ROOM GAME COMPETITION , and remake of Scratch W.A.R.
JavaScript
1
star
71

miaou.mountyhall

A plugin for miaou, dedicated to the MountyHall game
JavaScript
1
star
72

xterm-query

utility to query the terminal
Rust
1
star
73

byo-graphql

A simple "bring your own types and queries" GraphQL client
Rust
1
star
74

prompter

bash prompt designer
HTML
1
star
75

file-size

just a Rust function formatting file sizes in 4 chars
Rust
1
star
76

miaou.fireworks

A command to launch Fireworks in Miaou chat rooms
JavaScript
1
star
77

taliner.js

Is the caret on the first line of the textarea ? On the last line ?
HTML
1
star
78

canop.github.com

1
star
79

walk_pace

A simple evaluation of the cost of using rust's standard WalkDir
Rust
1
star