Skip to main content

vtcode_core/ui/
markdown.rs

1use crate::config::loader::SyntaxHighlightingConfig;
2use crate::ui::theme::{self, ThemeStyles};
3use anstyle::Style;
4
5// When TUI is enabled, use vtcode-ui's richer types (with internal methods).
6#[cfg(feature = "tui")]
7pub use vtcode_ui::tui::ui::markdown::{
8    HighlightedSegment, MarkdownLine, MarkdownSegment, RenderMarkdownOptions, highlight_code_to_ansi,
9    highlight_code_to_segments, highlight_line_for_diff,
10};
11
12// When headless, use the plain data types from commons.
13#[cfg(not(feature = "tui"))]
14pub use vtcode_commons::ui_protocol::{HighlightedSegment, MarkdownLine, MarkdownSegment, RenderMarkdownOptions};
15
16/// Highlight a code block to an ANSI-escaped string (headless passthrough).
17#[cfg(not(feature = "tui"))]
18pub fn highlight_code_to_ansi(code: &str, _language: Option<&str>, _theme: &str) -> String {
19    code.to_string()
20}
21
22/// Highlight a code block into styled segments (headless passthrough).
23#[cfg(not(feature = "tui"))]
24pub fn highlight_code_to_segments(code: &str, _language: Option<&str>, _theme: &str) -> Vec<HighlightedSegment> {
25    vec![HighlightedSegment { style: Style::default(), text: code.to_string() }]
26}
27
28/// Highlight a single line for diff display (headless passthrough).
29#[cfg(not(feature = "tui"))]
30pub fn highlight_line_for_diff(line: &str, _language: Option<&str>) -> Option<Vec<(Style, String)>> {
31    Some(vec![(Style::default(), line.to_string())])
32}
33
34// ── Markdown rendering ──────────────────────────────────────────────────────
35
36/// Render markdown source into styled lines using default rendering options.
37pub fn render_markdown_to_lines(
38    source: &str,
39    base_style: Style,
40    theme_styles: &ThemeStyles,
41    highlight_config: Option<&SyntaxHighlightingConfig>,
42) -> Vec<MarkdownLine> {
43    render_markdown_to_lines_with_options(
44        source,
45        base_style,
46        theme_styles,
47        highlight_config,
48        RenderMarkdownOptions::default(),
49    )
50}
51
52/// Render markdown source into styled lines with custom rendering options (TUI build).
53#[cfg(feature = "tui")]
54pub fn render_markdown_to_lines_with_options(
55    source: &str,
56    base_style: Style,
57    theme_styles: &ThemeStyles,
58    highlight_config: Option<&SyntaxHighlightingConfig>,
59    render_options: RenderMarkdownOptions,
60) -> Vec<MarkdownLine> {
61    let tui_theme_styles = crate::ui::tui_compat::tui_theme_styles_from_core(theme_styles);
62    let tui_highlight_cfg = highlight_config.map(|cfg| vtcode_ui::tui::TuiSyntaxHighlightingConfig {
63        enabled: cfg.enabled,
64        theme: cfg.theme.clone(),
65        cache_themes: cfg.cache_themes,
66        max_file_size_mb: cfg.max_file_size_mb,
67        enabled_languages: cfg.enabled_languages.clone(),
68        highlight_timeout_ms: cfg.highlight_timeout_ms,
69    });
70    vtcode_ui::tui::ui::markdown::render_markdown_to_lines_with_options(
71        source,
72        base_style,
73        &tui_theme_styles,
74        tui_highlight_cfg.as_ref(),
75        render_options,
76    )
77}
78
79/// Render markdown source into styled lines (headless passthrough, no syntax highlighting).
80#[cfg(not(feature = "tui"))]
81pub fn render_markdown_to_lines_with_options(
82    source: &str,
83    base_style: Style,
84    _theme_styles: &ThemeStyles,
85    _highlight_config: Option<&SyntaxHighlightingConfig>,
86    _render_options: RenderMarkdownOptions,
87) -> Vec<MarkdownLine> {
88    let mut lines: Vec<MarkdownLine> = source
89        .lines()
90        .map(|line| MarkdownLine {
91            segments: if line.is_empty() {
92                Vec::new()
93            } else {
94                vec![MarkdownSegment {
95                    style: base_style,
96                    text: line.to_string(),
97                    link_target: None,
98                }]
99            },
100        })
101        .collect();
102    if lines.is_empty() {
103        lines.push(MarkdownLine::default());
104    }
105    lines
106}
107
108/// Render markdown source using the currently active theme styles.
109pub fn render_markdown(source: &str) -> Vec<MarkdownLine> {
110    let styles = theme::active_styles();
111    render_markdown_to_lines(source, Style::default(), &styles, None)
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn facade_renders_markdown() {
120        let lines = render_markdown("# Heading");
121        assert!(!lines.is_empty());
122    }
123}