Compare commits

..

7 commits

9 changed files with 76 additions and 12 deletions

View file

@ -114,8 +114,6 @@ Redact the selected message with the optional reason.
Reply to the selected message. Reply to the selected message.
.It Sy ":cancel" .It Sy ":cancel"
Cancel the currently drafted message including replies. Cancel the currently drafted message including replies.
.It Sy ":unreads clear"
Mark all unread rooms as read.
.It Sy ":upload [path]" .It Sy ":upload [path]"
Upload an attachment and send it to the currently selected room. Upload an attachment and send it to the currently selected room.
.El .El

View file

@ -208,6 +208,9 @@ See
.Sx "SORTING LISTS" .Sx "SORTING LISTS"
for more details. for more details.
.It Sy state_event_display
Defines whether the state events like joined or left are shown.
.It Sy typing_notice_send .It Sy typing_notice_send
Defines whether or not the typing state is sent. Defines whether or not the typing state is sent.
@ -231,6 +234,10 @@ Possible values are
Specify the width of the column where usernames are displayed in a room. Specify the width of the column where usernames are displayed in a room.
Usernames that are too long are truncated. Usernames that are too long are truncated.
Defaults to 30. Defaults to 30.
.It Sy tabstop
Number of spaces that a <Tab> counts for.
Defaults to 4.
.El .El
.Ss Example 1: Avoid showing Emojis (useful for terminals w/o support) .Ss Example 1: Avoid showing Emojis (useful for terminals w/o support)

View file

@ -1361,7 +1361,9 @@ impl RoomInfo {
} }
if !settings.tunables.typing_notice_display { if !settings.tunables.typing_notice_display {
return area; // still keep one line blank, so `render_jump_to_recent` doesn't immediately hide the
// last line in scrollback
return Rect::new(area.x, area.y, area.width, area.height - 1);
} }
let top = Rect::new(area.x, area.y, area.width, area.height - 1); let top = Rect::new(area.x, area.y, area.width, area.height - 1);

View file

@ -578,6 +578,7 @@ pub struct TunableValues {
pub image_preview: Option<ImagePreviewValues>, pub image_preview: Option<ImagePreviewValues>,
pub user_gutter_width: usize, pub user_gutter_width: usize,
pub external_edit_file_suffix: String, pub external_edit_file_suffix: String,
pub tabstop: usize,
} }
#[derive(Clone, Default, Deserialize)] #[derive(Clone, Default, Deserialize)]
@ -605,6 +606,7 @@ pub struct Tunables {
pub image_preview: Option<ImagePreview>, pub image_preview: Option<ImagePreview>,
pub user_gutter_width: Option<usize>, pub user_gutter_width: Option<usize>,
pub external_edit_file_suffix: Option<String>, pub external_edit_file_suffix: Option<String>,
pub tabstop: Option<usize>,
} }
impl Tunables { impl Tunables {
@ -638,6 +640,7 @@ impl Tunables {
external_edit_file_suffix: self external_edit_file_suffix: self
.external_edit_file_suffix .external_edit_file_suffix
.or(other.external_edit_file_suffix), .or(other.external_edit_file_suffix),
tabstop: self.tabstop.or(other.tabstop),
} }
} }
@ -667,6 +670,7 @@ impl Tunables {
external_edit_file_suffix: self external_edit_file_suffix: self
.external_edit_file_suffix .external_edit_file_suffix
.unwrap_or_else(|| ".md".to_string()), .unwrap_or_else(|| ".md".to_string()),
tabstop: self.tabstop.unwrap_or(4),
} }
} }
} }

View file

