Interpret newlines as line breaks when converting Markdown to HTML (#74)

This commit is contained in:
Ulyssa 2023-04-06 16:10:48 -07:00
parent 953be6a195
commit ad3b40d538
No known key found for this signature in database
GPG key ID: 1B3965A3D18B9B64
6 changed files with 442 additions and 42 deletions

View file

@ -1027,9 +1027,8 @@ pub mod tests {
Span::raw(""),
Span::raw(" "),
Span::raw(""),
Span::styled(" ", bold),
Span::styled("3", bold),
Span::styled(" ", bold),
Span::styled(" ", bold),
Span::raw("")
]);
@ -1157,4 +1156,21 @@ pub mod tests {
assert_eq!(text.lines[1], Spans(vec![Span::raw("World"), Span::raw(" "),]));
assert_eq!(text.lines[2], Spans(vec![Span::raw("Goodbye")]),);
}
#[test]
fn test_embedded_newline() {
let s = "<p>Hello\nWorld</p>";
let tree = parse_matrix_html(s);
let text = tree.to_text(15, Style::default(), true);
assert_eq!(text.lines.len(), 1);
assert_eq!(
text.lines[0],
Spans(vec![
Span::raw("Hello"),
Span::raw(" "),
Span::raw("World"),
Span::raw(" ")
])
);
}
}

View file

@ -7,6 +7,7 @@ use std::hash::{Hash, Hasher};
use std::slice::Iter;
use chrono::{DateTime, Local as LocalTz, NaiveDateTime, TimeZone};
use comrak::{markdown_to_html, ComrakOptions};
use unicode_width::UnicodeWidthStr;
use matrix_sdk::ruma::{
@ -26,6 +27,7 @@ use matrix_sdk::ruma::{
Relation,
RoomMessageEvent,
RoomMessageEventContent,
TextMessageEventContent,
},
redaction::SyncRoomRedactionEvent,
},
@ -93,6 +95,19 @@ const USER_GUTTER_EMPTY_SPAN: Span<'static> = span_static(USER_GUTTER_EMPTY);
const TIME_GUTTER_EMPTY: &str = " ";
const TIME_GUTTER_EMPTY_SPAN: Span<'static> = span_static(TIME_GUTTER_EMPTY);
fn text_to_message_content(input: String) -> TextMessageEventContent {
let mut options = ComrakOptions::default();
options.render.hardbreaks = true;
let html = markdown_to_html(input.as_str(), &options);
TextMessageEventContent::html(input, html)
}
pub fn text_to_message(input: String) -> RoomMessageEventContent {
let msg = MessageType::Text(text_to_message_content(input));
RoomMessageEventContent::new(msg)
}
#[inline]
fn millis_to_datetime(ms: UInt) -> DateTime<LocalTz> {
let time = i64::from(ms) / 1000;
@ -972,4 +987,48 @@ pub mod tests {
// MessageCursor::latest() should point at the most recent message after conversion.
assert_eq!(identity(&mc6), mc1);
}
#[test]
fn test_markdown_message() {
let input = "**bold**\n";
let content = text_to_message_content(input.into());
assert_eq!(content.body, input);
assert_eq!(content.formatted.unwrap().body, "<p><strong>bold</strong></p>\n");
let input = "*emphasis*\n";
let content = text_to_message_content(input.into());
assert_eq!(content.body, input);
assert_eq!(content.formatted.unwrap().body, "<p><em>emphasis</em></p>\n");
let input = "`code`\n";
let content = text_to_message_content(input.into());
assert_eq!(content.body, input);
assert_eq!(content.formatted.unwrap().body, "<p><code>code</code></p>\n");
let input = "```rust\nconst A: usize = 1;\n```\n";
let content = text_to_message_content(input.into());
assert_eq!(content.body, input);
assert_eq!(
content.formatted.unwrap().body,
"<pre><code class=\"language-rust\">const A: usize = 1;\n</code></pre>\n"
);
let input = "para 1\n\npara 2\n";
let content = text_to_message_content(input.into());
assert_eq!(content.body, input);
assert_eq!(content.formatted.unwrap().body, "<p>para 1</p>\n<p>para 2</p>\n");
let input = "line 1\nline 2\n";
let content = text_to_message_content(input.into());
assert_eq!(content.body, input);
assert_eq!(content.formatted.unwrap().body, "<p>line 1<br />\nline 2</p>\n");
let input = "# Heading\n## Subheading\n\ntext\n";
let content = text_to_message_content(input.into());
assert_eq!(content.body, input);
assert_eq!(
content.formatted.unwrap().body,
"<h1>Heading</h1>\n<h2>Subheading</h2>\n<p>text</p>\n"
);
}
}

View file

@ -156,8 +156,13 @@ impl<'a> TextPrinter<'a> {
pub fn push_str(&mut self, s: &'a str, style: Style) {
let style = self.base_style.patch(style);
for word in UnicodeSegmentation::split_word_bounds(s) {
if self.width == 0 && word.chars().all(char::is_whitespace) {
for mut word in UnicodeSegmentation::split_word_bounds(s) {
if let "\n" | "\r\n" = word {
// Render embedded newlines as spaces.
word = " ";
}
if self.curr_width == 0 && word.chars().all(char::is_whitespace) {
// Drop leading whitespace.
continue;
}

View file

@ -75,7 +75,7 @@ use crate::base::{
SendAction,
};
use crate::message::{Message, MessageEvent, MessageKey, MessageTimeStamp};
use crate::message::{text_to_message, Message, MessageEvent, MessageKey, MessageTimeStamp};
use crate::worker::Requester;
use super::scrollback::{Scrollback, ScrollbackState};
@ -407,10 +407,7 @@ impl ChatState {
return Ok(None);
}
let msg = TextMessageEventContent::markdown(msg.to_string());
let msg = MessageType::Text(msg);
let mut msg = RoomMessageEventContent::new(msg);
let mut msg = text_to_message(msg.to_string());
if let Some((_, event_id)) = &self.editing {
msg.relates_to = Some(Relation::Replacement(Replacement::new(