Replies: 8 comments
-
I wrote some code too for rendering markdown with Ratatui that is still in an unfinished unpublished project. Another idea: syntax highlighting in code blocks. I implemented something like this for translating code blocks to ratatui with language specific syntax highlighting using syntect: use syntect::{
easy::HighlightLines, highlighting::ThemeSet, parsing::SyntaxSet, util::LinesWithEndings,
};
// ...
if let Some(Tag::CodeBlock(kind)) = tags.last() {
let language = match kind {
CodeBlockKind::Fenced(language) => language,
CodeBlockKind::Indented => "",
};
let ps = SyntaxSet::load_defaults_newlines();
let ts = ThemeSet::load_defaults();
let syntax = ps.find_syntax_by_token(language).unwrap_or_else(|| {
ps.find_syntax_by_name(language).unwrap_or_else(|| ps.find_syntax_plain_text())
});
let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
for line in LinesWithEndings::from(text.as_ref()) {
let line_spans: Vec<ratatui::text::Span> = h
.highlight_line(line, &ps)
.unwrap()
.into_iter()
.filter_map(|(style, s)| into_span((style, s.to_string())).ok())
.collect();
let line = ratatui::text::Line::from(line_spans);
lines.push(line);
}
pub fn into_span(
(style, content): (syntect::highlighting::Style, String),
) -> Result<ratatui::text::Span<'static>> {
// ...
} I'll share the full version of |
Beta Was this translation helpful? Give feedback.
-
Hey that looks pretty good. Taking a very quick look at the code, it seems like there's some code that is fairly app specific, intermixed with code that's not. There's a lot of complexity in that folder - is markdown actually fairly complex, or is that just because of what your app needs? I don't see a license in the repo - are you able to license this as MIT/Apache 2.0?
Yep yep yep... This and themes are two necessary things |
Beta Was this translation helpful? Give feedback.
-
It has MIT license which is written in Cargo.toml.
Yeah... Let me explain what I need and what I do: syntect is great at highlighting syntaxes including the whole markdown document, but the biggest problem for me are text wrapping and how links should be rendered?for example, here are two versions of rendering markdown in syntect and term-rustdoc on the same screen for the same docsyntect:
Actually, I do use syntect to highlight codeblocks too. Since code snippets are not wrappable like markdown texts (it would change the meaning if we arbitrarily eliminate whitespaces to wrap code), syntect can directly emit the colors on each words and we just need to simply wrap the long line to next line by moving these words beyond the width to it. |
Beta Was this translation helpful? Give feedback.
-
Neat - thanks for the explanation. I think I'd like to handle that with something I'm experimenting with. I have some ideas around having actual widgets for each element, each implementing an E.g. a over-simplified Heading element (that can only have a string child) might be something like: pub trait Element: WidgetRef + Debug {}
#[derive(Debug)]
pub struct Heading<'a> {
level: u8,
text: &'a str,
}
impl Element for Heading<'_> {}
impl WidgetRef for Heading<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
Text::from(self.text).style(self.style()).render(area, buf);
}
}
impl Heading<'_> {
pub fn new(level: u8) -> Self {
let text = "";
Self { level, text }
}
pub fn add_text(&mut self, text: &str) {
self.text = text;
}
fn style(&self) -> Style {
match self.level {
1 => Style::default().fg(Color::Red),
2 => Style::default().fg(Color::Green),
3 => Style::default().fg(Color::Blue),
4 => Style::default().fg(Color::Yellow),
5 => Style::default().fg(Color::Magenta),
6 => Style::default().fg(Color::Cyan),
_ => Style::default(),
}
}
} And the markdown widget might look something like: #[derive(Debug)]
pub struct MarkdownDoc {
elements: Vec<Box<dyn Element>>,
} I think the wrapping should be handled by the widgets - One of the things that leads me to this approach is that it allows the user to interact with elements (e.g. adding focusable regions for code blocks that can be copied, or having headings / links be navigable.) I think you have something similar in your app. I'm in agreement with your approach to handling the markdown by throwing away some of the data (e.g. the link type or choice of style for emphasis probably doesn't matter) This is going to be more an opinionated viewer library than a markdown editor library. |
Beta Was this translation helpful? Give feedback.
-
Thinking about this more, it seems that this approach has two directions it can go:
|
Beta Was this translation helpful? Give feedback.
-
Hah, I had a similar thought when deciding to write my own rendering code. There is a third choice: termimad. It contains incomplete markdown parsing code and also expresses a line with styles and purposes. But it's independent of ratatui. I didn't dive into it too much. The main drawback is no highlighting in codeblocks. I guess it won't be hard to support it by syntect in termimad! A good thing to see is termimad also supports scrolling and wrapping. My app term-rustdoc is similar with termimad in rendering markdown:
But term-rustdoc is featured as follows
|
Beta Was this translation helpful? Give feedback.
-
Word is a basic and handy concept.
When
If you don't want this behavior, just make a Word
That's the secret to see links are able to be wrapped in the middle. |
Beta Was this translation helpful? Give feedback.
-
There's lots of good info here, that I'd prefer doesn't get hidden away when an issue is closed, but there's nothing immediately actionable, so I'm moving it to a discussion. |
Beta Was this translation helpful? Give feedback.
-
Hi, I noticed this crate is trying to implement markdown rendering with ratatui.
I'm delighted to share the code about rendering markdown I write in term-rustdoc.
The markdown rendering code is complete and viable to be split from term-rustdoc. I write it for displaying Rust docs and Help. Screenshots.
Relevant code is here.
Beta Was this translation helpful? Give feedback.
All reactions