From 8848291d5d25790b158a304cfd7408f720ebdc5c Mon Sep 17 00:00:00 2001
From: Erlend Walstad <96946613+lampsitter@users.noreply.github.com>
Date: Sat, 7 Sep 2024 20:29:14 +0200
Subject: [PATCH] Inline code blocks
---
CHANGELOG.md | 1 +
egui_commonmark/examples/macros.rs | 16 ++++--
.../examples/markdown/code-blocks.md | 3 ++
egui_commonmark/examples/mixing.rs | 3 ++
egui_commonmark/examples/scroll.rs | 2 +-
egui_commonmark/src/parsers/pulldown.rs | 33 +++++++-----
egui_commonmark_backend/src/lib.rs | 2 +-
egui_commonmark_backend/src/misc.rs | 36 +++++++++----
egui_commonmark_macros/src/generator.rs | 53 ++++++++++---------
9 files changed, 95 insertions(+), 54 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81a86c9..2050bce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
### Added
- Definition lists
+- Proper inline code block rendering
### Changed
diff --git a/egui_commonmark/examples/macros.rs b/egui_commonmark/examples/macros.rs
index 61d10be..59865d5 100644
--- a/egui_commonmark/examples/macros.rs
+++ b/egui_commonmark/examples/macros.rs
@@ -61,11 +61,17 @@ impl eframe::App for App {
commonmark!(ui, &mut self.cache, "------------");
- commonmark_str!(
- ui,
- &mut self.cache,
- "egui_commonmark/examples/markdown/tables.md"
- );
+ // The table will end up with the same id as the table in the hello_world file.
+ // Providing the id explicitly is annoying for all other widgets that are not tables
+ // so push_id must be used in this case.
+ ui.push_id("tables", |ui| {
+ commonmark_str!(
+ ui,
+ &mut self.cache,
+ "egui_commonmark/examples/markdown/tables.md"
+ );
+ });
+
commonmark!(ui, &mut self.cache, "------------");
commonmark_str!(
diff --git a/egui_commonmark/examples/markdown/code-blocks.md b/egui_commonmark/examples/markdown/code-blocks.md
index 91e0f22..d787865 100644
--- a/egui_commonmark/examples/markdown/code-blocks.md
+++ b/egui_commonmark/examples/markdown/code-blocks.md
@@ -26,4 +26,7 @@ image = { version = "0.24", default-features = false, features = ["png"] }
```
- Code blocks can be in lists too :)
+
More content...
+
+ Inline code blocks are supported if you for some reason need them
diff --git a/egui_commonmark/examples/mixing.rs b/egui_commonmark/examples/mixing.rs
index e81b2b1..3ea1395 100644
--- a/egui_commonmark/examples/mixing.rs
+++ b/egui_commonmark/examples/mixing.rs
@@ -54,6 +54,9 @@ let x = 3;
```
"#,
r#"
+ let x = 3;
+ "#,
+ r#"
A footnote [^F1]
[^F1]: The footnote"#,
diff --git a/egui_commonmark/examples/scroll.rs b/egui_commonmark/examples/scroll.rs
index 0dc50be..e75cb68 100644
--- a/egui_commonmark/examples/scroll.rs
+++ b/egui_commonmark/examples/scroll.rs
@@ -15,7 +15,7 @@ struct App {
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
let mut text = r#"# Commonmark Viewer Example
- This is a fairly large markdown file showcasing scroll.
+This is a fairly large markdown file showcasing scroll.
"#
.to_string();
diff --git a/egui_commonmark/src/parsers/pulldown.rs b/egui_commonmark/src/parsers/pulldown.rs
index 7afc7a9..d217515 100644
--- a/egui_commonmark/src/parsers/pulldown.rs
+++ b/egui_commonmark/src/parsers/pulldown.rs
@@ -71,7 +71,7 @@ pub struct CommonMarkViewerInternal {
link: Option,
image: Option,
line: Newline,
- fenced_code_block: Option,
+ code_block: Option,
is_list_item: bool,
def_list: DefinitionList,
is_table: bool,
@@ -95,7 +95,7 @@ impl CommonMarkViewerInternal {
line: Newline::default(),
is_list_item: false,
def_list: Default::default(),
- fenced_code_block: None,
+ code_block: None,
is_table: false,
is_blockquote: false,
checkbox_events: Vec::new(),
@@ -542,7 +542,7 @@ impl CommonMarkViewerInternal {
let rich_text = self.text_style.to_richtext(ui, &text);
if let Some(image) = &mut self.image {
image.alt_text.push(rich_text);
- } else if let Some(block) = &mut self.fenced_code_block {
+ } else if let Some(block) = &mut self.code_block {
block.content.push_str(&text);
} else if let Some(link) = &mut self.link {
link.text.push(rich_text);
@@ -576,17 +576,23 @@ impl CommonMarkViewerInternal {
self.is_blockquote = true;
}
pulldown_cmark::Tag::CodeBlock(c) => {
- if let pulldown_cmark::CodeBlockKind::Fenced(lang) = c {
- self.fenced_code_block = Some(crate::FencedCodeBlock {
- lang: lang.to_string(),
- content: "".to_string(),
- });
-
- self.line.try_insert_start(ui);
+ match c {
+ pulldown_cmark::CodeBlockKind::Fenced(lang) => {
+ self.code_block = Some(crate::CodeBlock {
+ lang: Some(lang.to_string()),
+ content: "".to_string(),
+ });
+ }
+ pulldown_cmark::CodeBlockKind::Indented => {
+ self.code_block = Some(crate::CodeBlock {
+ lang: None,
+ content: "".to_string(),
+ });
+ }
}
-
- self.text_style.code = true;
+ self.line.try_insert_start(ui);
}
+
pulldown_cmark::Tag::List(point) => {
if !self.list.is_inside_a_list() && self.line.can_insert_start() {
newline(ui);
@@ -739,10 +745,9 @@ impl CommonMarkViewerInternal {
options: &CommonMarkOptions,
max_width: f32,
) {
- if let Some(block) = self.fenced_code_block.take() {
+ if let Some(block) = self.code_block.take() {
block.end(ui, cache, options, max_width);
self.line.try_insert_end(ui);
}
- self.text_style.code = false;
}
}
diff --git a/egui_commonmark_backend/src/lib.rs b/egui_commonmark_backend/src/lib.rs
index d516589..4c0e35f 100644
--- a/egui_commonmark_backend/src/lib.rs
+++ b/egui_commonmark_backend/src/lib.rs
@@ -17,7 +17,7 @@ pub use {
alerts::{alert_ui, Alert, AlertBundle},
// Pretty much every single element in this module is used by the proc macros
elements::*,
- misc::{prepare_show, CommonMarkOptions, FencedCodeBlock, Image, Link},
+ misc::{prepare_show, CodeBlock, CommonMarkOptions, Image, Link},
};
// The only struct that is allowed to use directly. (If one does not need egui_commonmark)
diff --git a/egui_commonmark_backend/src/misc.rs b/egui_commonmark_backend/src/misc.rs
index d5c6da8..91519a5 100644
--- a/egui_commonmark_backend/src/misc.rs
+++ b/egui_commonmark_backend/src/misc.rs
@@ -229,12 +229,12 @@ impl Image {
}
}
-pub struct FencedCodeBlock {
- pub lang: String,
+pub struct CodeBlock {
+ pub lang: Option,
pub content: String,
}
-impl FencedCodeBlock {
+impl CodeBlock {
pub fn end(
&self,
ui: &mut Ui,
@@ -246,7 +246,12 @@ impl FencedCodeBlock {
Self::pre_syntax_highlighting(cache, options, ui);
let mut layout = |ui: &Ui, string: &str, wrap_width: f32| {
- let mut job = self.syntax_highlighting(cache, options, &self.lang, ui, string);
+ let mut job = if let Some(lang) = &self.lang {
+ self.syntax_highlighting(cache, options, lang, ui, string)
+ } else {
+ plain_highlighting(ui, string)
+ };
+
job.wrap.max_width = wrap_width;
ui.fonts(|f| f.layout_job(job))
};
@@ -257,7 +262,7 @@ impl FencedCodeBlock {
}
#[cfg(not(feature = "better_syntax_highlighting"))]
-impl FencedCodeBlock {
+impl CodeBlock {
fn pre_syntax_highlighting(
_cache: &mut CommonMarkCache,
_options: &CommonMarkOptions,
@@ -274,12 +279,12 @@ impl FencedCodeBlock {
ui: &Ui,
text: &str,
) -> egui::text::LayoutJob {
- plain_highlighting(ui, text, extension)
+ simple_highlighting(ui, text, extension)
}
}
#[cfg(feature = "better_syntax_highlighting")]
-impl FencedCodeBlock {
+impl CodeBlock {
fn pre_syntax_highlighting(
cache: &mut CommonMarkCache,
options: &CommonMarkOptions,
@@ -328,12 +333,12 @@ impl FencedCodeBlock {
job
} else {
- plain_highlighting(ui, text, extension)
+ simple_highlighting(ui, text, extension)
}
}
}
-fn plain_highlighting(ui: &Ui, text: &str, extension: &str) -> egui::text::LayoutJob {
+fn simple_highlighting(ui: &Ui, text: &str, extension: &str) -> egui::text::LayoutJob {
egui_extras::syntax_highlighting::highlight(
ui.ctx(),
&egui_extras::syntax_highlighting::CodeTheme::from_style(ui.style()),
@@ -342,6 +347,19 @@ fn plain_highlighting(ui: &Ui, text: &str, extension: &str) -> egui::text::Layou
)
}
+fn plain_highlighting(ui: &Ui, text: &str) -> egui::text::LayoutJob {
+ let mut job = egui::text::LayoutJob::default();
+ job.append(
+ text,
+ 0.0,
+ egui::TextFormat::simple(
+ TextStyle::Monospace.resolve(ui.style()),
+ ui.style().visuals.text_color(),
+ ),
+ );
+ job
+}
+
#[cfg(feature = "better_syntax_highlighting")]
fn syntect_color_to_egui(color: syntect::highlighting::Color) -> egui::Color32 {
egui::Color32::from_rgb(color.r, color.g, color.b)
diff --git a/egui_commonmark_macros/src/generator.rs b/egui_commonmark_macros/src/generator.rs
index 8d4e8ee..dafe89b 100644
--- a/egui_commonmark_macros/src/generator.rs
+++ b/egui_commonmark_macros/src/generator.rs
@@ -1,7 +1,7 @@
use std::iter::Peekable;
use egui_commonmark_backend::{
- alerts::Alert, misc::Style, pulldown::*, CommonMarkOptions, FencedCodeBlock, Image,
+ alerts::Alert, misc::Style, pulldown::*, CodeBlock, CommonMarkOptions, Image,
};
use proc_macro2::TokenStream;
@@ -169,7 +169,7 @@ pub(crate) struct CommonMarkViewerInternal {
link: Option,
image: Option,
line: Newline,
- fenced_code_block: Option,
+ code_block: Option,
is_list_item: bool,
def_list: DefinitionList,
is_table: bool,
@@ -192,7 +192,7 @@ impl CommonMarkViewerInternal {
line: Newline::default(),
is_list_item: false,
def_list: Default::default(),
- fenced_code_block: None,
+ code_block: None,
is_table: false,
is_blockquote: false,
dumps_heading: false,
@@ -559,7 +559,7 @@ impl CommonMarkViewerInternal {
image
.alt_text
.push(StyledText::new(self.text_style.clone(), text.to_string()));
- } else if let Some(block) = &mut self.fenced_code_block {
+ } else if let Some(block) = &mut self.code_block {
block.content.push_str(&text);
} else if let Some(link) = &mut self.link {
link.text
@@ -595,18 +595,22 @@ impl CommonMarkViewerInternal {
TokenStream::new()
}
pulldown_cmark::Tag::CodeBlock(c) => {
- self.text_style.code = true;
-
- if let pulldown_cmark::CodeBlockKind::Fenced(lang) = c {
- self.fenced_code_block = Some(FencedCodeBlock {
- lang: lang.to_string(),
- content: "".to_string(),
- });
-
- self.line.try_insert_start()
- } else {
- TokenStream::new()
+ match c {
+ pulldown_cmark::CodeBlockKind::Fenced(lang) => {
+ self.code_block = Some(CodeBlock {
+ lang: Some(lang.to_string()),
+ content: "".to_string(),
+ });
+ }
+ pulldown_cmark::CodeBlockKind::Indented => {
+ self.code_block = Some(CodeBlock {
+ lang: None,
+ content: "".to_string(),
+ });
+ }
}
+
+ self.line.try_insert_start()
}
pulldown_cmark::Tag::List(point) => {
@@ -807,21 +811,22 @@ impl CommonMarkViewerInternal {
fn end_code_block(&mut self, cache: &Expr) -> TokenStream {
let mut stream = TokenStream::new();
- if let Some(block) = self.fenced_code_block.take() {
- let lang = block.lang;
+ if let Some(block) = self.code_block.take() {
let content = block.content;
- stream.extend(
- quote!(
- egui_commonmark_backend::FencedCodeBlock {lang: #lang.to_owned(), content: #content.to_owned()}
- .end(ui, #cache, &options, max_width);
- ));
+ stream.extend(if let Some(lang) = block.lang {
+ quote!(egui_commonmark_backend::CodeBlock {
+ lang: Some(#lang.to_owned()), content: #content.to_owned()}
+ .end(ui, #cache, &options, max_width);)
+ } else {
+ quote!(egui_commonmark_backend::CodeBlock {
+ lang: None, content: #content.to_owned()}
+ .end(ui, #cache, &options, max_width);)
+ });
stream.extend(self.line.try_insert_end());
}
- self.text_style.code = false;
-
stream
}