Skip to content

Commit 012ac80

Browse files
authored
Fixed alignment issues (#48)
1 parent 73760b1 commit 012ac80

File tree

2 files changed

+48
-33
lines changed

2 files changed

+48
-33
lines changed

cli-table/src/table.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ impl TableStruct {
122122
&self.format,
123123
&color_spec,
124124
)?;
125-
println(&mut buffers)?;
126125

127126
if let Some(ref title) = self.title {
128127
let title_dimension = row_dimensions.next().unwrap();
@@ -148,8 +147,6 @@ impl TableStruct {
148147
&color_spec,
149148
)?
150149
}
151-
152-
println(&mut buffers)?;
153150
}
154151

155152
let mut rows = self.rows.iter().zip(row_dimensions).peekable();
@@ -175,10 +172,6 @@ impl TableStruct {
175172
&color_spec,
176173
)?,
177174
}
178-
179-
if rows.peek().is_some() {
180-
println(&mut buffers)?;
181-
}
182175
}
183176

184177
buffers.into_vec()

cli-table/src/utils.rs

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,64 @@
11
use std::io::{Result, Write};
22

33
use termcolor::{ColorSpec, WriteColor};
4-
use unicode_width::UnicodeWidthStr;
54

65
use crate::{
76
buffers::Buffers,
87
table::{Dimension as TableDimension, HorizontalLine, TableFormat, VerticalLine},
98
};
109

11-
/// NOTE: `display_width()` is ported from https://github.com/phsym/prettytable-rs
10+
const ESC: char = '\x1b';
11+
12+
/// NOTE: `display_width()` is ported from https://docs.rs/ansi-width/0.1.0/src/ansi_width/lib.rs.html#9-55
1213
///
1314
/// Return the display width of a unicode string.
1415
/// This functions takes ANSI-escaped color codes into account.
1516
pub(crate) fn display_width(text: &str) -> usize {
16-
let width = UnicodeWidthStr::width(text);
17-
18-
let mut state = 0;
19-
let mut hidden = 0;
20-
21-
for c in text.chars() {
22-
state = match (state, c) {
23-
(0, '\u{1b}') => 1,
24-
(1, '[') => 2,
25-
(1, _) => 0,
26-
(2, 'm') => 3,
27-
_ => state,
28-
};
29-
30-
// We don't count escape characters as hidden as
31-
// UnicodeWidthStr::width already considers them.
32-
if state > 1 {
33-
hidden += 1;
34-
}
35-
36-
if state == 3 {
37-
state = 0;
17+
let mut width = 0;
18+
let mut chars = text.chars();
19+
20+
// This lint is a false positive, because we use the iterator later, leading to
21+
// ownership issues if we follow the lint.
22+
#[allow(clippy::while_let_on_iterator)]
23+
while let Some(c) = chars.next() {
24+
// ESC starts escape sequences, so we need to take characters until the
25+
// end of the escape sequence.
26+
if c == ESC {
27+
let Some(c) = chars.next() else {
28+
break;
29+
};
30+
match c {
31+
// String terminator character: ends other sequences
32+
// We probably won't encounter this but it's here for completeness.
33+
// Or for if we get passed invalid codes.
34+
'\\' => {
35+
// ignore
36+
}
37+
// Control Sequence Introducer: continue until `\x40-\x7C`
38+
'[' => while !matches!(chars.next(), Some('\x40'..='\x7C') | None) {},
39+
// Operating System Command: continue until ST
40+
']' => {
41+
let mut last = c;
42+
while let Some(new) = chars.next() {
43+
if new == '\x07' || (new == '\\' && last == ESC) {
44+
break;
45+
}
46+
last = new;
47+
}
48+
}
49+
// We don't know what character it is, best bet is to fall back to unicode width
50+
// The ESC is assumed to have 0 width in this case.
51+
_ => {
52+
width += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
53+
}
54+
}
55+
} else {
56+
// If it's a normal character outside an escape sequence, use the
57+
// unicode width.
58+
width += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
3859
}
3960
}
40-
41-
width - hidden
61+
width
4262
}
4363

4464
pub fn transpose<T>(v: Vec<Vec<T>>) -> Vec<Vec<T>> {
@@ -104,6 +124,8 @@ pub(crate) fn print_horizontal_line(
104124
}
105125
}
106126
}
127+
128+
println(buffers)?;
107129
}
108130

109131
Ok(())

0 commit comments

Comments
 (0)