miden-debug 0.17.0

An interactive debugger for Miden VM programs
Documentation
use std::string::String;

use miden_assembly_syntax::diagnostics::miette::MietteSpanContents;

use super::*;

#[test]
fn fallback_selection_preserves_utf8_and_excludes_trailing_newlines() {
    let source = MietteSpanContents::new(b"text", (0, 4).into(), 0, 0, 1);
    let mut state = NoopHighlighter.start_highlighter_state(&source);
    assert_eq!(state.highlight_line(Cow::Borrowed("text")), vec![Span::raw("text")]);
    let style = Style::new().fg(Color::Red).add_modifier(Modifier::BOLD);
    for (line, selection, expected) in [
        ("aéz\n", 2..3, ["a", "é", "z"]),
        ("abc\n", 1..99, ["a", "bc", ""]),
        ("abc", 99..100, ["abc", "", ""]),
        ("abc", Range { start: 2, end: 1 }, ["ab", "", "c"]),
    ] {
        let spans = state.highlight_line_with_selection(Cow::Borrowed(line), selection, style);
        assert_eq!(spans.iter().map(|span| span.content.as_ref()).collect::<Vec<_>>(), expected);
        assert_eq!(spans[1].style, style);
        assert_eq!(spans[0].style, Style::default());
        assert_eq!(spans[2].style, Style::default());
    }
}

#[test]
fn syntax_detection_uses_language_extension_and_shebang() {
    let highlighter = SyntectHighlighter::default();
    let source =
        MietteSpanContents::new(b"fn main() {}", (0, 12).into(), 0, 0, 1).with_language("Rust");
    assert_eq!(highlighter.detect_syntax(&source).unwrap().name, "Rust");
    let named =
        MietteSpanContents::new_named("main.rs".into(), b"fn main() {}", (0, 12).into(), 0, 0, 1);
    assert_eq!(highlighter.detect_syntax(&named).unwrap().name, "Rust");
    let shebang = MietteSpanContents::new(b"#!/usr/bin/env python\n", (0, 22).into(), 0, 0, 1);
    assert_eq!(highlighter.detect_syntax(&shebang).unwrap().name, "Python");
    let invalid = MietteSpanContents::new(&[255], (0, 1).into(), 0, 0, 1);
    assert!(highlighter.detect_syntax(&invalid).is_none());
    let unknown = MietteSpanContents::new(b"text", (0, 4).into(), 0, 0, 1).with_language("unknown");
    let mut fallback = highlighter.start_highlighter_state(&unknown);
    assert_eq!(fallback.highlight_line(Cow::Borrowed("text")), vec![Span::raw("text")]);
    let mut state = highlighter.start_highlighter_state(&source);
    let line = "fn main() { let answer = 42; }";
    let spans = state.highlight_line(Cow::Borrowed(line));
    assert_eq!(spans.iter().map(|span| span.content.as_ref()).collect::<String>(), line);
    assert!(spans.iter().any(|span| span.style.fg.is_some()));
    let spans = state.highlight_line_with_selection(
        Cow::Borrowed(line),
        3..7,
        Style::new().fg(Color::Red).bg(Color::Black).add_modifier(Modifier::BOLD),
    );
    assert_eq!(spans.iter().map(|span| span.content.as_ref()).collect::<String>(), line);
    assert!(
        spans
            .iter()
            .any(|span| span.content == "main" && span.style.add_modifier.contains(Modifier::BOLD))
    );
}

#[test]
fn style_conversion_handles_transparency_blending_and_font_modifiers() {
    let transparent = syntax::Color {
        r: 0,
        g: 0,
        b: 0,
        a: 0,
    };
    let background = syntax::Color {
        r: 0,
        g: 100,
        b: 200,
        a: 255,
    };
    let foreground = syntax::Color {
        r: 200,
        g: 100,
        b: 0,
        a: 128,
    };
    let modifiers = Modifier::BOLD | Modifier::ITALIC | Modifier::UNDERLINED;
    let font_style = convert_to_font_style(modifiers | Modifier::REVERSED);
    assert_eq!(convert_font_style(font_style), modifiers);
    assert!(convert_to_font_style(Modifier::empty()).is_empty());
    let style = syntax::Style {
        foreground,
        background,
        font_style,
    };
    assert_eq!(
        convert_style(style, true),
        Style::new()
            .fg(Color::Rgb(100, 100, 99))
            .bg(Color::Rgb(0, 100, 200))
            .add_modifier(modifiers)
    );
    assert_eq!(
        convert_style(style, false),
        Style::new().fg(Color::Rgb(200, 100, 0)).add_modifier(modifiers)
    );
    let opaque = syntax::Style {
        foreground: background,
        ..style
    };
    assert_eq!(blend_fg_color(opaque), Color::Rgb(0, 100, 200));
    let empty = syntax::Style {
        foreground: transparent,
        background: transparent,
        font_style: syntax::FontStyle::empty(),
    };
    assert_eq!(convert_style(empty, true), Style::new());
    assert_eq!(convert_to_syntect_style(Style::new(), false), empty);
    let converted = convert_to_syntect_style(
        Style::new().fg(Color::Red).bg(Color::Blue).add_modifier(modifiers),
        true,
    );
    assert_eq!(
        convert_style(converted, true),
        Style::new()
            .fg(Color::Rgb(128, 0, 0))
            .bg(Color::Rgb(0, 0, 128))
            .add_modifier(modifiers)
    );
}

#[test]
fn ansi_palette_maps_named_and_indexed_colors_to_the_same_rgb() {
    for (index, named, rgb) in [
        (0, Color::Black, [0, 0, 0]),
        (1, Color::Red, [128, 0, 0]),
        (2, Color::Green, [0, 128, 0]),
        (3, Color::Yellow, [128, 128, 0]),
        (4, Color::Blue, [0, 0, 128]),
        (5, Color::Magenta, [128, 0, 128]),
        (6, Color::Cyan, [0, 128, 128]),
        (7, Color::Gray, [192, 192, 192]),
        (8, Color::DarkGray, [128, 128, 128]),
        (9, Color::LightRed, [255, 0, 0]),
        (10, Color::LightGreen, [0, 255, 0]),
        (11, Color::LightYellow, [255, 255, 0]),
        (12, Color::LightBlue, [0, 0, 255]),
        (13, Color::LightMagenta, [255, 0, 255]),
        (14, Color::LightCyan, [0, 255, 255]),
        (15, Color::White, [255, 255, 255]),
    ] {
        let expected = syntax::Color {
            r: rgb[0],
            g: rgb[1],
            b: rgb[2],
            a: 255,
        };
        assert_eq!(convert_to_syntect_color(named), expected);
        assert_eq!(convert_to_syntect_color(Color::Indexed(index)), expected);
    }
    assert_eq!(
        convert_color(convert_to_syntect_color(Color::Rgb(1, 2, 3))),
        Color::Rgb(1, 2, 3)
    );
}