Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/iamb.5
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ or
.It Sy user_gutter_width
Specify the width of the column where usernames are displayed in a room.
Usernames that are too long are truncated.
If this value is less than 3, the usernames are displayed in an extra line at the top of the message
and this value is only used to indent the message.
Defaults to 30.

.It Sy tabstop
Expand Down
96 changes: 67 additions & 29 deletions src/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,20 @@ impl MessageColumns {
}
}

#[derive(Default, Debug)]
enum SenderSpan<'a> {
/// Show the sender name in the user gutter.
/// This is truncated and padded to fit [`user_gutter_width`](`crate::config::TunableValues::user_gutter_width`).
Gutter(Span<'a>),

/// Show the sender name in an extra line at the top of the message.
Line(Span<'a>),

/// The sender name has already been printed.
#[default]
None,
}

struct MessageFormatter<'a> {
settings: &'a ApplicationSettings,

Expand All @@ -612,7 +626,7 @@ struct MessageFormatter<'a> {
fill: usize,

/// The formatted Span for the message sender.
user: Option<Span<'a>>,
user: SenderSpan<'a>,

/// The time the message was sent.
time: Option<Span<'a>>,
Expand All @@ -629,6 +643,20 @@ impl<'a> MessageFormatter<'a> {
self.fill
}

fn message_start_line(&self) -> u16 {
let mut line = 0;

if self.date.is_some() {
line += 1;
}

if let SenderSpan::Line(_) = self.user {
line += 1;
}

line
}

#[inline]
fn push_spans(&mut self, prev_line: Line<'a>, style: Style, text: &mut Text<'a>) {
if let Some(date) = self.date.take() {
Expand All @@ -643,13 +671,21 @@ impl<'a> MessageFormatter<'a> {
let user_gutter_empty_span =
space_span(self.settings.tunables.user_gutter_width, Style::default());

let user_gutter = match std::mem::take(&mut self.user) {
SenderSpan::Line(user) => {
text.lines.push(user.into());
user_gutter_empty_span
},
SenderSpan::Gutter(user) => user,
SenderSpan::None => user_gutter_empty_span,
};

match self.cols {
MessageColumns::Four => {
let settings = self.settings;
let user = self.user.take().unwrap_or(user_gutter_empty_span);
let time = self.time.take().unwrap_or(TIME_GUTTER_EMPTY_SPAN);

let mut line = vec![user];
let mut line = vec![user_gutter];
line.extend(prev_line.spans);
line.push(time);

Expand All @@ -669,27 +705,21 @@ impl<'a> MessageFormatter<'a> {
text.lines.push(Line::from(line))
},
MessageColumns::Three => {
let user = self.user.take().unwrap_or(user_gutter_empty_span);
let time = self.time.take().unwrap_or_else(|| Span::from(""));

let mut line = vec![user];
let mut line = vec![user_gutter];
line.extend(prev_line.spans);
line.push(time);

text.lines.push(Line::from(line))
},
MessageColumns::Two => {
let user = self.user.take().unwrap_or(user_gutter_empty_span);
let mut line = vec![user];
let mut line = vec![user_gutter];
line.extend(prev_line.spans);

text.lines.push(Line::from(line));
},
MessageColumns::One => {
if let Some(user) = self.user.take() {
text.lines.push(Line::from(vec![user]));
}

let leading = space_span(2, style);
let mut line = vec![leading];
line.extend(prev_line.spans);
Expand Down Expand Up @@ -933,7 +963,7 @@ impl Message {
{
let cols = MessageColumns::Four;
let fill = width - user_gutter - TIME_GUTTER - READ_GUTTER;
let user = self.show_sender(prev, true, info, settings);
let user = self.show_sender(prev, true, info, settings, width);
let time = self.timestamp.show_time();
let read = info
.event_receipts
Expand All @@ -947,23 +977,23 @@ impl Message {
} else if user_gutter + TIME_GUTTER + MIN_MSG_LEN <= width {
let cols = MessageColumns::Three;
let fill = width - user_gutter - TIME_GUTTER;
let user = self.show_sender(prev, true, info, settings);
let user = self.show_sender(prev, true, info, settings, width);
let time = self.timestamp.show_time();
let read = Vec::new();

MessageFormatter { settings, cols, orig, fill, user, date, time, read }
} else if user_gutter + MIN_MSG_LEN <= width {
let cols = MessageColumns::Two;
let fill = width - user_gutter;
let user = self.show_sender(prev, true, info, settings);
let user = self.show_sender(prev, true, info, settings, width);
let time = None;
let read = Vec::new();

MessageFormatter { settings, cols, orig, fill, user, date, time, read }
} else {
let cols = MessageColumns::One;
let fill = width.saturating_sub(2);
let user = self.show_sender(prev, false, info, settings);
let user = self.show_sender(prev, false, info, settings, width);
let time = None;
let read = Vec::new();

Expand Down Expand Up @@ -1006,9 +1036,9 @@ impl Message {
let proto_main = proto.map(|p| {
let y_off = text.lines.len() as u16;
let x_off = fmt.cols.user_gutter_width(settings);
// Adjust y_off by 1 if a date was printed before the message to account for
// the extra line we're going to print.
let y_off = if fmt.date.is_some() { y_off + 1 } else { y_off };

// Account for extra lines printed before the message;
let y_off = y_off + fmt.message_start_line();
(p, x_off, y_off)
});

Expand Down Expand Up @@ -1093,31 +1123,39 @@ impl Message {
fn show_sender<'a>(
&'a self,
prev: Option<&Message>,
align_right: bool,
gutter_enabled: bool,
info: &'a RoomInfo,
settings: &'a ApplicationSettings,
) -> Option<Span<'a>> {
width: usize,
) -> SenderSpan<'a> {
if let Some(prev) = prev {
if self.sender == prev.sender &&
self.timestamp.same_day(&prev.timestamp) &&
!self.event.is_emote()
{
return None;
return SenderSpan::None;
}
}

let Span { content, style } = self.sender_span(info, settings);
let user_gutter = settings.tunables.user_gutter_width;
let ((truncated, width), _) = take_width(content, user_gutter - 2);
let padding = user_gutter - 2 - width;

let sender = if align_right {
format!("{}{} ", space(padding), truncated)
} else {
format!("{}{} ", truncated, space(padding))
};
let show_in_gutter = gutter_enabled && user_gutter > 2;

if show_in_gutter {
let ((truncated, width), _) = take_width(content, user_gutter - 2);
let padding = user_gutter - 2 - width;

let sender = format!("{}{} ", space(padding), truncated);

Span::styled(sender, style).into()
SenderSpan::Gutter(Span::styled(sender, style))
} else if UnicodeWidthStr::width(content.as_ref()) > width {
let ((truncated, _), _) = take_width(content, width);

SenderSpan::Line(Span::styled(truncated, style))
} else {
SenderSpan::Line(Span::styled(content, style))
}
}

pub fn redact(&mut self, redaction: SyncRoomRedactionEvent) {
Expand Down
Loading