1#[cfg(feature = "syntax-highlighting")]
4use syntect::easy::HighlightLines;
5#[cfg(feature = "syntax-highlighting")]
6use syntect::highlighting::ThemeSet;
7#[cfg(feature = "syntax-highlighting")]
8use syntect::parsing::SyntaxSet;
9#[cfg(feature = "syntax-highlighting")]
10use syntect::util::LinesWithEndings;
11
12use ratatui::style::{Color, Style};
13use ratatui::text::{Line, Span, Text};
14
15#[cfg(feature = "syntax-highlighting")]
17fn syntect_color_to_ratatui(color: syntect::highlighting::Color) -> Color {
18 Color::Rgb(color.r, color.g, color.b)
19}
20
21#[cfg(feature = "syntax-highlighting")]
23pub fn highlight_shell_script(code: &str) -> Text<'static> {
24 let ps = SyntaxSet::load_defaults_newlines();
25 let ts = ThemeSet::load_defaults();
26
27 let theme = &ts.themes["base16-ocean.dark"];
29
30 let syntax = ps
32 .find_syntax_by_extension("sh")
33 .or_else(|| ps.find_syntax_by_extension("bash"))
34 .or_else(|| ps.find_syntax_by_extension("zsh"))
35 .unwrap_or_else(|| ps.find_syntax_plain_text());
36
37 let mut highlighter = HighlightLines::new(syntax, theme);
38 let mut lines = Vec::new();
39
40 for line in LinesWithEndings::from(code) {
41 let ranges = highlighter.highlight_line(line, &ps).unwrap_or_default();
42
43 let mut spans = Vec::new();
44 for (style, text) in ranges {
45 let fg = syntect_color_to_ratatui(style.foreground);
46 let ratatui_style = Style::default().fg(fg);
47 spans.push(Span::styled(text.to_string(), ratatui_style));
48 }
49
50 lines.push(Line::from(spans));
51 }
52
53 Text::from(lines)
54}
55
56#[cfg(not(feature = "syntax-highlighting"))]
58pub fn highlight_shell_script(code: &str) -> Text<'static> {
59 Text::from(code.to_string())
60}
61
62pub fn highlight_shell_script_with_style(
64 code: &str,
65 dim: bool,
66) -> Text<'static> {
67 let text = highlight_shell_script(code);
68
69 let mut numbered_lines = Vec::new();
71 let line_count = text.lines.len();
72 let line_num_width = line_count.to_string().len().max(2); for (idx, line) in text.lines.into_iter().enumerate() {
75 let line_num = idx + 1;
76 let line_num_str = format!("{:>width$} ", line_num, width = line_num_width);
77
78 let line_num_span = Span::styled(
80 line_num_str,
81 Style::default()
82 .fg(Color::Rgb(100, 100, 100)) .add_modifier(if dim { ratatui::style::Modifier::DIM } else { ratatui::style::Modifier::empty() }),
84 );
85
86 let mut new_spans = vec![line_num_span];
88 new_spans.extend(line.spans.into_iter().map(|span| {
89 if dim {
90 Span::styled(span.content, span.style.add_modifier(ratatui::style::Modifier::DIM))
91 } else {
92 span
93 }
94 }));
95
96 numbered_lines.push(Line::from(new_spans));
97 }
98
99 Text::from(numbered_lines)
100}