#![cfg_attr(feature = "document-features", doc = "# Features")]
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
use egui::{self, Id};
mod parsers;
pub use egui_commonmark_backend::RenderHtmlFn;
pub use egui_commonmark_backend::RenderMathFn;
pub use egui_commonmark_backend::alerts::{Alert, AlertBundle};
pub use egui_commonmark_backend::misc::CommonMarkCache;
#[cfg(feature = "better_syntax_highlighting")]
pub use egui_commonmark_backend::syntect;
#[cfg(feature = "macros")]
pub use egui_commonmark_macros::*;
#[cfg(feature = "macros")]
#[doc(hidden)]
pub use egui_commonmark_backend;
use egui_commonmark_backend::*;
#[derive(Debug, Default)]
pub struct CommonMarkViewer<'f> {
options: CommonMarkOptions<'f>,
}
impl<'f> CommonMarkViewer<'f> {
pub fn new() -> Self {
Self::default()
}
pub fn indentation_spaces(mut self, spaces: usize) -> Self {
self.options.indentation_spaces = spaces;
self
}
pub fn max_image_width(mut self, width: Option<usize>) -> Self {
self.options.max_image_width = width;
self
}
pub fn default_width(mut self, width: Option<usize>) -> Self {
self.options.default_width = width;
self
}
pub fn show_alt_text_on_hover(mut self, show: bool) -> Self {
self.options.show_alt_text_on_hover = show;
self
}
pub fn default_implicit_uri_scheme<S: Into<String>>(mut self, scheme: S) -> Self {
self.options.default_implicit_uri_scheme = scheme.into();
self
}
pub fn explicit_image_uri_scheme(mut self, use_explicit: bool) -> Self {
self.options.use_explicit_uri_scheme = use_explicit;
self
}
#[cfg(feature = "better_syntax_highlighting")]
pub fn syntax_theme_light<S: Into<String>>(mut self, theme: S) -> Self {
self.options.theme_light = theme.into();
self
}
#[cfg(feature = "better_syntax_highlighting")]
pub fn syntax_theme_dark<S: Into<String>>(mut self, theme: S) -> Self {
self.options.theme_dark = theme.into();
self
}
pub fn alerts(mut self, alerts: AlertBundle) -> Self {
self.options.alerts = alerts;
self
}
pub fn render_math_fn(mut self, func: Option<&'f RenderMathFn>) -> Self {
self.options.math_fn = func;
self
}
pub fn render_html_fn(mut self, func: Option<&'f RenderHtmlFn>) -> Self {
self.options.html_fn = func;
self
}
pub fn show(
self,
ui: &mut egui::Ui,
cache: &mut CommonMarkCache,
text: &str,
) -> egui::InnerResponse<()> {
egui_commonmark_backend::prepare_show(cache, ui.ctx());
let (response, _) = parsers::pulldown::CommonMarkViewerInternal::new().show(
ui,
cache,
&self.options,
text,
None,
);
response
}
pub fn show_mut(
mut self,
ui: &mut egui::Ui,
cache: &mut CommonMarkCache,
text: &mut String,
) -> egui::InnerResponse<()> {
self.options.mutable = true;
egui_commonmark_backend::prepare_show(cache, ui.ctx());
let (mut inner_response, checkmark_events) =
parsers::pulldown::CommonMarkViewerInternal::new().show(
ui,
cache,
&self.options,
text,
None,
);
for ev in checkmark_events {
if ev.checked {
text.replace_range(ev.span, "[x]")
} else {
text.replace_range(ev.span, "[ ]")
}
inner_response.response.mark_changed();
}
inner_response
}
#[doc(hidden)] #[cfg(feature = "pulldown_cmark")]
pub fn show_scrollable(
self,
source_id: impl std::hash::Hash,
ui: &mut egui::Ui,
cache: &mut CommonMarkCache,
text: &str,
) {
egui_commonmark_backend::prepare_show(cache, ui.ctx());
parsers::pulldown::CommonMarkViewerInternal::new().show_scrollable(
Id::new(source_id),
ui,
cache,
&self.options,
text,
);
}
}
pub(crate) struct ListLevel {
current_number: Option<u64>,
}
#[derive(Default)]
pub(crate) struct List {
items: Vec<ListLevel>,
has_list_begun: bool,
}
impl List {
pub fn start_level_with_number(&mut self, start_number: u64) {
self.items.push(ListLevel {
current_number: Some(start_number),
});
}
pub fn start_level_without_number(&mut self) {
self.items.push(ListLevel {
current_number: None,
});
}
pub fn is_inside_a_list(&self) -> bool {
!self.items.is_empty()
}
pub fn is_last_level(&self) -> bool {
self.items.len() == 1
}
pub fn start_item(&mut self, ui: &mut egui::Ui, options: &CommonMarkOptions) {
if self.has_list_begun {
newline(ui);
} else {
self.has_list_begun = true;
}
let len = self.items.len();
if let Some(item) = self.items.last_mut() {
ui.label(" ".repeat((len - 1) * options.indentation_spaces));
if let Some(number) = &mut item.current_number {
number_point(ui, &number.to_string());
*number += 1;
} else if len > 1 {
bullet_point_hollow(ui);
} else {
bullet_point(ui);
}
} else {
unreachable!();
}
ui.add_space(4.0);
}
pub fn end_level(&mut self, ui: &mut egui::Ui, insert_newline: bool) {
self.items.pop();
if self.items.is_empty() && insert_newline {
newline(ui);
}
}
}