зеркало из
https://github.com/glebtv/tui-textarea.git
synced 2026-09-07 16:05:50 +03:00
Merge branch 'issue-43' (fix #43)
Этот коммит содержится в:
256
src/highlight.rs
256
src/highlight.rs
@@ -40,39 +40,54 @@ impl Boundary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_display_text(s: &str, tab_len: u8, mask: Option<char>) -> Cow<'_, str> {
|
struct DisplayTextBuilder {
|
||||||
if let Some(ch) = mask {
|
tab_len: u8,
|
||||||
// No tab character processing in the mask case
|
width: usize,
|
||||||
let masked = iter::repeat(ch).take(s.chars().count()).collect();
|
mask: Option<char>,
|
||||||
return Cow::Owned(masked);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let tab = spaces(tab_len);
|
impl DisplayTextBuilder {
|
||||||
let mut buf = String::new();
|
fn new(tab_len: u8, mask: Option<char>) -> Self {
|
||||||
let mut width = 0;
|
Self {
|
||||||
for (i, c) in s.char_indices() {
|
tab_len,
|
||||||
if c == '\t' {
|
width: 0,
|
||||||
if buf.is_empty() {
|
mask,
|
||||||
buf.reserve(s.len());
|
|
||||||
buf.push_str(&s[..i]);
|
|
||||||
}
|
|
||||||
if tab_len > 0 {
|
|
||||||
let len = tab_len as usize - (width % tab_len as usize);
|
|
||||||
buf.push_str(&tab[..len]);
|
|
||||||
width += len;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !buf.is_empty() {
|
|
||||||
buf.push(c);
|
|
||||||
}
|
|
||||||
width += c.width().unwrap_or(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !buf.is_empty() {
|
fn build<'s>(&mut self, s: &'s str) -> Cow<'s, str> {
|
||||||
Cow::Owned(buf)
|
if let Some(ch) = self.mask {
|
||||||
} else {
|
// No tab character processing in the mask case
|
||||||
Cow::Borrowed(s)
|
let masked = iter::repeat(ch).take(s.chars().count()).collect();
|
||||||
|
return Cow::Owned(masked);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tab = spaces(self.tab_len);
|
||||||
|
let mut buf = String::new();
|
||||||
|
for (i, c) in s.char_indices() {
|
||||||
|
if c == '\t' {
|
||||||
|
if buf.is_empty() {
|
||||||
|
buf.reserve(s.len());
|
||||||
|
buf.push_str(&s[..i]);
|
||||||
|
}
|
||||||
|
if self.tab_len > 0 {
|
||||||
|
let len = self.tab_len as usize - (self.width % self.tab_len as usize);
|
||||||
|
buf.push_str(&tab[..len]);
|
||||||
|
self.width += len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !buf.is_empty() {
|
||||||
|
buf.push(c);
|
||||||
|
}
|
||||||
|
self.width += c.width().unwrap_or(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !buf.is_empty() {
|
||||||
|
Cow::Owned(buf)
|
||||||
|
} else {
|
||||||
|
Cow::Borrowed(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,12 +154,10 @@ impl<'a> LineHighlighter<'a> {
|
|||||||
cursor_at_end,
|
cursor_at_end,
|
||||||
mask,
|
mask,
|
||||||
} = self;
|
} = self;
|
||||||
|
let mut builder = DisplayTextBuilder::new(tab_len, mask);
|
||||||
|
|
||||||
if boundaries.is_empty() {
|
if boundaries.is_empty() {
|
||||||
spans.push(Span::styled(
|
spans.push(Span::styled(builder.build(line), style_begin));
|
||||||
line_display_text(line, tab_len, mask),
|
|
||||||
style_begin,
|
|
||||||
));
|
|
||||||
if cursor_at_end {
|
if cursor_at_end {
|
||||||
spans.push(Span::styled(" ", cursor_style));
|
spans.push(Span::styled(" ", cursor_style));
|
||||||
}
|
}
|
||||||
@@ -161,87 +174,122 @@ impl<'a> LineHighlighter<'a> {
|
|||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
let mut stack = vec![];
|
let mut stack = vec![];
|
||||||
|
|
||||||
loop {
|
while let Some((next_boundary, end)) = boundaries.next() {
|
||||||
if let Some((next_boundary, end)) = boundaries.next() {
|
if start < end {
|
||||||
if start < end {
|
spans.push(Span::styled(builder.build(&line[start..end]), style));
|
||||||
spans.push(Span::styled(
|
|
||||||
line_display_text(&line[start..end], tab_len, mask),
|
|
||||||
style,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
style = if let Some(s) = next_boundary.style() {
|
|
||||||
stack.push(style);
|
|
||||||
s
|
|
||||||
} else {
|
|
||||||
stack.pop().unwrap_or(style_begin)
|
|
||||||
};
|
|
||||||
start = end;
|
|
||||||
} else {
|
|
||||||
if start != line.len() {
|
|
||||||
spans.push(Span::styled(
|
|
||||||
line_display_text(&line[start..], tab_len, mask),
|
|
||||||
style,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if cursor_at_end {
|
|
||||||
spans.push(Span::styled(" ", cursor_style));
|
|
||||||
}
|
|
||||||
return Line::from(spans);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
style = if let Some(s) = next_boundary.style() {
|
||||||
|
stack.push(style);
|
||||||
|
s
|
||||||
|
} else {
|
||||||
|
stack.pop().unwrap_or(style_begin)
|
||||||
|
};
|
||||||
|
start = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if start != line.len() {
|
||||||
|
spans.push(Span::styled(builder.build(&line[start..]), style));
|
||||||
|
}
|
||||||
|
if cursor_at_end {
|
||||||
|
spans.push(Span::styled(" ", cursor_style));
|
||||||
|
}
|
||||||
|
Line::from(spans)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn build(text: &'static str, tab: u8, mask: Option<char>) -> Cow<'static, str> {
|
||||||
|
DisplayTextBuilder::new(tab, mask).build(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn build_with_offset(offset: usize, text: &'static str, tab: u8) -> (Cow<'static, str>, usize) {
|
||||||
|
let mut b = DisplayTextBuilder::new(tab, None);
|
||||||
|
b.width = offset;
|
||||||
|
(b.build(text), b.width)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn test_line_display_text() {
|
fn test_line_display_text() {
|
||||||
assert_eq!(&line_display_text( "", 0, None), "");
|
assert_eq!(&build( "", 0, None), "");
|
||||||
assert_eq!(&line_display_text( "", 4, None), "");
|
assert_eq!(&build( "", 4, None), "");
|
||||||
assert_eq!(&line_display_text( "", 8, None), "");
|
assert_eq!(&build( "", 8, None), "");
|
||||||
assert_eq!(&line_display_text( "", 0, Some('x')), "");
|
assert_eq!(&build( "", 0, Some('x')), "");
|
||||||
assert_eq!(&line_display_text( "", 4, Some('x')), "");
|
assert_eq!(&build( "", 4, Some('x')), "");
|
||||||
assert_eq!(&line_display_text( "", 8, Some('x')), "");
|
assert_eq!(&build( "", 8, Some('x')), "");
|
||||||
assert_eq!(&line_display_text( "a", 0, None), "a");
|
assert_eq!(&build( "a", 0, None), "a");
|
||||||
assert_eq!(&line_display_text( "a", 4, None), "a");
|
assert_eq!(&build( "a", 4, None), "a");
|
||||||
assert_eq!(&line_display_text( "a", 8, None), "a");
|
assert_eq!(&build( "a", 8, None), "a");
|
||||||
assert_eq!(&line_display_text( "a", 0, Some('x')), "x");
|
assert_eq!(&build( "a", 0, Some('x')), "x");
|
||||||
assert_eq!(&line_display_text( "a", 4, Some('x')), "x");
|
assert_eq!(&build( "a", 4, Some('x')), "x");
|
||||||
assert_eq!(&line_display_text( "a", 8, Some('x')), "x");
|
assert_eq!(&build( "a", 8, Some('x')), "x");
|
||||||
assert_eq!(&line_display_text( "a\t", 0, None), "a");
|
assert_eq!(&build( "a\t", 0, None), "a");
|
||||||
assert_eq!(&line_display_text( "a\t", 4, None), "a ");
|
assert_eq!(&build( "a\t", 4, None), "a ");
|
||||||
assert_eq!(&line_display_text( "a\t", 8, None), "a ");
|
assert_eq!(&build( "a\t", 8, None), "a ");
|
||||||
assert_eq!(&line_display_text( "a\t", 0, Some('x')), "xx");
|
assert_eq!(&build( "a\t", 0, Some('x')), "xx");
|
||||||
assert_eq!(&line_display_text( "a\t", 4, Some('x')), "xx");
|
assert_eq!(&build( "a\t", 4, Some('x')), "xx");
|
||||||
assert_eq!(&line_display_text( "a\t", 8, Some('x')), "xx");
|
assert_eq!(&build( "a\t", 8, Some('x')), "xx");
|
||||||
assert_eq!(&line_display_text( "\t", 0, None), "\t");
|
assert_eq!(&build( "\t", 0, None), "\t");
|
||||||
assert_eq!(&line_display_text( "\t", 4, None), " ");
|
assert_eq!(&build( "\t", 4, None), " ");
|
||||||
assert_eq!(&line_display_text( "\t", 8, None), " ");
|
assert_eq!(&build( "\t", 8, None), " ");
|
||||||
assert_eq!(&line_display_text( "\t", 0, Some('x')), "x");
|
assert_eq!(&build( "\t", 0, Some('x')), "x");
|
||||||
assert_eq!(&line_display_text( "\t", 4, Some('x')), "x");
|
assert_eq!(&build( "\t", 4, Some('x')), "x");
|
||||||
assert_eq!(&line_display_text( "\t", 8, Some('x')), "x");
|
assert_eq!(&build( "\t", 8, Some('x')), "x");
|
||||||
assert_eq!(&line_display_text( "a\tb", 0, None), "ab");
|
assert_eq!(&build( "a\tb", 0, None), "ab");
|
||||||
assert_eq!(&line_display_text( "a\tb", 4, None), "a b");
|
assert_eq!(&build( "a\tb", 4, None), "a b");
|
||||||
assert_eq!(&line_display_text( "a\tb", 8, None), "a b");
|
assert_eq!(&build( "a\tb", 8, None), "a b");
|
||||||
assert_eq!(&line_display_text( "a\tb", 0, Some('x')), "xxx");
|
assert_eq!(&build( "a\tb", 0, Some('x')), "xxx");
|
||||||
assert_eq!(&line_display_text( "a\tb", 4, Some('x')), "xxx");
|
assert_eq!(&build( "a\tb", 4, Some('x')), "xxx");
|
||||||
assert_eq!(&line_display_text( "a\tb", 8, Some('x')), "xxx");
|
assert_eq!(&build( "a\tb", 8, Some('x')), "xxx");
|
||||||
assert_eq!(&line_display_text("a\t\tb", 0, None), "ab");
|
assert_eq!(&build("a\t\tb", 0, None), "ab");
|
||||||
assert_eq!(&line_display_text("a\t\tb", 4, None), "a b");
|
assert_eq!(&build("a\t\tb", 4, None), "a b");
|
||||||
assert_eq!(&line_display_text("a\t\tb", 8, None), "a b");
|
assert_eq!(&build("a\t\tb", 8, None), "a b");
|
||||||
assert_eq!(&line_display_text("a\t\tb", 0, Some('x')), "xxxx");
|
assert_eq!(&build("a\t\tb", 0, Some('x')), "xxxx");
|
||||||
assert_eq!(&line_display_text("a\t\tb", 4, Some('x')), "xxxx");
|
assert_eq!(&build("a\t\tb", 4, Some('x')), "xxxx");
|
||||||
assert_eq!(&line_display_text("a\t\tb", 8, Some('x')), "xxxx");
|
assert_eq!(&build("a\t\tb", 8, Some('x')), "xxxx");
|
||||||
assert_eq!(&line_display_text("ab\t\t", 0, None), "ab");
|
assert_eq!(&build("a\tb\tc", 0, None), "abc");
|
||||||
assert_eq!(&line_display_text("ab\t\t", 4, None), "ab ");
|
assert_eq!(&build("a\tb\tc", 4, None), "a b c");
|
||||||
assert_eq!(&line_display_text("ab\t\t", 8, None), "ab ");
|
assert_eq!(&build("a\tb\tc", 8, None), "a b c");
|
||||||
assert_eq!(&line_display_text("abcd\t", 4, None), "abcd ");
|
assert_eq!(&build("a\tb\tc", 0, Some('x')), "xxxxx");
|
||||||
assert_eq!(&line_display_text( "あ\t", 0, None), "あ");
|
assert_eq!(&build("a\tb\tc", 4, Some('x')), "xxxxx");
|
||||||
assert_eq!(&line_display_text( "あ\t", 4, None), "あ ");
|
assert_eq!(&build("a\tb\tc", 8, Some('x')), "xxxxx");
|
||||||
assert_eq!(&line_display_text( "🐶\t", 4, None), "🐶 ");
|
assert_eq!(&build("ab\t\t", 0, None), "ab");
|
||||||
assert_eq!(&line_display_text( "あ\t", 4, Some('x')), "xx");
|
assert_eq!(&build("ab\t\t", 4, None), "ab ");
|
||||||
|
assert_eq!(&build("ab\t\t", 8, None), "ab ");
|
||||||
|
assert_eq!(&build("abcd\t", 4, None), "abcd ");
|
||||||
|
assert_eq!(&build( "あ\t", 0, None), "あ");
|
||||||
|
assert_eq!(&build( "あ\t", 4, None), "あ ");
|
||||||
|
assert_eq!(&build( "🐶\t", 4, None), "🐶 ");
|
||||||
|
assert_eq!(&build( "あ\t", 4, Some('x')), "xx");
|
||||||
|
|
||||||
|
// When the start position of the text is not start of the line (#43)
|
||||||
|
assert_eq!(build_with_offset(1, "", 0), ( "".into(), 1));
|
||||||
|
assert_eq!(build_with_offset(1, "a", 0), ( "a".into(), 2));
|
||||||
|
assert_eq!(build_with_offset(1, "あ", 0), ( "あ".into(), 3));
|
||||||
|
assert_eq!(build_with_offset(1, "\t", 4), ( " ".into(), 4));
|
||||||
|
assert_eq!(build_with_offset(1, "a\t", 4), ( "a ".into(), 4));
|
||||||
|
assert_eq!(build_with_offset(1, "あ\t", 4), ( "あ ".into(), 4));
|
||||||
|
assert_eq!(build_with_offset(2, "\t", 4), ( " ".into(), 4));
|
||||||
|
assert_eq!(build_with_offset(2, "a\t", 4), ( "a ".into(), 4));
|
||||||
|
assert_eq!(build_with_offset(2, "あ\t", 4), ( "あ ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(3, "a\t", 4), ( "a ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(4, "\t", 4), ( " ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(4, "a\t", 4), ( "a ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(4, "あ\t", 4), ( "あ ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(5, "\t", 4), ( " ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(5, "a\t", 4), ( "a ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(5, "あ\t", 4), ( "あ ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(2, "\t\t", 4), ( " ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(2, "a\ta\t", 4), ( "a a ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(1, "あ\tあ\t", 4), ( "あ あ ".into(), 8));
|
||||||
|
assert_eq!(build_with_offset(2, "あ\tあ\t", 4), ("あ あ ".into(), 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add tests for LineHighlighter
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user