Skip to main content

freya_markdown/
code_editor.rs

1use std::borrow::Cow;
2
3use freya_code_editor::prelude::{
4    CodeEditor,
5    CodeEditorData,
6    EditorLanguage,
7    Rope,
8};
9use freya_core::prelude::*;
10use torin::prelude::*;
11
12/// Maps a code block's language string to an [`EditorLanguage`], or `None` to skip highlighting.
13pub type LanguageResolver = Callback<String, Option<EditorLanguage>>;
14
15#[derive(PartialEq)]
16pub(crate) struct CodeBlockEditor {
17    code: NoArgCallback<Cow<'static, str>>,
18    language: Option<String>,
19    resolver: Option<LanguageResolver>,
20    font_size: f32,
21    font_family: Cow<'static, str>,
22    key: DiffKey,
23}
24
25impl CodeBlockEditor {
26    pub(crate) fn new(
27        code: impl Into<NoArgCallback<Cow<'static, str>>>,
28        language: Option<String>,
29        resolver: Option<LanguageResolver>,
30        font_size: f32,
31        font_family: Cow<'static, str>,
32    ) -> Self {
33        Self {
34            code: code.into(),
35            language,
36            resolver,
37            font_size,
38            font_family,
39            key: DiffKey::None,
40        }
41    }
42}
43
44impl KeyExt for CodeBlockEditor {
45    fn write_key(&mut self) -> &mut DiffKey {
46        &mut self.key
47    }
48}
49
50impl Component for CodeBlockEditor {
51    fn render(&self) -> impl IntoElement {
52        let code = self.code.clone();
53        let language = self.language.clone();
54        let resolver = self.resolver.clone();
55        let font_size = self.font_size;
56        let font_family = self.font_family.clone();
57
58        let a11y_id = use_a11y();
59
60        let editor = use_state({
61            let font_family = font_family.clone();
62            move || {
63                let code = code.call();
64                let language = language
65                    .zip(resolver)
66                    .and_then(|(language, resolve)| resolve.call(language));
67                let mut editor = CodeEditorData::new(Rope::from_str(&code), language);
68                editor.parse();
69                editor.measure(font_size, &font_family);
70                editor
71            }
72        });
73
74        let line_height = (font_size * 1.4).floor();
75        let lines = editor.read().rope.len_lines().max(1);
76
77        rect()
78            .width(Size::fill())
79            .height(Size::px(lines as f32 * line_height))
80            .corner_radius(6.)
81            .overflow(Overflow::Clip)
82            .child(
83                CodeEditor::new(editor, a11y_id)
84                    .read_only(true)
85                    .gutter(false)
86                    .show_whitespace(false)
87                    .font_size(font_size)
88                    .line_height(1.4)
89                    .font_family(font_family),
90            )
91    }
92
93    fn render_key(&self) -> DiffKey {
94        self.key.clone().or(self.default_key())
95    }
96}