1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-29 21:44:24 +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>>(); .collect::<Vec<String>>();
let n_chunks = chunks.len(); let n_chunks = chunks.len();
for (idx, chunk) in chunks.into_iter().enumerate() { 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 { if idx < n_chunks - 1 {
line.set_last_cell_was_wrapped(true, SEQ_ZERO); line.set_last_cell_was_wrapped(true, SEQ_ZERO);
} }
@ -877,10 +877,10 @@ mod test {
let attr = Default::default(); let attr = Default::default();
let logical = LogicalLine { let logical = LogicalLine {
physical_lines: vec![ physical_lines: vec![
Line::from_text("hello", &attr, SEQ_ZERO), Line::from_text("hello", &attr, SEQ_ZERO, None),
Line::from_text("yo", &attr, SEQ_ZERO), 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, first_row: 0,
}; };

View File

@ -13,7 +13,7 @@ fn test_789() {
let black = CellAttributes::default() let black = CellAttributes::default()
.set_background(AnsiColor::Black) .set_background(AnsiColor::Black)
.clone(); .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); line.resize(8, 0);
for x in 3..8 { for x in 3..8 {
line.set_cell(x, Cell::blank_with_attrs(black.clone()), 0); 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(); let mut input = CellAttributes::default();
input.set_semantic_type(SemanticType::Input); 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 { for i in 0..2 {
prompt_line.cells_mut()[i] prompt_line.cells_mut()[i]
.attrs_mut() .attrs_mut()
@ -503,11 +503,11 @@ fn test_semantic() {
line!(), line!(),
&term.screen().visible_lines(), &term.screen().visible_lines(),
&[ &[
Line::from_text("hello ", &output, SEQ_ZERO), Line::from_text("hello ", &output, SEQ_ZERO, None),
Line::from_text("there ", &output, SEQ_ZERO), Line::from_text("there ", &output, SEQ_ZERO, None),
Line::from_text("three ", &output, SEQ_ZERO), Line::from_text("three ", &output, SEQ_ZERO, None),
prompt_line, prompt_line,
Line::from_text("some file ", &output, SEQ_ZERO), Line::from_text("some file ", &output, SEQ_ZERO, None),
], ],
Compare::TEXT | Compare::ATTRS, Compare::TEXT | Compare::ATTRS,
); );
@ -1121,7 +1121,7 @@ fn test_hyperlinks() {
line!(), line!(),
&term.screen().visible_lines(), &term.screen().visible_lines(),
&[ &[
Line::from_text("hello", &linked, SEQ_ZERO), Line::from_text("hello", &linked, SEQ_ZERO, None),
" ".into(), " ".into(),
" ".into(), " ".into(),
], ],
@ -1140,7 +1140,7 @@ fn test_hyperlinks() {
&term.screen().visible_lines(), &term.screen().visible_lines(),
&[ &[
Line::from_text_with_wrapped_last_col("hello", &linked, SEQ_ZERO), 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(), " ".into(),
], ],
Compare::TEXT | Compare::ATTRS, Compare::TEXT | Compare::ATTRS,
@ -1155,7 +1155,7 @@ fn test_hyperlinks() {
term.soft_reset(); term.soft_reset();
term.print("00t"); 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( partial_line.set_cell(
0, 0,
Cell::new( Cell::new(

View File

@ -489,7 +489,7 @@ where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let text = String::deserialize(deserializer)?; let text = String::deserialize(deserializer)?;
Ok(TeenyString::from_str(&text, None)) Ok(TeenyString::from_str(&text, None, None))
} }
#[cfg(feature = "use_serde")] #[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 // De-fang the input text such that it has no special meaning
// to a terminal. All control and movement characters are rewritten // to a terminal. All control and movement characters are rewritten
// as a space. // as a space.
@ -596,7 +600,7 @@ impl TeenyString {
let bytes = s.as_bytes(); let bytes = s.as_bytes();
let len = bytes.len(); 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>() { if len < std::mem::size_of::<usize>() {
debug_assert!(width < 3); debug_assert!(width < 3);
@ -651,7 +655,7 @@ impl TeenyString {
pub fn from_char(c: char) -> Self { pub fn from_char(c: char) -> Self {
let mut bytes = [0u8; 8]; 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 { pub fn width(&self) -> usize {
@ -705,7 +709,7 @@ impl std::clone::Clone for TeenyString {
if Self::is_marker_bit_set(self.0) { if Self::is_marker_bit_set(self.0) {
Self(self.0) Self(self.0)
} else { } 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 /// over. This function technically allows for an arbitrary string to
/// be passed but it should not be used to hold strings other than /// be passed but it should not be used to hold strings other than
/// graphemes. /// graphemes.
pub fn new_grapheme(text: &str, attrs: CellAttributes) -> Self { pub fn new_grapheme(
let storage = TeenyString::from_str(text, None); text: &str,
attrs: CellAttributes,
unicode_version: Option<UnicodeVersion>,
) -> Self {
let storage = TeenyString::from_str(text, None, unicode_version);
Self { Self {
text: storage, text: storage,
@ -801,7 +809,7 @@ impl Cell {
} }
pub fn new_grapheme_with_width(text: &str, width: usize, attrs: CellAttributes) -> Self { 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 { Self {
text: storage, text: storage,
attrs, attrs,
@ -955,7 +963,7 @@ mod test {
let s = TeenyString::from_char('a'); let s = TeenyString::from_char('a');
assert_eq!(s.as_bytes(), &[b'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!(longer.as_bytes(), b"hellothere");
assert_eq!( assert_eq!(
@ -984,7 +992,7 @@ mod test {
} }
for g in &["", " ", "\n", "\r", "\t", "\r\n"] { 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(), " "); assert_eq!(cell.str(), " ");
} }
} }
@ -1006,6 +1014,7 @@ mod test {
let cell = Cell::new_grapheme( let cell = Cell::new_grapheme(
women_holding_hands_dark_skin_tone_medium_light_skin_tone, women_holding_hands_dark_skin_tone_medium_light_skin_tone,
CellAttributes::default(), CellAttributes::default(),
None,
); );
assert_eq!( assert_eq!(
cell.str(), cell.str(),
@ -1063,7 +1072,7 @@ mod test {
vec!["x".to_string(), "\u{3000}".to_string(), "x".to_string()], 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); 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::cellcluster::CellCluster;
use crate::hyperlink::Rule; use crate::hyperlink::Rule;
use crate::surface::{Change, SequenceNo, SEQ_ZERO}; 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(); let mut cells = Vec::new();
for sub in s.graphemes(true) { 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(); let width = cell.width();
cells.push(cell); cells.push(cell);
for _ in 1..width { for _ in 1..width {
@ -145,7 +150,7 @@ impl Line {
attrs: &CellAttributes, attrs: &CellAttributes,
seqno: SequenceNo, seqno: SequenceNo,
) -> Line { ) -> Line {
let mut line = Self::from_text(s, attrs, seqno); let mut line = Self::from_text(s, attrs, seqno, None);
line.cells line.cells
.last_mut() .last_mut()
.map(|cell| cell.attrs_mut().set_wrapped(true)); .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( Some(ref link) if link.is_implicit() => Some(Cell::new_grapheme(
cell.str(), cell.str(),
cell.attrs().clone().set_hyperlink(None).clone(), cell.attrs().clone().set_hyperlink(None).clone(),
None,
)), )),
_ => None, _ => None,
}; };
@ -661,7 +667,7 @@ impl Line {
seqno: SequenceNo, seqno: SequenceNo,
) { ) {
for (i, c) in text.graphemes(true).enumerate() { 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(); let width = cell.width();
self.set_cell(i + start_idx, cell, seqno); self.set_cell(i + start_idx, cell, seqno);
@ -922,7 +928,7 @@ impl Line {
impl<'a> From<&'a str> for Line { impl<'a> From<&'a str> for Line {
fn from(s: &str) -> 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!( assert_eq!(
line.cells().to_vec(), line.cells().to_vec(),
vec![ vec![
Cell::new_grapheme("", CellAttributes::default()), Cell::new_grapheme("", CellAttributes::default(), None),
Cell::new(' ', CellAttributes::default()), // double width spacer 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()), // 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()), // double width spacer
Cell::new(' ', CellAttributes::default()), Cell::new(' ', CellAttributes::default()),
Cell::new('h', hyperlink_attr.clone()), Cell::new('h', hyperlink_attr.clone()),
@ -982,7 +988,8 @@ mod test {
Cell::new_grapheme( Cell::new_grapheme(
// man: dark skin tone, red hair ZWJ emoji grapheme // man: dark skin tone, red hair ZWJ emoji grapheme
"\u{1f468}\u{1f3fe}\u{200d}\u{1f9b0}", "\u{1f468}\u{1f3fe}\u{200d}\u{1f9b0}",
CellAttributes::default() CellAttributes::default(),
None,
), ),
Cell::new(' ', CellAttributes::default()), // double width spacer Cell::new(' ', CellAttributes::default()), // double width spacer
Cell::new(' ', CellAttributes::default()), Cell::new(' ', CellAttributes::default()),

View File

@ -442,7 +442,7 @@ impl Surface {
self.xpos = 0; 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 // 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 // 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. // 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) .set_underline(Underline::Double)
.clone(); .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 { if row == 0 {
for cell in text_line.cells() { for cell in text_line.cells() {

View File

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

View File

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

View File

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