Skip to content

Commit

Permalink
tabled/ Refactorings wrap
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiburt committed Feb 18, 2025
1 parent a17fbd3 commit 69479aa
Showing 1 changed file with 90 additions and 101 deletions.
191 changes: 90 additions & 101 deletions tabled/src/settings/width/wrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,130 +462,94 @@ fn split_keeping_words(text: &str, width: usize, prefix: &str, suffix: &str) ->
return String::new();
}

let stripped_text = ansi_str::AnsiStr::ansi_strip(text);
let mut word_width = 0;
let mut word_chars = 0;
let mut blocks = parsing::Blocks::new(ansi_str::get_blocks(text));
let mut buf = parsing::MultilineBuffer::new(width);
buf.set_prefix(prefix);
buf.set_suffix(suffix);

for c in stripped_text.chars() {
match c {
' ' => {
parsing::handle_word(&mut buf, &mut blocks, word_chars, word_width, 1);
word_chars = 0;
word_width = 0;
}
'\n' => {
parsing::handle_word(&mut buf, &mut blocks, word_chars, word_width, 1);
word_chars = 0;
word_width = 0;
}
_ => {
word_width += std::cmp::max(1, get_char_width(c));
word_chars += 1;
}
}
}

if word_chars > 0 {
parsing::handle_word(&mut buf, &mut blocks, word_chars, word_width, 0);
buf.finish_line(&blocks);
}

buf.into_string()
parsing::split_text(text, width, prefix, suffix)
}

#[cfg(feature = "ansi")]
mod parsing {
use super::get_char_width;
use ansi_str::{AnsiBlock, AnsiBlockIter, Style};
use ansi_str::{get_blocks, AnsiBlock, AnsiBlockIter, Style};
use std::fmt::Write;

pub(super) struct Blocks<'a> {
struct TextBlocks<'a> {
iter: AnsiBlockIter<'a>,
current: Option<RelativeBlock<'a>>,
}

impl<'a> Blocks<'a> {
pub(super) fn new(iter: AnsiBlockIter<'a>) -> Self {
impl<'a> TextBlocks<'a> {
fn new(text: &'a str) -> Self {
Self {
iter,
iter: get_blocks(text),
current: None,
}
}

pub(super) fn next_block(&mut self) -> Option<RelativeBlock<'a>> {
fn next(&mut self) -> Option<RelativeBlock<'a>> {
self.current
.take()
.or_else(|| self.iter.next().map(RelativeBlock::new))
}
}

pub(super) struct RelativeBlock<'a> {
struct RelativeBlock<'a> {
block: AnsiBlock<'a>,
pos: usize,
}

