Crate codesnake

Source
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 Labels:

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>")
});

Structs§

Block
Sequence of lines, containing code C and (label) text T.
CodeWidth
Piece of code together with its display width.
Epilogue
Line that succeeds a block.
Label
Code label with text and style.
LineIndex
Associate byte offsets with line numbers.
Prologue
Line that precedes a block.