1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-26 08:25:50 +03:00

feed unicode version config through to Line for ls-fonts

This makes the reported metrics show correctly for:

```
wezterm -n --config "font=wezterm.font('Noto Sans Mono CJK JP')" \
        --config treat_east_asian_ambiguous_width_as_wide=true \
        ls-fonts --text ".☆a☆☆☆☆"
```

refs: https://github.com/wez/wezterm/issues/1888
This commit is contained in:
Wez Furlong 2022-04-19 20:37:35 -07:00
parent c0cd6b1f29
commit fb635c4362
10 changed files with 68 additions and 42 deletions

View File

@ -510,7 +510,7 @@ mod test {
.collect::<Vec<String>>();
let n_chunks = chunks.len();
for (idx, chunk) in chunks.into_iter().enumerate() {
let mut line = Line::from_text(&chunk, &Default::default(), SEQ_ZERO);
let mut line = Line::from_text(&chunk, &Default::default(), SEQ_ZERO, None);
if idx < n_chunks - 1 {
line.set_last_cell_was_wrapped(true, SEQ_ZERO);
}
@ -877,10 +877,10 @@ mod test {
let attr = Default::default();
let logical = LogicalLine {
physical_lines: vec![
Line::from_text("hello", &attr, SEQ_ZERO),
Line::from_text("yo", &attr, SEQ_ZERO),
Line::from_text("hello", &attr, SEQ_ZERO, None),
Line::from_text("yo", &attr, SEQ_ZERO, None),
],
logical: Line::from_text("helloyo", &attr, SEQ_ZERO),
logical: Line::from_text("helloyo", &attr, SEQ_ZERO, None),
first_row: 0,
};

View File

@ -13,7 +13,7 @@ fn test_789() {
let black = CellAttributes::default()
.set_background(AnsiColor::Black)
.clone();
let mut line = Line::from_text("foo", &black, SEQ_ZERO);
let mut line = Line::from_text("foo", &black, SEQ_ZERO, None);
line.resize(8, 0);
for x in 3..8 {
line.set_cell(x, Cell::blank_with_attrs(black.clone()), 0);

View File

@ -450,7 +450,7 @@ fn test_semantic() {
let mut input = CellAttributes::default();
input.set_semantic_type(SemanticType::Input);
let mut prompt_line = Line::from_text("> ls -l ", &output, SEQ_ZERO);
let mut prompt_line = Line::from_text("> ls -l ", &output, SEQ_ZERO, None);
for i in 0..2 {
prompt_line.cells_mut()[i]
.attrs_mut()
@ -503,11 +503,11 @@ fn test_semantic() {
line!(),
&term.screen().visible_lines(),
&[
Line::from_text("hello ", &output, SEQ_ZERO),
Line::from_text("there ", &output, SEQ_ZERO),
Line::from_text("three ", &output, SEQ_ZERO),
Line::from_text("hello ", &output, SEQ_ZERO, None),
Line::from_text("there ", &output, SEQ_ZERO, None),
Line::from_text("three ", &output, SEQ_ZERO, None),
prompt_line,
Line::from_text("some file ", &output, SEQ_ZERO),
Line::from_text("some file ", &output, SEQ_ZERO, None),
],
Compare::TEXT | Compare::ATTRS,
);
@ -1121,7 +1121,7 @@ fn test_hyperlinks() {
line!(),
&term.screen().visible_lines(),
&[
Line::from_text("hello", &linked, SEQ_ZERO),
Line::from_text("hello", &linked, SEQ_ZERO, None),
" ".into(),
" ".into(),
],
@ -1140,7 +1140,7 @@ fn test_hyperlinks() {
&term.screen().visible_lines(),
&[
Line::from_text_with_wrapped_last_col("hello", &linked, SEQ_ZERO),
Line::from_text("hey!!", &linked, SEQ_ZERO),
Line::from_text("hey!!", &linked, SEQ_ZERO, None),
" ".into(),
],
Compare::TEXT | Compare::ATTRS,
@ -1155,7 +1155,7 @@ fn test_hyperlinks() {
term.soft_reset();
term.print("00t");
let mut partial_line = Line::from_text("wo00t", &CellAttributes::default(), SEQ_ZERO);
let mut partial_line = Line::from_text("wo00t", &CellAttributes::default(), SEQ_ZERO, None);
partial_line.set_cell(
0,
Cell::new(

View File

@ -489,7 +489,7 @@ where
D: Deserializer<'de>,
{
let text = String::deserialize(deserializer)?;
Ok(TeenyString::from_str(&text, None))
Ok(TeenyString::from_str(&text, None, None))
}
#[cfg(feature = "use_serde")]
@ -577,7 +577,11 @@ impl TeenyString {
}
}
pub fn from_str(s: &str, width: Option<usize>) -> Self {
pub fn from_str(
s: &str,
width: Option<usize>,
unicode_version: Option<UnicodeVersion>,
) -> Self {
// De-fang the input text such that it has no special meaning
// to a terminal. All control and movement characters are rewritten
// as a space.
@ -596,7 +600,7 @@ impl TeenyString {
let bytes = s.as_bytes();
let len = bytes.len();
let width = width.unwrap_or_else(|| grapheme_column_width(s, None));
let width = width.unwrap_or_else(|| grapheme_column_width(s, unicode_version));
if len < std::mem::size_of::<usize>() {
debug_assert!(width < 3);
@ -651,7 +655,7 @@ impl TeenyString {
pub fn from_char(c: char) -> Self {
let mut bytes = [0u8; 8];
Self::from_str(c.encode_utf8(&mut bytes), None)
Self::from_str(c.encode_utf8(&mut bytes), None, None)
}
pub fn width(&self) -> usize {
@ -705,7 +709,7 @@ impl std::clone::Clone for TeenyString {
if Self::is_marker_bit_set(self.0) {
Self(self.0)
} else {
Self::from_str(self.str(), None)
Self::from_str(self.str(), None, None)
}
}
}
@ -791,8 +795,12 @@ impl Cell {
/// over. This function technically allows for an arbitrary string to
/// be passed but it should not be used to hold strings other than
/// graphemes.
pub fn new_grapheme(text: &str, attrs: CellAttributes) -> Self {
let storage = TeenyString::from_str(text, None);
pub fn new_grapheme(
text: &str,
attrs: CellAttributes,
unicode_version: Option<UnicodeVersion>,
) -> Self {
let storage = TeenyString::from_str(text, None, unicode_version);
Self {
text: storage,
@ -801,7 +809,7 @@ impl Cell {
}
pub fn new_grapheme_with_width(text: &str, width: usize, attrs: CellAttributes) -> Self {
let storage = TeenyString::from_str(text, Some(width));
let storage = TeenyString::from_str(text, Some(width), None);
Self {
text: storage,
attrs,
@ -955,7 +963,7 @@ mod test {
let s = TeenyString::from_char('a');
assert_eq!(s.as_bytes(), &[b'a']);
let longer = TeenyString::from_str("hellothere", None);
let longer = TeenyString::from_str("hellothere", None, None);
assert_eq!(longer.as_bytes(), b"hellothere");
assert_eq!(
@ -984,7 +992,7 @@ mod test {
}
for g in &["", " ", "\n", "\r", "\t", "\r\n"] {
let cell = Cell::new_grapheme(g, CellAttributes::default());
let cell = Cell::new_grapheme(g, CellAttributes::default(), None);
assert_eq!(cell.str(), " ");
}
}
@ -1006,6 +1014,7 @@ mod test {
let cell = Cell::new_grapheme(
women_holding_hands_dark_skin_tone_medium_light_skin_tone,
CellAttributes::default(),
None,
);
assert_eq!(
cell.str(),
@ -1063,7 +1072,7 @@ mod test {
vec!["x".to_string(), "\u{3000}".to_string(), "x".to_string()],
);
let c = Cell::new_grapheme("\u{3000}", CellAttributes::blank());
let c = Cell::new_grapheme("\u{3000}", CellAttributes::blank(), None);
assert_eq!(c.width(), 2);
}

View File

@ -1,4 +1,4 @@
use crate::cell::{Cell, CellAttributes, SemanticType};
use crate::cell::{Cell, CellAttributes, SemanticType, UnicodeVersion};
use crate::cellcluster::CellCluster;
use crate::hyperlink::Rule;
use crate::surface::{Change, SequenceNo, SEQ_ZERO};
@ -120,11 +120,16 @@ impl Line {
}
}
pub fn from_text(s: &str, attrs: &CellAttributes, seqno: SequenceNo) -> Line {
pub fn from_text(
s: &str,
attrs: &CellAttributes,
seqno: SequenceNo,
unicode_version: Option<UnicodeVersion>,
) -> Line {
let mut cells = Vec::new();
for sub in s.graphemes(true) {
let cell = Cell::new_grapheme(sub, attrs.clone());
let cell = Cell::new_grapheme(sub, attrs.clone(), unicode_version);
let width = cell.width();
cells.push(cell);
for _ in 1..width {
@ -145,7 +150,7 @@ impl Line {
attrs: &CellAttributes,
seqno: SequenceNo,
) -> Line {
let mut line = Self::from_text(s, attrs, seqno);
let mut line = Self::from_text(s, attrs, seqno, None);
line.cells
.last_mut()
.map(|cell| cell.attrs_mut().set_wrapped(true));
@ -445,6 +450,7 @@ impl Line {
Some(ref link) if link.is_implicit() => Some(Cell::new_grapheme(
cell.str(),
cell.attrs().clone().set_hyperlink(None).clone(),
None,
)),
_ => None,
};
@ -661,7 +667,7 @@ impl Line {
seqno: SequenceNo,
) {
for (i, c) in text.graphemes(true).enumerate() {
let cell = Cell::new_grapheme(c, attr.clone());
let cell = Cell::new_grapheme(c, attr.clone(), None);
let width = cell.width();
self.set_cell(i + start_idx, cell, seqno);
@ -922,7 +928,7 @@ impl Line {
impl<'a> From<&'a str> for Line {
fn from(s: &str) -> Line {
Line::from_text(s, &CellAttributes::default(), SEQ_ZERO)
Line::from_text(s, &CellAttributes::default(), SEQ_ZERO, None)
}
}
@ -953,11 +959,11 @@ mod test {
assert_eq!(
line.cells().to_vec(),
vec![
Cell::new_grapheme("", CellAttributes::default()),
Cell::new_grapheme("", CellAttributes::default(), None),
Cell::new(' ', CellAttributes::default()), // double width spacer
Cell::new_grapheme("😍", CellAttributes::default()),
Cell::new_grapheme("😍", CellAttributes::default(), None),
Cell::new(' ', CellAttributes::default()), // double width spacer
Cell::new_grapheme("🤢", CellAttributes::default()),
Cell::new_grapheme("🤢", CellAttributes::default(), None),
Cell::new(' ', CellAttributes::default()), // double width spacer
Cell::new(' ', CellAttributes::default()),
Cell::new('h', hyperlink_attr.clone()),
@ -982,7 +988,8 @@ mod test {
Cell::new_grapheme(
// man: dark skin tone, red hair ZWJ emoji grapheme
"\u{1f468}\u{1f3fe}\u{200d}\u{1f9b0}",
CellAttributes::default()
CellAttributes::default(),
None,
),
Cell::new(' ', CellAttributes::default()), // double width spacer
Cell::new(' ', CellAttributes::default()),

View File

@ -442,7 +442,7 @@ impl Surface {
self.xpos = 0;
}
let cell = Cell::new_grapheme(g, self.attributes.clone());
let cell = Cell::new_grapheme(g, self.attributes.clone(), None);
// the max(1) here is to ensure that we advance to the next cell
// position for zero-width graphemes. We want to make sure that
// they occupy a cell so that we can re-emit them when we output them.

View File

@ -248,7 +248,7 @@ impl RenderableInner {
.set_underline(Underline::Double)
.clone();
let text_line = Line::from_text(text, &attrs, SEQ_ZERO);
let text_line = Line::from_text(text, &attrs, SEQ_ZERO, None);
if row == 0 {
for cell in text_line.cells() {

View File

@ -16,7 +16,7 @@ use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use structopt::StructOpt;
use termwiz::cell::CellAttributes;
use termwiz::cell::{CellAttributes, UnicodeVersion};
use termwiz::surface::{Line, SEQ_ZERO};
use wezterm_bidi::Direction;
use wezterm_client::domain::{ClientDomain, ClientDomainConfig};
@ -701,8 +701,18 @@ pub fn run_ls_fonts(config: config::ConfigHandle, cmd: &LsFontsCommand) -> anyho
None
};
let unicode_version = UnicodeVersion {
version: config.unicode_version,
ambiguous_are_wide: config.treat_east_asian_ambiguous_width_as_wide,
};
if let Some(text) = &cmd.text {
let line = Line::from_text(text, &CellAttributes::default(), SEQ_ZERO);
let line = Line::from_text(
text,
&CellAttributes::default(),
SEQ_ZERO,
Some(unicode_version),
);
let cell_clusters = line.cluster(bidi_hint);
let ft_lib = wezterm_font::ftwrap::Library::new()?;

View File

@ -151,7 +151,7 @@ mod test {
T: Texture2d,
T: std::fmt::Debug,
{
let line = Line::from_text(text, &CellAttributes::default(), SEQ_ZERO);
let line = Line::from_text(text, &CellAttributes::default(), SEQ_ZERO, None);
eprintln!("{:?}", line);
let cell_clusters = line.cluster(None);
assert_eq!(cell_clusters.len(), 1);
@ -294,7 +294,7 @@ mod test {
);
let style = TextStyle::default();
let font = fonts.resolve_font(&style).unwrap();
let line = Line::from_text(&text, &CellAttributes::default(), SEQ_ZERO);
let line = Line::from_text(&text, &CellAttributes::default(), SEQ_ZERO, None);
let cell_clusters = line.cluster(None);
let cluster = &cell_clusters[0];
let presentation_width = PresentationWidth::with_cluster(&cluster);

View File

@ -161,7 +161,7 @@ impl TabBarState {
line: Line::with_width(1, SEQ_ZERO),
items: vec![TabEntry {
item: TabBarItem::None,
title: Line::from_text(" ", &CellAttributes::blank(), 1),
title: Line::from_text(" ", &CellAttributes::blank(), 1, None),
x: 1,
width: 1,
}],
@ -386,7 +386,7 @@ fn parse_status_text(text: &str, default_cell: CellAttributes) -> Line {
fn flush_print(buf: &mut String, cells: &mut Vec<Cell>, pen: &CellAttributes) {
for g in unicode_segmentation::UnicodeSegmentation::graphemes(buf.as_str(), true) {
let cell = Cell::new_grapheme(g, pen.clone());
let cell = Cell::new_grapheme(g, pen.clone(), None);
let width = cell.width();
cells.push(cell);
for _ in 1..width {