mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
refactor: simplify the text wrapper (#3808)
This commit is contained in:
parent
5c05350d52
commit
e2fae151a5
8 changed files with 107 additions and 164 deletions
|
|
@ -71,9 +71,12 @@ pub(super) fn lines(lua: &Lua) -> mlua::Result<Value> {
|
||||||
let b = s.as_bytes();
|
let b = s.as_bytes();
|
||||||
let s = &*String::from_utf8_lossy(&b);
|
let s = &*String::from_utf8_lossy(&b);
|
||||||
|
|
||||||
|
let parsed;
|
||||||
let tab_size = opts.raw_get("tab_size")?;
|
let tab_size = opts.raw_get("tab_size")?;
|
||||||
|
|
||||||
let mut it = if opts.raw_get("ansi")? {
|
let mut it = if opts.raw_get("ansi")? {
|
||||||
LineIter::parsed(s.to_text().into_lua_err()?.lines, tab_size)
|
parsed = s.to_text().into_lua_err()?.lines;
|
||||||
|
LineIter::parsed(&parsed, tab_size)
|
||||||
} else {
|
} else {
|
||||||
LineIter::source(&s, tab_size)
|
LineIter::source(&s, tab_size)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
28
yazi-plugin/src/external/highlighter.rs
vendored
28
yazi-plugin/src/external/highlighter.rs
vendored
|
|
@ -8,7 +8,6 @@ use yazi_shared::{Id, Ids, errors::PeekError, replace_to_printable};
|
||||||
use yazi_shim::ratatui::LineIter;
|
use yazi_shim::ratatui::LineIter;
|
||||||
|
|
||||||
static INCR: Ids = Ids::new();
|
static INCR: Ids = Ids::new();
|
||||||
static SYNTECT: OnceLock<(Theme, SyntaxSet)> = OnceLock::new();
|
|
||||||
|
|
||||||
pub struct Highlighter {
|
pub struct Highlighter {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
|
@ -37,8 +36,10 @@ impl Highlighter {
|
||||||
where
|
where
|
||||||
P: Into<PathBuf>,
|
P: Into<PathBuf>,
|
||||||
{
|
{
|
||||||
|
static CACHE: OnceLock<(Theme, SyntaxSet)> = OnceLock::new();
|
||||||
|
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
let (theme, syntaxes) = Self::load();
|
let (theme, syntaxes) = CACHE.get_or_init(Self::load);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
reader: BufReader::new(std::fs::File::open(&path)?),
|
reader: BufReader::new(std::fs::File::open(&path)?),
|
||||||
|
|
@ -117,9 +118,9 @@ impl Highlighter {
|
||||||
let h = self.inner.get_or_insert_with(|| HighlightLines::new(syntax, self.theme));
|
let h = self.inner.get_or_insert_with(|| HighlightLines::new(syntax, self.theme));
|
||||||
|
|
||||||
let s = String::from_utf8_lossy(buf);
|
let s = String::from_utf8_lossy(buf);
|
||||||
let line = Self::to_line_widget(h.highlight_line(&s, self.syntaxes)?);
|
let line = [Self::to_line_widget(h.highlight_line(&s, self.syntaxes)?)];
|
||||||
|
|
||||||
let mut it = LineIter::parsed(vec![line], YAZI.preview.tab_size);
|
let mut it = LineIter::parsed(&line, YAZI.preview.tab_size);
|
||||||
if let Some(wrap) = YAZI.preview.wrap.into() {
|
if let Some(wrap) = YAZI.preview.wrap.into() {
|
||||||
it = it.wrapped(wrap, self.size.width);
|
it = it.wrapped(wrap, self.size.width);
|
||||||
}
|
}
|
||||||
|
|
@ -141,20 +142,15 @@ impl Highlighter {
|
||||||
if self.ticket != INCR.current() { Err("Highlighting cancelled".into()) } else { Ok(()) }
|
if self.ticket != INCR.current() { Err("Highlighting cancelled".into()) } else { Ok(()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load() -> (&'static Theme, &'static SyntaxSet) {
|
fn load() -> (Theme, SyntaxSet) {
|
||||||
let f = || {
|
let theme = std::fs::File::open(&THEME.mgr.syntect_theme)
|
||||||
let theme = std::fs::File::open(&THEME.mgr.syntect_theme)
|
.map_err(LoadingError::Io)
|
||||||
.map_err(LoadingError::Io)
|
.and_then(|f| ThemeSet::load_from_reader(&mut std::io::BufReader::new(f)))
|
||||||
.and_then(|f| ThemeSet::load_from_reader(&mut std::io::BufReader::new(f)))
|
.or_else(|_| ThemeSet::load_from_reader(&mut Cursor::new(yazi_prebuilt::ansi_theme())));
|
||||||
.or_else(|_| ThemeSet::load_from_reader(&mut Cursor::new(yazi_prebuilt::ansi_theme())));
|
|
||||||
|
|
||||||
let syntaxes = dumps::from_uncompressed_data(yazi_prebuilt::syntaxes());
|
let syntaxes = dumps::from_uncompressed_data(yazi_prebuilt::syntaxes());
|
||||||
|
|
||||||
(theme.unwrap(), syntaxes.unwrap())
|
(theme.unwrap(), syntaxes.unwrap())
|
||||||
};
|
|
||||||
|
|
||||||
let (theme, syntaxes) = SYNTECT.get_or_init(f);
|
|
||||||
(theme, syntaxes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_syntax(&mut self) -> Result<()> {
|
fn load_syntax(&mut self) -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use mlua::{ExternalError, Function, IntoLuaMulti, Lua, Table, Value};
|
use mlua::{ExternalError, Function, IntoLuaMulti, Lua, Table, Value};
|
||||||
use yazi_binding::{Error, elements::{Area, Renderable, Text}};
|
use yazi_binding::{Error, elements::{Area, Renderable, Text}};
|
||||||
use yazi_config::YAZI;
|
|
||||||
use yazi_fs::FsUrl;
|
use yazi_fs::FsUrl;
|
||||||
use yazi_parser::mgr::{PreviewLock, UpdatePeekedOpt};
|
use yazi_parser::mgr::{PreviewLock, UpdatePeekedOpt};
|
||||||
use yazi_proxy::MgrProxy;
|
use yazi_proxy::MgrProxy;
|
||||||
|
|
@ -24,12 +23,7 @@ impl Utils {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
lock.data = vec![Renderable::Text(Text {
|
lock.data = vec![Renderable::Text(Text { area, inner, ..Default::default() })];
|
||||||
area,
|
|
||||||
inner,
|
|
||||||
wrap: YAZI.preview.wrap.into(),
|
|
||||||
scroll: Default::default(),
|
|
||||||
})];
|
|
||||||
|
|
||||||
MgrProxy::update_peeked(UpdatePeekedOpt { lock });
|
MgrProxy::update_peeked(UpdatePeekedOpt { lock });
|
||||||
().into_lua_multi(&lua)
|
().into_lua_multi(&lua)
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
// Copied from https://github.com/ratatui/ratatui/blob/main/ratatui-core/src/text/grapheme.rs
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use ratatui::style::{Style, Styled};
|
|
||||||
|
|
||||||
const NBSP: &str = "\u{00a0}";
|
|
||||||
const ZWSP: &str = "\u{200b}";
|
|
||||||
|
|
||||||
/// A grapheme associated to a style.
|
|
||||||
/// Note that, although `StyledGrapheme` is the smallest divisible unit of text,
|
|
||||||
/// it actually is not a member of the text type hierarchy (`Text` -> `Line` ->
|
|
||||||
/// `Span`). It is a separate type used mostly for rendering purposes. A `Span`
|
|
||||||
/// consists of components that can be split into `StyledGrapheme`s, but it does
|
|
||||||
/// not contain a collection of `StyledGrapheme`s.
|
|
||||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub struct StyledGrapheme<'a> {
|
|
||||||
pub symbol: Cow<'a, str>,
|
|
||||||
pub style: Style,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> StyledGrapheme<'a> {
|
|
||||||
/// Creates a new `StyledGrapheme` with the given symbol and style.
|
|
||||||
///
|
|
||||||
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`],
|
|
||||||
/// [`Color`], or your own type that implements [`Into<Style>`]).
|
|
||||||
///
|
|
||||||
/// [`Color`]: crate::style::Color
|
|
||||||
pub fn new<S: Into<Cow<'a, str>>>(symbol: S, style: Style) -> Self {
|
|
||||||
Self { symbol: symbol.into(), style }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_whitespace(&self) -> bool {
|
|
||||||
let symbol = &*self.symbol;
|
|
||||||
symbol == ZWSP || symbol.chars().all(char::is_whitespace) && symbol != NBSP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Styled for StyledGrapheme<'_> {
|
|
||||||
type Item = Self;
|
|
||||||
|
|
||||||
fn style(&self) -> Style { self.style }
|
|
||||||
|
|
||||||
fn set_style<S: Into<Style>>(mut self, style: S) -> Self::Item {
|
|
||||||
self.style = style.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +1,79 @@
|
||||||
use std::{boxed::Box, iter, mem, str::{self, Lines}, vec::IntoIter};
|
use std::{boxed::Box, iter, slice::Iter, str::{self, Lines}};
|
||||||
|
|
||||||
use ratatui::{layout::Alignment, text::Line, widgets::Wrap};
|
use ratatui::{layout::Alignment, style::Style, text::Line, widgets::Wrap};
|
||||||
|
|
||||||
use super::wrapper::{LineComposer, WordWrapper};
|
use super::wrapper::{LineComposer, WordWrapper};
|
||||||
use crate::ratatui::SpanIter;
|
use crate::ratatui::SpanIter;
|
||||||
|
|
||||||
type WrappedLines<'text> = Box<dyn Iterator<Item = (SpanIter<'text, 'text>, Alignment)> + 'text>;
|
type WrappedLines<'lend, 'text> =
|
||||||
|
Box<dyn Iterator<Item = (SpanIter<'lend, 'text>, Alignment)> + 'lend>;
|
||||||
|
|
||||||
pub struct LineIter<'text> {
|
pub struct LineIter<'lend, 'text> {
|
||||||
inner: LineIterInner<'text>,
|
inner: LineIterInner<'lend, 'text>,
|
||||||
tab_size: u8,
|
tab_size: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LineIterInner<'text> {
|
impl<'lend, 'text> LineIter<'lend, 'text> {
|
||||||
Source { empty: bool, lines: Lines<'text> },
|
|
||||||
Parsed(IntoIter<Line<'text>>),
|
|
||||||
Wrapped(WordWrapper<'text, WrappedLines<'text>, SpanIter<'text, 'text>>),
|
|
||||||
Unlimited(WrappedLines<'text>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'text> LineIter<'text> {
|
|
||||||
pub fn source(source: &'text str, tab_size: u8) -> Self {
|
pub fn source(source: &'text str, tab_size: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: LineIterInner::Source { empty: source.is_empty(), lines: source.lines() },
|
inner: LineIterInner::Source(if source.is_empty() { "\n" } else { source }.lines()),
|
||||||
tab_size,
|
tab_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parsed(mut text: Vec<Line<'text>>, tab_size: u8) -> Self {
|
pub fn parsed(text: &'lend [Line<'text>], tab_size: u8) -> Self {
|
||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
text.push(Line::from(""));
|
Self::source("", tab_size)
|
||||||
|
} else {
|
||||||
|
Self { inner: LineIterInner::Parsed(text.iter()), tab_size }
|
||||||
}
|
}
|
||||||
Self { inner: LineIterInner::Parsed(text.into_iter()), tab_size }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrapped(mut self, wrap: Wrap, width: u16) -> Self {
|
pub fn wrapped(mut self, wrap: Wrap, width: u16) -> Self {
|
||||||
|
if width == 0 || matches!(self.inner, LineIterInner::Wrapped(_)) {
|
||||||
|
return self; // No wrapping needed, return the original iterator
|
||||||
|
}
|
||||||
|
|
||||||
let lines = Box::new(iter::from_fn(move || self.next_owned()));
|
let lines = Box::new(iter::from_fn(move || self.next_owned()));
|
||||||
Self {
|
Self {
|
||||||
inner: if width == 0 {
|
inner: LineIterInner::Wrapped(WordWrapper::new(lines, width, wrap.trim)),
|
||||||
LineIterInner::Unlimited(lines)
|
|
||||||
} else {
|
|
||||||
LineIterInner::Wrapped(WordWrapper::new(lines, width, wrap.trim))
|
|
||||||
},
|
|
||||||
tab_size: 0,
|
tab_size: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next<'lend>(&'lend mut self) -> Option<(SpanIter<'lend, 'text>, Alignment)> {
|
pub fn next<'a>(&'a mut self) -> Option<(SpanIter<'a, 'text>, Alignment)> {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
LineIterInner::Source { .. } | LineIterInner::Parsed(_) => self.next_owned(),
|
LineIterInner::Source { .. } | LineIterInner::Parsed { .. } => self.next_owned(),
|
||||||
LineIterInner::Wrapped(ref mut wrapper) => {
|
LineIterInner::Wrapped(ref mut wrapper) => {
|
||||||
let wrapped = wrapper.next_line()?;
|
let wrapped = wrapper.next_line()?;
|
||||||
Some((SpanIter::Wrapped(wrapped.graphemes.iter()), wrapped.alignment))
|
Some((SpanIter::Wrapped(wrapped.graphemes.iter()), wrapped.alignment))
|
||||||
}
|
}
|
||||||
LineIterInner::Unlimited(ref mut lines) => lines.next(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_owned(&mut self) -> Option<(SpanIter<'text, 'text>, Alignment)> {
|
fn next_owned(&mut self) -> Option<(SpanIter<'lend, 'text>, Alignment)> {
|
||||||
match &mut self.inner {
|
match &mut self.inner {
|
||||||
LineIterInner::Source { empty, lines } => {
|
LineIterInner::Source(it) => {
|
||||||
let line = if mem::replace(empty, false) {
|
Some((SpanIter::from_span(it.next()?, Style::new(), self.tab_size), Alignment::Left))
|
||||||
Some(Line::from(""))
|
|
||||||
} else {
|
|
||||||
lines.next().map(Line::from)
|
|
||||||
}?;
|
|
||||||
let alignment = line.alignment.unwrap_or(Alignment::Left);
|
|
||||||
|
|
||||||
Some((SpanIter::new(line, self.tab_size), alignment))
|
|
||||||
}
|
}
|
||||||
LineIterInner::Parsed(lines) => {
|
LineIterInner::Parsed(it) => {
|
||||||
let line = lines.next()?;
|
let line = it.next()?;
|
||||||
let alignment = line.alignment.unwrap_or(Alignment::Left);
|
let alignment = line.alignment.unwrap_or(Alignment::Left);
|
||||||
|
Some((SpanIter::from_line(&line.spans, line.style, self.tab_size), alignment))
|
||||||
Some((SpanIter::new(line, self.tab_size), alignment))
|
|
||||||
}
|
}
|
||||||
LineIterInner::Wrapped(_) | LineIterInner::Unlimited(_) => {
|
LineIterInner::Wrapped(_) => {
|
||||||
unreachable!(); // This branch is handled by next() and should never call next_owned()
|
unreachable!(); // This branch is handled by next() and should never call next_owned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -- LineIterInner
|
||||||
|
enum LineIterInner<'lend, 'text>
|
||||||
|
where
|
||||||
|
'lend: 'text,
|
||||||
|
{
|
||||||
|
Source(Lines<'text>),
|
||||||
|
Parsed(Iter<'lend, Line<'text>>),
|
||||||
|
Wrapped(WordWrapper<'text, WrappedLines<'lend, 'text>, SpanIter<'lend, 'text>>),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
yazi_macro::mod_flat!(grapheme line span wrapper);
|
yazi_macro::mod_flat!(line span wrapper);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
use std::{borrow::Cow, slice::Iter, vec::IntoIter};
|
use std::slice::Iter;
|
||||||
|
|
||||||
use ratatui::{style::Style, text::{Line, Span}};
|
use ratatui::{style::Style, text::{Line, Span, StyledGrapheme}};
|
||||||
use unicode_segmentation::{Graphemes, UnicodeSegmentation};
|
use unicode_segmentation::{Graphemes, UnicodeSegmentation};
|
||||||
|
|
||||||
use crate::ratatui::StyledGrapheme;
|
|
||||||
|
|
||||||
#[allow(private_interfaces)]
|
#[allow(private_interfaces)]
|
||||||
pub enum SpanIter<'lend, 'text> {
|
pub enum SpanIter<'lend, 'text> {
|
||||||
|
Span {
|
||||||
|
style: Style,
|
||||||
|
graphemes: Graphemes<'text>,
|
||||||
|
tab_size: u8,
|
||||||
|
pending_tabs: u8,
|
||||||
|
},
|
||||||
Line {
|
Line {
|
||||||
spans: IntoIter<Span<'text>>,
|
spans: Iter<'lend, Span<'text>>,
|
||||||
line_style: Style,
|
line_style: Style,
|
||||||
current: Option<CurrentSpan<'text>>,
|
current: Option<CurrentSpan<'text>>,
|
||||||
tab_size: u8,
|
tab_size: u8,
|
||||||
|
|
@ -19,10 +23,14 @@ pub enum SpanIter<'lend, 'text> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lend, 'text> SpanIter<'lend, 'text> {
|
impl<'lend, 'text> SpanIter<'lend, 'text> {
|
||||||
pub(super) fn new(line: Line<'text>, tab_size: u8) -> Self {
|
pub(super) fn from_span(source: &'text str, style: Style, tab_size: u8) -> Self {
|
||||||
|
Self::Span { style, graphemes: source.graphemes(true), tab_size, pending_tabs: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn from_line(spans: &'lend [Span<'text>], line_style: Style, tab_size: u8) -> Self {
|
||||||
Self::Line {
|
Self::Line {
|
||||||
spans: line.spans.into_iter(),
|
spans: spans.iter(),
|
||||||
line_style: line.style,
|
line_style,
|
||||||
current: None,
|
current: None,
|
||||||
tab_size,
|
tab_size,
|
||||||
pending_tabs: 0,
|
pending_tabs: 0,
|
||||||
|
|
@ -31,16 +39,34 @@ impl<'lend, 'text> SpanIter<'lend, 'text> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_static_line(self) -> Line<'static> {
|
pub fn into_static_line(self) -> Line<'static> {
|
||||||
Line::from_iter(self.map(|g| Span { style: g.style, content: g.symbol.into_owned().into() }))
|
Line::from_iter(self.map(|g| Span { style: g.style, content: g.symbol.to_owned().into() }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lend, 'text> Iterator for SpanIter<'lend, 'text> {
|
impl<'lend, 'text> Iterator for SpanIter<'lend, 'text>
|
||||||
|
where
|
||||||
|
'lend: 'text,
|
||||||
|
{
|
||||||
type Item = StyledGrapheme<'text>;
|
type Item = StyledGrapheme<'text>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self {
|
match self {
|
||||||
Self::Wrapped(inner) => inner.next().cloned(),
|
Self::Span { style, graphemes, tab_size, pending_tabs } => {
|
||||||
|
if *pending_tabs > 0 {
|
||||||
|
*pending_tabs -= 1;
|
||||||
|
return Some(StyledGrapheme::new(" ", *style));
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let symbol = graphemes.next()?;
|
||||||
|
if symbol != "\t" {
|
||||||
|
return Some(StyledGrapheme::new(symbol, *style));
|
||||||
|
} else if let Some(n) = tab_size.checked_sub(1) {
|
||||||
|
*pending_tabs = n;
|
||||||
|
return Some(StyledGrapheme::new(" ", *style));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Self::Line { spans, line_style, current, tab_size, pending_tabs, pending_style } => loop {
|
Self::Line { spans, line_style, current, tab_size, pending_tabs, pending_style } => loop {
|
||||||
if *pending_tabs > 0 {
|
if *pending_tabs > 0 {
|
||||||
*pending_tabs -= 1;
|
*pending_tabs -= 1;
|
||||||
|
|
@ -48,54 +74,31 @@ impl<'lend, 'text> Iterator for SpanIter<'lend, 'text> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(span) = current
|
if let Some(span) = current
|
||||||
&& let Some(symbol) = span.next_symbol()
|
&& let Some(symbol) = span.graphemes.next()
|
||||||
{
|
{
|
||||||
if symbol == "\t" {
|
if symbol != "\t" {
|
||||||
if *tab_size == 0 {
|
return Some(StyledGrapheme::new(symbol, span.style));
|
||||||
continue;
|
} else if let Some(n) = tab_size.checked_sub(1) {
|
||||||
}
|
*pending_tabs = n;
|
||||||
|
*pending_style = span.style;
|
||||||
*pending_tabs = tab_size.saturating_sub(1);
|
return Some(StyledGrapheme::new(" ", span.style));
|
||||||
*pending_style = span.style();
|
|
||||||
return Some(StyledGrapheme::new(" ", span.style()));
|
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
return Some(StyledGrapheme::new(symbol, span.style()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = spans.next()?;
|
let span = spans.next()?;
|
||||||
*current = Some(match span.content {
|
*current = Some(CurrentSpan {
|
||||||
Cow::Borrowed(content) => CurrentSpan::Borrowed {
|
style: line_style.patch(span.style),
|
||||||
style: line_style.patch(span.style),
|
graphemes: span.content.graphemes(true),
|
||||||
graphemes: content.graphemes(true),
|
|
||||||
},
|
|
||||||
Cow::Owned(content) => CurrentSpan::Owned {
|
|
||||||
style: line_style.patch(span.style),
|
|
||||||
graphemes: content.graphemes(true).map(str::to_owned).collect::<Vec<_>>().into_iter(),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
Self::Wrapped(inner) => inner.next().cloned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- CurrentSpan
|
// --- CurrentSpan
|
||||||
enum CurrentSpan<'text> {
|
struct CurrentSpan<'text> {
|
||||||
Borrowed { style: Style, graphemes: Graphemes<'text> },
|
style: Style,
|
||||||
Owned { style: Style, graphemes: IntoIter<String> },
|
graphemes: Graphemes<'text>,
|
||||||
}
|
|
||||||
|
|
||||||
impl<'text> CurrentSpan<'text> {
|
|
||||||
fn style(&self) -> Style {
|
|
||||||
match self {
|
|
||||||
Self::Borrowed { style, .. } | Self::Owned { style, .. } => *style,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_symbol(&mut self) -> Option<Cow<'text, str>> {
|
|
||||||
match self {
|
|
||||||
Self::Borrowed { graphemes, .. } => graphemes.next().map(Cow::Borrowed),
|
|
||||||
Self::Owned { graphemes, .. } => graphemes.next().map(Cow::Owned),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
// Copied from https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/src/reflow.rs
|
// Copied from https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/src/reflow.rs
|
||||||
use std::{collections::VecDeque, mem};
|
use std::{collections::VecDeque, mem};
|
||||||
|
|
||||||
use ratatui::layout::Alignment;
|
use ratatui::{layout::Alignment, text::StyledGrapheme};
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
use crate::ratatui::StyledGrapheme;
|
|
||||||
|
|
||||||
pub trait LineComposer<'a> {
|
pub trait LineComposer<'a> {
|
||||||
fn next_line<'lend>(&'lend mut self) -> Option<WrappedLine<'lend, 'a>>;
|
fn next_line<'lend>(&'lend mut self) -> Option<WrappedLine<'lend, 'a>>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue