Expand description
Pretty printer for non-overlapping code spans.
This crate aids you in creating output like the following, both for the terminal (ANSI) as well as for the web (HTML):
╭─[fac.lisp] │ 1 │ (defun factorial (n) (if (zerop n) 1 ┆ ────┬──── ▲ ┆ │ │ ┆ ╰───────────────────────── this function ... ┆ ╭──────────────────────╯ 2 │ │ (* n (factorial (1- n))))) ┆ │ ▲ ┬ ┆ │ │ │ ┆ ╰─────────────────────────────────┴─── ... is defined by this ┆ │ ┆ ╰─ (and here is EOF) ──╯
This example has been created with cargo run --example example -- --html
.
To see its console output, run cargo run --example example
.
§Usage
Suppose that we have a source file and a list of byte ranges that we want to annotate. For example:
let src = r#"if true { 42 } else { "42" }"#;
let labels = [
(8..14, "this is of type Nat"),
(20..28, "this is of type String"),
];
First, we have to create a LineIndex
.
This splits the source into lines, so that further functions can
quickly find in which line a byte is situated.
use codesnake::LineIndex;
let idx = LineIndex::new(src);
Next, we create a code Block
from our index and the Label
s:
use codesnake::{Block, Label};
let block = Block::new(&idx, labels.map(|(range, text)| Label::new(range).with_text(text))).unwrap();
This will fail if your labels refer to bytes outside the range of your source.
Finally, we can print our code block:
use codesnake::CodeWidth;
let block = block.map_code(|c| CodeWidth::new(c, c.len()));
// yield " ╭─[main.rs]"
println!("{}{}", block.prologue(), "[main.rs]");
print!("{block}");
// yield "──╯"
println!("{}", block.epilogue());
§Colors
To color the output on a terminal, you can use a crate like yansi
.
This allows you to color the snakes of a label as follows:
use codesnake::Label;
use yansi::Paint;
let label = Label::new(range).with_text(text).with_style(|s| s.red().to_string());
For HTML, you can use something like:
use codesnake::Label;
let label = Label::new(range).with_text(text).with_style(|s| {
format!("<span style=\"color:red\">{s}</span>")
});