Make WidthDB tab-width-aware
This commit is contained in:
parent
d186291ef7
commit
31bb2de87b
5 changed files with 45 additions and 51 deletions
|
|
@ -4,7 +4,6 @@ use crossterm::style::ContentStyle;
|
||||||
|
|
||||||
use crate::styled::Styled;
|
use crate::styled::Styled;
|
||||||
use crate::widthdb::WidthDB;
|
use crate::widthdb::WidthDB;
|
||||||
use crate::wrap;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
|
|
@ -331,7 +330,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&mut self, widthdb: &mut WidthDB, tab_width: u8, pos: Pos, styled: &Styled) {
|
pub fn write(&mut self, widthdb: &mut WidthDB, pos: Pos, styled: &Styled) {
|
||||||
let frame = self.current_frame();
|
let frame = self.current_frame();
|
||||||
let (xrange, yrange) = match frame.legal_ranges() {
|
let (xrange, yrange) = match frame.legal_ranges() {
|
||||||
Some(ranges) => ranges,
|
Some(ranges) => ranges,
|
||||||
|
|
@ -348,21 +347,17 @@ impl Buffer {
|
||||||
let x = pos.x + col as i32;
|
let x = pos.x + col as i32;
|
||||||
let g = *styled_grapheme.content();
|
let g = *styled_grapheme.content();
|
||||||
let style = *styled_grapheme.style();
|
let style = *styled_grapheme.style();
|
||||||
if g == "\t" {
|
let width = widthdb.grapheme_width(g, col);
|
||||||
let width = wrap::tab_width_at_column(tab_width, col);
|
|
||||||
col += width as usize;
|
col += width as usize;
|
||||||
|
if g == "\t" {
|
||||||
for dx in 0..width {
|
for dx in 0..width {
|
||||||
self.write_grapheme(&xrange, x + dx as i32, y, width, " ", style);
|
self.write_grapheme(&xrange, x + dx as i32, y, width, " ", style);
|
||||||
}
|
}
|
||||||
} else {
|
} else if width > 0 {
|
||||||
let width = widthdb.grapheme_width(g);
|
|
||||||
col += width as usize;
|
|
||||||
if width > 0 {
|
|
||||||
self.write_grapheme(&xrange, x, y, width, g, style);
|
self.write_grapheme(&xrange, x, y, width, g, style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Assumes that `pos.y` is in range.
|
/// Assumes that `pos.y` is in range.
|
||||||
fn write_grapheme(
|
fn write_grapheme(
|
||||||
|
|
|
||||||
31
src/frame.rs
31
src/frame.rs
|
|
@ -6,23 +6,11 @@ use crate::styled::Styled;
|
||||||
use crate::widthdb::WidthDB;
|
use crate::widthdb::WidthDB;
|
||||||
use crate::wrap;
|
use crate::wrap;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Frame {
|
pub struct Frame {
|
||||||
pub(crate) widthdb: WidthDB,
|
pub(crate) widthdb: WidthDB,
|
||||||
pub(crate) buffer: Buffer,
|
pub(crate) buffer: Buffer,
|
||||||
cursor: Option<Pos>,
|
cursor: Option<Pos>,
|
||||||
pub(crate) tab_width: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Frame {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
widthdb: Default::default(),
|
|
||||||
buffer: Default::default(),
|
|
||||||
cursor: None,
|
|
||||||
tab_width: 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Frame {
|
impl Frame {
|
||||||
|
|
@ -62,30 +50,29 @@ impl Frame {
|
||||||
|
|
||||||
/// Determine the width of a grapheme.
|
/// Determine the width of a grapheme.
|
||||||
///
|
///
|
||||||
|
/// If the grapheme is a tab, the column is used to determine its width.
|
||||||
|
///
|
||||||
/// If the width has not been measured yet, it is estimated using the
|
/// If the width has not been measured yet, it is estimated using the
|
||||||
/// Unicode Standard Annex #11.
|
/// Unicode Standard Annex #11.
|
||||||
pub fn grapheme_width(&mut self, grapheme: &str) -> u8 {
|
pub fn grapheme_width(&mut self, grapheme: &str, col: usize) -> u8 {
|
||||||
self.widthdb.grapheme_width(grapheme)
|
self.widthdb.grapheme_width(grapheme, col)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine the width of a string based on its graphemes.
|
/// Determine the width of a string based on its graphemes.
|
||||||
///
|
///
|
||||||
|
/// If a grapheme is a tab, its column is used to determine its width.
|
||||||
|
///
|
||||||
/// If the width of a grapheme has not been measured yet, it is estimated
|
/// If the width of a grapheme has not been measured yet, it is estimated
|
||||||
/// using the Unicode Standard Annex #11.
|
/// using the Unicode Standard Annex #11.
|
||||||
pub fn width(&mut self, s: &str) -> usize {
|
pub fn width(&mut self, s: &str) -> usize {
|
||||||
self.widthdb.width(s)
|
self.widthdb.width(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tab_width_at_column(&self, col: usize) -> u8 {
|
|
||||||
wrap::tab_width_at_column(self.tab_width, col)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wrap(&mut self, text: &str, width: usize) -> Vec<usize> {
|
pub fn wrap(&mut self, text: &str, width: usize) -> Vec<usize> {
|
||||||
wrap::wrap(&mut self.widthdb, self.tab_width, text, width)
|
wrap::wrap(&mut self.widthdb, text, width)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write<S: Into<Styled>>(&mut self, pos: Pos, styled: S) {
|
pub fn write<S: Into<Styled>>(&mut self, pos: Pos, styled: S) {
|
||||||
self.buffer
|
self.buffer.write(&mut self.widthdb, pos, &styled.into());
|
||||||
.write(&mut self.widthdb, self.tab_width, pos, &styled.into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,11 +60,11 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_tab_width(&mut self, tab_width: u8) {
|
pub fn set_tab_width(&mut self, tab_width: u8) {
|
||||||
self.frame.tab_width = tab_width;
|
self.frame.widthdb.tab_width = tab_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tab_width(&self) -> u8 {
|
pub fn tab_width(&self) -> u8 {
|
||||||
self.frame.tab_width
|
self.frame.widthdb.tab_width
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_measuring(&mut self, active: bool) {
|
pub fn set_measuring(&mut self, active: bool) {
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,42 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
/// Measures and stores the with (in terminal coordinates) of graphemes.
|
/// Measures and stores the with (in terminal coordinates) of graphemes.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug)]
|
||||||
pub struct WidthDB {
|
pub struct WidthDB {
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
known: HashMap<String, u8>,
|
known: HashMap<String, u8>,
|
||||||
requested: HashSet<String>,
|
requested: HashSet<String>,
|
||||||
|
pub(crate) tab_width: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for WidthDB {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
active: false,
|
||||||
|
known: Default::default(),
|
||||||
|
requested: Default::default(),
|
||||||
|
tab_width: 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WidthDB {
|
impl WidthDB {
|
||||||
|
/// Determine the width of a tab character starting at the specified column.
|
||||||
|
fn tab_width_at_column(&self, col: usize) -> u8 {
|
||||||
|
self.tab_width - (col % self.tab_width as usize) as u8
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine the width of a grapheme.
|
/// Determine the width of a grapheme.
|
||||||
///
|
///
|
||||||
|
/// If the grapheme is a tab, the column is used to determine its width.
|
||||||
|
///
|
||||||
/// If the width has not been measured yet, it is estimated using the
|
/// If the width has not been measured yet, it is estimated using the
|
||||||
/// Unicode Standard Annex #11.
|
/// Unicode Standard Annex #11.
|
||||||
pub fn grapheme_width(&mut self, grapheme: &str) -> u8 {
|
pub fn grapheme_width(&mut self, grapheme: &str, col: usize) -> u8 {
|
||||||
assert_eq!(Some(grapheme), grapheme.graphemes(true).next());
|
assert_eq!(Some(grapheme), grapheme.graphemes(true).next());
|
||||||
|
if grapheme == "\t" {
|
||||||
|
return self.tab_width_at_column(col);
|
||||||
|
}
|
||||||
if !self.active {
|
if !self.active {
|
||||||
return grapheme.width() as u8;
|
return grapheme.width() as u8;
|
||||||
}
|
}
|
||||||
|
|
@ -36,20 +58,14 @@ impl WidthDB {
|
||||||
|
|
||||||
/// Determine the width of a string based on its graphemes.
|
/// Determine the width of a string based on its graphemes.
|
||||||
///
|
///
|
||||||
|
/// If a grapheme is a tab, its column is used to determine its width.
|
||||||
|
///
|
||||||
/// If the width of a grapheme has not been measured yet, it is estimated
|
/// If the width of a grapheme has not been measured yet, it is estimated
|
||||||
/// using the Unicode Standard Annex #11.
|
/// using the Unicode Standard Annex #11.
|
||||||
pub fn width(&mut self, s: &str) -> usize {
|
pub fn width(&mut self, s: &str) -> usize {
|
||||||
if !self.active {
|
|
||||||
return s.width();
|
|
||||||
}
|
|
||||||
let mut total: usize = 0;
|
let mut total: usize = 0;
|
||||||
for grapheme in s.graphemes(true) {
|
for grapheme in s.graphemes(true) {
|
||||||
total += if let Some(width) = self.known.get(grapheme) {
|
total += self.grapheme_width(grapheme, total) as usize;
|
||||||
(*width).into()
|
|
||||||
} else {
|
|
||||||
self.requested.insert(grapheme.to_string());
|
|
||||||
grapheme.width()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
total
|
total
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use crate::widthdb::WidthDB;
|
use crate::widthdb::WidthDB;
|
||||||
|
|
||||||
pub fn tab_width_at_column(tab_width: u8, col: usize) -> u8 {
|
pub fn wrap(widthdb: &mut WidthDB, text: &str, width: usize) -> Vec<usize> {
|
||||||
tab_width - (col % tab_width as usize) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wrap(widthdb: &mut WidthDB, tab_width: u8, text: &str, width: usize) -> Vec<usize> {
|
|
||||||
let mut breaks = vec![];
|
let mut breaks = vec![];
|
||||||
|
|
||||||
let mut break_options = unicode_linebreak::linebreaks(text).peekable();
|
let mut break_options = unicode_linebreak::linebreaks(text).peekable();
|
||||||
|
|
@ -54,7 +50,7 @@ pub fn wrap(widthdb: &mut WidthDB, tab_width: u8, text: &str, width: usize) -> V
|
||||||
// Calculate widths after current grapheme
|
// Calculate widths after current grapheme
|
||||||
let g_is_whitespace = g.chars().all(|c| c.is_whitespace());
|
let g_is_whitespace = g.chars().all(|c| c.is_whitespace());
|
||||||
let g_width = if g == "\t" {
|
let g_width = if g == "\t" {
|
||||||
tab_width_at_column(tab_width, current_width) as usize
|
widthdb.tab_width_at_column(current_width) as usize
|
||||||
} else {
|
} else {
|
||||||
widthdb.grapheme_width(g) as usize
|
widthdb.grapheme_width(g) as usize
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue