Skip to main content

netget/scripting/
highlight.rs

1//! Syntax highlighting for script code in logs
2
3use std::sync::OnceLock;
4use syntect::easy::HighlightLines;
5use syntect::highlighting::ThemeSet;
6use syntect::parsing::SyntaxSet;
7use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};
8
9static SYNTAXES: OnceLock<SyntaxSet> = OnceLock::new();
10static THEMES: OnceLock<ThemeSet> = OnceLock::new();
11
12/// Highlight script code for terminal output
13///
14/// Uses ANSI escape codes to add syntax highlighting to code.
15/// Falls back to plain text if highlighting fails.
16pub fn highlight_code(code: &str, language: &str) -> String {
17    // Load syntax definitions and theme
18    let ps = SYNTAXES.get_or_init(SyntaxSet::load_defaults_newlines);
19    let ts = THEMES.get_or_init(ThemeSet::load_defaults);
20
21    // Use a dark theme suitable for terminals
22    let theme = &ts.themes["base16-ocean.dark"];
23
24    // Find the syntax definition for the language
25    let syntax = ps
26        .find_syntax_by_token(language)
27        .unwrap_or_else(|| ps.find_syntax_plain_text());
28
29    // Highlight the code
30    let mut h = HighlightLines::new(syntax, theme);
31    let mut highlighted = String::new();
32
33    for line in LinesWithEndings::from(code) {
34        if let Ok(ranges) = h.highlight_line(line, ps) {
35            let escaped = as_24_bit_terminal_escaped(&ranges[..], false);
36            highlighted.push_str(&escaped);
37        } else {
38            // Fallback to plain text if highlighting fails
39            highlighted.push_str(line);
40        }
41    }
42
43    // Add reset code at the end to prevent color bleeding
44    highlighted.push_str("\x1b[0m");
45
46    highlighted
47}
48
49/// Format script code with a header and syntax highlighting
50///
51/// Returns a formatted, highlighted code block suitable for DEBUG logs
52pub fn format_script_for_log(code: &str, language: &str) -> String {
53    let highlighted = highlight_code(code, language);
54
55    format!(
56        "\n┌─────────────────────────────────────────────┐\n\
57         │ LLM-Generated {} Script                     \n\
58         └─────────────────────────────────────────────┘\n\
59         {}\n\
60         ─────────────────────────────────────────────────",
61        language.to_uppercase(),
62        highlighted
63    )
64}