impl<'a> RelativeBlock<'a> {
pub(super) fn new(block: AnsiBlock<'a>) -> Self {
fn new(block: AnsiBlock<'a>) -> Self {
Self { block, pos: 0 }
}

pub(super) fn get_text(&self) -> &str {
fn get_text(&self) -> &str {
&self.block.text()[self.pos..]
}

pub(super) fn get_origin(&self) -> &str {
fn get_origin(&self) -> &str {
self.block.text()
}

pub(super) fn get_style(&self) -> &Style {
fn get_style(&self) -> &Style {
self.block.style()
}
}

pub(super) struct MultilineBuffer<'a> {
pub buf: String,
pub width_last: usize,
pub width: usize,
pub prefix: &'a str,
pub suffix: &'a str,
struct MultilineBuffer<'text, 'color> {
buf: String,
width_last: usize,
width: usize,
prefix: &'color str,
suffix: &'color str,
blocks: TextBlocks<'text>,
}

impl<'a> MultilineBuffer<'a> {
pub(super) fn new(width: usize) -> Self {
impl<'text, 'color> MultilineBuffer<'text, 'color> {
fn new(text: &'text str, width: usize, prefix: &'color str, suffix: &'color str) -> Self {
let blocks = TextBlocks::new(text);

Self {
buf: String::new(),
width_last: 0,
prefix: "",
suffix: "",
prefix,
suffix,
width,
blocks,
}
}

pub(super) fn into_string(self) -> String {
fn into_string(self) -> String {
self.buf
}

pub(super) fn set_suffix(&mut self, suffix: &'a str) {
self.suffix = suffix;
}

pub(super) fn set_prefix(&mut self, prefix: &'a str) {
self.prefix = prefix;
}

pub(super) fn max_width(&self) -> usize {
fn max_width(&self) -> usize {
self.width
}

pub(super) fn available_width(&self) -> usize {
fn available_width(&self) -> usize {
self.width - self.width_last
}

pub(super) fn fill(&mut self, c: char) -> usize {
fn fill(&mut self, c: char) -> usize {
debug_assert_eq!(get_char_width(c), 1);

let rest_width = self.available_width();
Expand All @@ -596,8 +560,8 @@ mod parsing {
rest_width
}

pub(super) fn set_next_line(&mut self, blocks: &Blocks<'_>) {
if let Some(block) = &blocks.current {
fn set_next_line(&mut self) {
if let Some(block) = &self.blocks.current {
let _ = self
.buf
.write_fmt(format_args!("{}", block.get_style().end()));
Expand All @@ -611,15 +575,15 @@ mod parsing {

self.buf.push_str(self.prefix);

if let Some(block) = &blocks.current {
if let Some(block) = &self.blocks.current {
let _ = self
.buf
.write_fmt(format_args!("{}", block.get_style().start()));
}
}

pub(super) fn finish_line(&mut self, blocks: &Blocks<'_>) {
if let Some(block) = &blocks.current {
fn finish_line(&mut self) {
if let Some(block) = &self.blocks.current {
let _ = self
.buf
.write_fmt(format_args!("{}", block.get_style().end()));
Expand All @@ -631,7 +595,7 @@ mod parsing {
self.width_last = 0;
}

pub(super) fn read_chars(&mut self, block: &RelativeBlock<'_>, n: usize) -> (usize, usize) {
fn read_chars(&mut self, block: &RelativeBlock<'_>, n: usize) -> (usize, usize) {
let mut count_chars = 0;
let mut count_bytes = 0;
for c in block.get_text().chars() {
Expand Down Expand Up @@ -675,11 +639,7 @@ mod parsing {
(count_chars, count_bytes)
}

pub(super) fn read_chars_unchecked(
&mut self,
block: &RelativeBlock<'_>,
n: usize,
) -> (usize, usize) {
fn read_chars_unchecked(&mut self, block: &RelativeBlock<'_>, n: usize) -> (usize, usize) {
let mut count_chars = 0;
let mut count_bytes = 0;
for c in block.get_text().chars() {
Expand All @@ -702,11 +662,12 @@ mod parsing {
}
}

pub(super) fn read_chars(buf: &mut MultilineBuffer<'_>, blocks: &mut Blocks<'_>, n: usize) {
fn read_chars(buf: &mut MultilineBuffer<'_, '_>, n: usize) {
let mut n = n;
while n > 0 {
let is_new_block = blocks.current.is_none();
let mut block = blocks.next_block().expect("Must never happen");
let is_new_block = buf.blocks.current.is_none();
let mut block = buf.blocks.next().expect("Must never happen");

if is_new_block {
buf.buf.push_str(buf.prefix);
let _ = buf
Expand All @@ -722,22 +683,18 @@ mod parsing {
.write_fmt(format_args!("{}", block.get_style().end()));
} else {
block.pos += read_bytes;
blocks.current = Some(block);
buf.blocks.current = Some(block);
}

n -= read_count;
}
}

pub(super) fn read_chars_unchecked(
buf: &mut MultilineBuffer<'_>,
blocks: &mut Blocks<'_>,
n: usize,
) {
fn read_chars_unchecked(buf: &mut MultilineBuffer<'_, '_>, n: usize) {
let mut n = n;
while n > 0 {
let is_new_block = blocks.current.is_none();
let mut block = blocks.next_block().expect("Must never happen");
let is_new_block = buf.blocks.current.is_none();
let mut block = buf.blocks.next().expect("Must never happen");

if is_new_block {
buf.buf.push_str(buf.prefix);
Expand All @@ -754,16 +711,15 @@ mod parsing {
.write_fmt(format_args!("{}", block.get_style().end()));
} else {
block.pos += read_bytes;
blocks.current = Some(block);
buf.blocks.current = Some(block);
}

n -= read_count;
}
}

pub(super) fn handle_word(
buf: &mut MultilineBuffer<'_>,
blocks: &mut Blocks<'_>,
fn handle_word(
buf: &mut MultilineBuffer<'_, '_>,
word_chars: usize,
word_width: usize,
additional_read: usize,
Expand All @@ -773,17 +729,17 @@ mod parsing {
let is_word_too_big = word_width > buf.max_width();

if is_word_too_big {
read_chars(buf, blocks, word_chars + additional_read);
read_chars(buf, word_chars + additional_read);
} else if has_line_space {
read_chars_unchecked(buf, blocks, word_chars);
read_chars_unchecked(buf, word_chars);
if additional_read > 0 {
read_chars(buf, blocks, additional_read);
read_chars(buf, additional_read);
}
} else {
buf.set_next_line(&*blocks);
read_chars_unchecked(buf, blocks, word_chars);
buf.set_next_line();
read_chars_unchecked(buf, word_chars);
if additional_read > 0 {
read_chars(buf, blocks, additional_read);
read_chars(buf, additional_read);
}
}

Expand All @@ -792,12 +748,45 @@ mod parsing {

let has_current_line_space = additional_read <= buf.available_width();
if has_current_line_space {
read_chars_unchecked(buf, blocks, additional_read);
read_chars_unchecked(buf, additional_read);
} else {
buf.set_next_line(&*blocks);
read_chars_unchecked(buf, blocks, additional_read);
buf.set_next_line();
read_chars_unchecked(buf, additional_read);
}
}

pub(super) fn split_text(text: &str, width: usize, prefix: &str, suffix: &str) -> String {
let mut word_width = 0;
let mut word_chars = 0;
let mut buf = MultilineBuffer::new(text, width, prefix, suffix);

let stripped_text = ansi_str::AnsiStr::ansi_strip(text);
for c in stripped_text.chars() {
match c {
' ' => {
handle_word(&mut buf, word_chars, word_width, 1);
word_chars = 0;
word_width = 0;
}
'\n' => {
handle_word(&mut buf, word_chars, word_width, 1);
word_chars = 0;
word_width = 0;
}
_ => {
word_width += std::cmp::max(1, get_char_width(c));
word_chars += 1;
}
}
}

if word_chars > 0 {
handle_word(&mut buf, word_chars, word_width, 0);
buf.finish_line();
}

buf.into_string()
}
}

fn split_string_at(text: &str, at: usize) -> (&str, &str, (usize, usize)) {
Expand Down

0 comments on commit 69479aa

Please sign in to comment.