@ -748,7 +748,7 @@ fn h2t(hdl: &Handle, state: &mut TreeGenState) -> StyleTreeChildren {
StyleTreeNode::Style(c, s) StyleTreeNode::Style(c, s)
}, },
"del" | "strike" => { "del" | "s" | "strike" => {
let c = c2t(&node.children.borrow(), state); let c = c2t(&node.children.borrow(), state);
let s = Style::default().add_modifier(StyleModifier::CROSSED_OUT); let s = Style::default().add_modifier(StyleModifier::CROSSED_OUT);
@ -1415,13 +1415,14 @@ pub mod tests {
let s = concat!( let s = concat!(
"<pre><code class=\"language-rust\">", "<pre><code class=\"language-rust\">",
"fn hello() -&gt; usize {\n", "fn hello() -&gt; usize {\n",
" \t// weired\n",
" return 5;\n", " return 5;\n",
"}\n", "}\n",
"</code></pre>\n" "</code></pre>\n"
); );
let tree = parse_matrix_html(s); let tree = parse_matrix_html(s);
let text = tree.to_text(25, Style::default(), true, &settings); let text = tree.to_text(25, Style::default(), true, &settings);
assert_eq!(text.lines.len(), 5); assert_eq!(text.lines.len(), 6);
assert_eq!( assert_eq!(
text.lines[0], text.lines[0],
Line::from(vec![ Line::from(vec![
@ -1452,6 +1453,20 @@ pub mod tests {
); );
assert_eq!( assert_eq!(
text.lines[2], text.lines[2],
Line::from(vec![
Span::raw(line::VERTICAL),
Span::raw(" "),
Span::raw(" "),
Span::raw("/"),
Span::raw("/"),
Span::raw(" "),
Span::raw("weired"),
Span::raw(" "),
Span::raw(line::VERTICAL)
])
);
assert_eq!(
text.lines[3],
Line::from(vec![ Line::from(vec![
Span::raw(line::VERTICAL), Span::raw(line::VERTICAL),
Span::raw(" "), Span::raw(" "),
@ -1464,7 +1479,7 @@ pub mod tests {
]) ])
); );
assert_eq!( assert_eq!(
text.lines[3], text.lines[4],
Line::from(vec![ Line::from(vec![
Span::raw(line::VERTICAL), Span::raw(line::VERTICAL),
Span::raw("}"), Span::raw("}"),
@ -1473,7 +1488,7 @@ pub mod tests {
]) ])
); );
assert_eq!( assert_eq!(
text.lines[4], text.lines[5],
Line::from(vec![ Line::from(vec![
Span::raw(line::BOTTOM_LEFT), Span::raw(line::BOTTOM_LEFT),
Span::raw(line::HORIZONTAL.repeat(23)), Span::raw(line::HORIZONTAL.repeat(23)),

View file

@ -176,7 +176,9 @@ fn placeholder_frame(
} }
let mut placeholder = "\u{230c}".to_string(); let mut placeholder = "\u{230c}".to_string();
placeholder.push_str(&" ".repeat(width - 2)); placeholder.push_str(&" ".repeat(width - 2));
placeholder.push_str("\u{230d}\n"); placeholder.push('\u{230d}');
placeholder.push_str(&"\n".repeat((height - 1) / 2));
if *height > 2 { if *height > 2 {
if let Some(text) = text { if let Some(text) = text {
if text.width() <= width - 2 { if text.width() <= width - 2 {
@ -186,7 +188,7 @@ fn placeholder_frame(
} }
} }
placeholder.push_str(&"\n".repeat(height - 2)); placeholder.push_str(&"\n".repeat(height / 2));
placeholder.push('\u{230e}'); placeholder.push('\u{230e}');
placeholder.push_str(&" ".repeat(width - 2)); placeholder.push_str(&" ".repeat(width - 2));
placeholder.push_str("\u{230f}\n"); placeholder.push_str("\u{230f}\n");
@ -1087,7 +1089,7 @@ impl Message {
}, },
ImageStatus::Loaded(backend) => { ImageStatus::Loaded(backend) => {
proto = Some(backend); proto = Some(backend);
placeholder_frame(Some("Loading..."), width, &backend.area().into()) placeholder_frame(Some("Cut off..."), width, &backend.area().into())
}, },
ImageStatus::Error(err) => Some(format!("[Image error: {err}]\n")), ImageStatus::Error(err) => Some(format!("[Image error: {err}]\n")),
}; };
@ -1357,6 +1359,33 @@ pub mod tests {
OK OK
"#
)
);
assert_eq!(
placeholder_frame(Some("OK"), 6, &ImagePreviewSize { width: 6, height: 6 }),
pretty_frame_test(
r#"
OK
"#
)
);
assert_eq!(
placeholder_frame(Some("OK"), 6, &ImagePreviewSize { width: 6, height: 7 }),
pretty_frame_test(
r#"
OK
"# "#
) )

View file

@ -216,6 +216,8 @@ impl<'a> TextPrinter<'a> {
return; return;
} }
let tabstop = self.settings().tunables.tabstop;
for mut word in UnicodeSegmentation::split_word_bounds(s) { for mut word in UnicodeSegmentation::split_word_bounds(s) {
if let "\n" | "\r\n" = word { if let "\n" | "\r\n" = word {
if self.literal { if self.literal {
@ -232,11 +234,17 @@ impl<'a> TextPrinter<'a> {
continue; continue;
} }
let cow = if self.emoji_shortcodes() { let mut cow = if self.emoji_shortcodes() {
Cow::Owned(replace_emojis_in_str(word)) Cow::Owned(replace_emojis_in_str(word))
} else { } else {
Cow::Borrowed(word) Cow::Borrowed(word)
}; };
if cow == "\t" {
let tablen = tabstop - (self.curr_width % tabstop);
cow = Cow::Owned(" ".repeat(tablen));
}
let sw = UnicodeWidthStr::width(cow.as_ref()); let sw = UnicodeWidthStr::width(cow.as_ref());
if sw > self.width { if sw > self.width {

View file

@ -199,6 +199,7 @@ pub fn mock_tunables() -> TunableValues {
}, },
image_preview: None, image_preview: None,
user_gutter_width: 30, user_gutter_width: 30,
tabstop: 4,
} }
} }

View file

@ -1571,7 +1571,7 @@ mod tests {
// MSG1: | XXXday, Month NN 20XX | // MSG1: | XXXday, Month NN 20XX |
// | @user1:example.com writhe | // | @user1:example.com writhe |
// |------------------------------------------------------------| // |------------------------------------------------------------|
let area = Rect::new(0, 0, 60, 4); let area = Rect::new(0, 0, 60, 5);
let mut buffer = Buffer::empty(area); let mut buffer = Buffer::empty(area);
scrollback.draw(area, &mut buffer, true, &mut store); scrollback.draw(area, &mut buffer, true, &mut store);