summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlessandro Menezes <alessandroasm@gmail.com>2020-10-03 23:17:36 -0400
committerGitHub <noreply@github.com>2020-10-04 03:17:36 +0000
commit44a6dba0af03d3b9305ceea31c3289709cc750e3 (patch)
treea351056c5e03bb4e598db56c4b8a9e94b6065edd
parentf5813899c3212d295358911635109201910b666b (diff)
Fix non-ascii message bar text width calculation
When formatting text for display in the message bar, Alacritty was using the byte length of the text instead of the glyph count. This lead to unnecessary blank space at the end of lines due to overestimation of their length. There also were no extra spaces inserted after fullwidth characters, leading to Alacritty giving them only a single cell of space. In line with the rest of Alacritty's rendering, a wide char spacer whitespace is now inserted in the message bar after glyphs which should occupy two cells. Fixes #4250. Co-authored-by: Christian Duerr <contact@christianduerr.com>
-rw-r--r--CHANGELOG.md1
-rw-r--r--alacritty/src/message_bar.rs68
2 files changed, 57 insertions, 12 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7f130a2..4da3e707 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -47,6 +47,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- IME window position with fullwidth characters in the search bar
- Selection expanding over 2 characters when scrolled in history with fullwidth characters in use
- Selection scrolling not starting when mouse is over the message bar
+- Incorrect text width calculation in message bar when the message contains multibyte characters
## 0.5.0
diff --git a/alacritty/src/message_bar.rs b/alacritty/src/message_bar.rs
index 851cb6ee..609af55d 100644
--- a/alacritty/src/message_bar.rs
+++ b/alacritty/src/message_bar.rs
@@ -1,6 +1,7 @@
use std::collections::VecDeque;
use alacritty_terminal::term::SizeInfo;
+use unicode_width::UnicodeWidthChar;
pub const CLOSE_BUTTON_TEXT: &str = "[X]";
const CLOSE_BUTTON_PADDING: usize = 1;
@@ -37,34 +38,49 @@ impl Message {
let total_lines =
(size_info.height() - 2. * size_info.padding_y()) / size_info.cell_height();
let max_lines = (total_lines as usize).saturating_sub(MIN_FREE_LINES);
- let button_len = CLOSE_BUTTON_TEXT.len();
+ let button_len = CLOSE_BUTTON_TEXT.chars().count();
// Split line to fit the screen.
let mut lines = Vec::new();
let mut line = String::new();
+ let mut line_len = 0;
for c in self.text.trim().chars() {
if c == '\n'
- || line.len() == num_cols
+ || line_len == num_cols
// Keep space in first line for button.
|| (lines.is_empty()
&& num_cols >= button_len
- && line.len() == num_cols.saturating_sub(button_len + CLOSE_BUTTON_PADDING))
+ && line_len == num_cols.saturating_sub(button_len + CLOSE_BUTTON_PADDING))
{
+ let is_whitespace = c.is_whitespace();
+
// Attempt to wrap on word boundaries.
- if let (Some(index), true) = (line.rfind(char::is_whitespace), c != '\n') {
+ let mut new_line = String::new();
+ if let Some(index) = line.rfind(char::is_whitespace).filter(|_| !is_whitespace) {
let split = line.split_off(index + 1);
line.pop();
- lines.push(Self::pad_text(line, num_cols));
- line = split
- } else {
- lines.push(Self::pad_text(line, num_cols));
- line = String::new();
+ new_line = split;
+ }
+
+ lines.push(Self::pad_text(line, num_cols));
+ line = new_line;
+ line_len = line.chars().count();
+
+ // Do not append whitespace at EOL.
+ if is_whitespace {
+ continue;
}
}
- if c != '\n' {
- line.push(c);
+ line.push(c);
+
+ // Reserve extra column for fullwidth characters.
+ let width = c.width().unwrap_or(0);
+ if width == 2 {
+ line.push(' ');
}
+
+ line_len += width
}
lines.push(Self::pad_text(line, num_cols));
@@ -110,7 +126,7 @@ impl Message {
/// Right-pad text to fit a specific number of columns.
#[inline]
fn pad_text(mut text: String, num_cols: usize) -> String {
- let padding_len = num_cols.saturating_sub(text.len());
+ let padding_len = num_cols.saturating_sub(text.chars().count());
text.extend(vec![' '; padding_len]);
text
}
@@ -341,6 +357,34 @@ mod tests {
}
#[test]
+ fn wrap_with_unicode() {
+ let input = "ab\nc 👩d fgh";
+ let mut message_buffer = MessageBuffer::new();
+ message_buffer.push(Message::new(input.into(), MessageType::Error));
+ let size = SizeInfo::new(7., 10., 1., 1., 0., 0., false);
+
+ let lines = message_buffer.message().unwrap().text(&size);
+
+ assert_eq!(lines, vec![
+ String::from("ab [X]"),
+ String::from("c 👩 d "),
+ String::from("fgh ")
+ ]);
+ }
+
+ #[test]
+ fn strip_whitespace_at_linebreak() {
+ let input = "\n0 1 2 3";
+ let mut message_buffer = MessageBuffer::new();
+ message_buffer.push(Message::new(input.into(), MessageType::Error));
+ let size = SizeInfo::new(3., 10., 1., 1., 0., 0., false);
+
+ let lines = message_buffer.message().unwrap().text(&size);
+
+ assert_eq!(lines, vec![String::from("[X]"), String::from("0 1"), String::from("2 3"),]);
+ }
+
+ #[test]
fn remove_duplicates() {
let mut message_buffer = MessageBuffer::new();
for _ in 0..10 {