1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
#![warn(
rust_2018_idioms,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unused_qualifications
)]
#![forbid(unsafe_code)]
//! A library to render snippets of source code with annotations.
//! It is meant to be used as a building block for compiler diagnostics.
//!
//! # Example
//!
//! ```
//! // Some source code
//! let source = indoc::indoc! {r#"
//! fn main() {
//! println!("Hello, world!");
//! }
//! "#};
//!
//! // Create the snippet
//! let snippet = sourceannot::SourceSnippet::build_from_utf8(1, source.as_bytes(), 4);
//!
//! // Styles are generic over the type of the metadata that accompanies each
//! // chunk of rendered text. In this example, we will use the following enum:
//! #[derive(Copy, Clone, Debug, PartialEq, Eq)]
//! enum Color {
//! Default,
//! Red,
//! Green,
//! Blue,
//! }
//! // If do not you need this per-chunk metadata, you can use `()` instead.
//!
//! // Define the styles
//! // Use Unicode box drawing characters
//! let main_style = sourceannot::MainStyle {
//! margin: Some(sourceannot::MarginStyle {
//! line_char: '│',
//! dot_char: '·',
//! meta: Color::Blue,
//! }),
//! horizontal_char: '─',
//! vertical_char: '│',
//! top_vertical_char: '╭',
//! top_corner_char: '╭',
//! bottom_corner_char: '╰',
//! spaces_meta: Color::Default,
//! text_normal_meta: Color::Default,
//! text_alt_meta: Color::Default,
//! };
//!
//! // Uou can use a different style for each annotation, but in
//! // this example we will use the same style for all of them.
//! let annot_style = sourceannot::AnnotStyle {
//! caret: '^',
//! text_normal_meta: Color::Red,
//! text_alt_meta: Color::Red,
//! line_meta: Color::Red,
//! };
//!
//! // Create the annotations
//! let mut annotations = sourceannot::Annotations::new(&snippet, main_style);
//!
//! annotations.add_annotation(
//! 0..44,
//! annot_style,
//! vec![("this is the `main` function".into(), Color::Red)],
//! );
//! annotations.add_annotation(
//! 16..24,
//! annot_style,
//! vec![("this is a macro invocation".into(), Color::Red)],
//! );
//!
//! // Render the snippet with annotations
//! let max_line_no_width = annotations.max_line_no_width();
//! let rendered = annotations.render(max_line_no_width, 0, 0);
//!
//! // `rendered` is a `Vec<(String, Color)>`, which you could print with
//! // your favorite terminal coloring library. In this example, we will
//! // ignore the colors.
//!
//! for (chunk, _) in rendered.iter() {
//! eprint!("{chunk}");
//! }
//!
//! # let rendered = rendered.iter().map(|(s, _)| s.as_str()).collect::<String>();
//! # assert_eq!(
//! # rendered,
//! # indoc::indoc! {"
//! # 1 │ ╭ fn main() {
//! # 2 │ │ println!(\"Hello, world!\");
//! # │ │ ^^^^^^^^ this is a macro invocation
//! # 3 │ │ }
//! # │ ╰─^ this is the `main` function
//! # "},
//! # );
//! ```
//!
//! The output will look like this:
//!
//! ```text
//! 1 │ ╭ fn main() {
//! 2 │ │ println!(\"Hello, world!\");
//! │ │ ^^^^^^^^ this is a macro invocation
//! 3 │ │ }
//! │ ╰─^ this is the `main` function
//! ```
mod annots;
mod range_set;
mod snippet;
pub use annots::Annotations;
pub use snippet::SourceSnippet;
/// The general style of an annotated snippet.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MainStyle<M> {
/// The style of the margin.
///
/// If `None`, there will not be any margin at all.
pub margin: Option<MarginStyle<M>>,
/// Character used to draw horizontal lines of multi-line annotations.
pub horizontal_char: char,
/// Character used to draw vertical lines of multi-line annotations.
pub vertical_char: char,
/// Character used to draw the top corner of multi-line annotations
/// that start at the first column.
pub top_vertical_char: char,
/// Character used to draw the top corner of multi-line annotations.
pub top_corner_char: char,
/// Character used to draw the bottom corner of multi-line annotations.
pub bottom_corner_char: char,
/// Metadata that accompanies spaces.
pub spaces_meta: M,
/// Metadata that accompanies unannotated normal text.
pub text_normal_meta: M,
/// Metadata that accompanies unannotated alternative text.
pub text_alt_meta: M,
}
/// The style of the margin of an annotated snippet.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MarginStyle<M> {
/// Character used to draw the vertical separator of the margin.
pub line_char: char,
/// Character used to draw discontinuities in the vertical separator
/// of the margin durin.
pub dot_char: char,
/// Metadata that accompanies margin characters.
pub meta: M,
}
/// The style of a concrete annotation.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct AnnotStyle<M> {
/// Caret character used to point to the annotated text.
pub caret: char,
/// Metadata that accompanies annotated normal text.
pub text_normal_meta: M,
/// Metadata that accompanies annotated alternative text.
pub text_alt_meta: M,
/// Metadata that accompanies annotation drawings.
pub line_meta: M,
}