use std::{
collections::HashMap,
hash::{DefaultHasher, Hash, Hasher},
};
use chart::Chart;
use gpui::{AnyElement, App, Global, MouseButton, SharedString, Window, div, prelude::*, px};
use theme::Theme;
use crate::render::{copy_button, fence_band, fence_panel};
pub const SELECTOR: &str = "chart";
const CACHED: usize = 64;
const HEIGHT: f32 = 260.0;
#[derive(Default)]
struct Cache(HashMap<u64, Option<Chart>>);
impl Global for Cache {}
fn parsed(key: u64, code: &str, parse: fn(&str) -> Option<Chart>, cx: &mut App) -> Option<Chart> {
let cache = &mut cx.default_global::<Cache>().0;
if let Some(chart) = cache.get(&key) {
return chart.clone();
}
if cache.len() >= CACHED {
cache.clear();
}
let chart = parse(code);
cache.insert(key, chart.clone());
chart
}
pub(crate) fn render(
language: &'static str,
code: &str,
parse: fn(&str) -> Option<Chart>,
height: Option<u32>,
window: &mut Window,
cx: &mut App,
) -> Option<AnyElement> {
let mut hasher = DefaultHasher::new();
(language, code).hash(&mut hasher);
let key = hasher.finish();
let chart = parsed(key, code, parse, cx)?;
let theme = Theme::of(cx).clone();
let id = SharedString::from(format!("md-chart-{key:x}"));
let body = div()
.id(id.clone())
.debug_selector(|| SELECTOR.into())
.w_full()
.map(|el| match height {
Some(_) => el.flex_1().min_h_0(),
None => el.h(px(HEIGHT)),
})
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
.child(chart::view(id, chart).size_full());
Some(
fence_panel(&theme)
.when_some(height, |el, height| {
el.h(px(height as f32)).flex().flex_col()
})
.child(
fence_band(&theme)
.flex_none()
.text_color(theme.text_muted)
.child(language),
)
.child(body)
.child(copy_button(code, key as usize, &theme, window, cx))
.into_any_element(),
)
}