use newt_core::tty::str_width;
use ratatui::layout::Rect;
use ratatui::style::{Color, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;
use ratatui::Frame;
use tui_textarea::TextArea;
pub(super) fn wrap_segments(text: &str, first_w: usize, cont_w: usize) -> Vec<(usize, String)> {
let chars: Vec<char> = text.chars().collect();
if chars.is_empty() {
return vec![(0, String::new())];
}
let mut segs: Vec<(usize, String)> = Vec::new();
let mut start = 0;
while start < chars.len() {
let avail = if segs.is_empty() { first_w } else { cont_w }.max(1);
let mut hard_end = start;
while hard_end < chars.len() {
let candidate: String = chars[start..=hard_end].iter().collect();
if str_width(&candidate) > avail && hard_end > start {
break;
}
hard_end += 1;
}
if hard_end == chars.len() {
segs.push((start, chars[start..].iter().collect()));
break;
}
let break_at = (start..hard_end)
.rev()
.find(|&i| chars[i] == ' ')
.map(|i| i + 1)
.unwrap_or(hard_end)
.max(start + 1);
segs.push((start, chars[start..break_at].iter().collect()));
start = break_at;
}
segs
}
pub(super) fn overhang_rows(
prompt: &Line<'static>,
lines: &[String],
cursor: (usize, usize),
g: u16,
width: u16,
hint: Option<&str>,
) -> (Vec<Line<'static>>, u16, u16) {
let pw = prompt.width() as u16;
let (crow, ccol) = cursor;
let cont_w = width.saturating_sub(g).max(1) as usize;
let mut rows: Vec<Line> = Vec::new();
let (mut cx, mut cy) = (pw, 0u16);
for (i, text) in lines.iter().enumerate() {
let first_indent = if i == 0 { pw } else { g };
let first_w = width.saturating_sub(first_indent).max(1) as usize;
let segs = wrap_segments(text, first_w, cont_w);
let n = segs.len();
for (s, (seg_start, seg_text)) in segs.into_iter().enumerate() {
let indent = if s == 0 { first_indent } else { g };
let line = if i == 0 && s == 0 {
let mut spans = prompt.spans.clone();
spans.push(Span::raw(seg_text.clone()));
if let Some(h) = hint {
spans.push(Span::styled(
h.to_string(),
Style::default().fg(Color::DarkGray),
));
}
Line::from(spans)
} else {
Line::from(vec![
Span::raw(" ".repeat(indent as usize)),
Span::raw(seg_text.clone()),
])
};
if i == crow {
let seg_end = seg_start + seg_text.chars().count();
let last = s + 1 == n;
if (ccol >= seg_start && ccol < seg_end) || (last && ccol >= seg_end) {
cy = rows.len() as u16;
let cursor_chars = ccol.saturating_sub(seg_start);
let cursor_prefix: String = seg_text.chars().take(cursor_chars).collect();
let cursor_cells = str_width(&cursor_prefix);
cx = indent + cursor_cells as u16;
}
}
rows.push(line);
}
}
(rows, cx, cy)
}
pub(super) fn draw_overhang(
f: &mut Frame,
area: Rect,
prompt: &Line<'static>,
textarea: &TextArea,
g: u16,
hint: Option<&str>,
show_cursor: bool,
) {
let (rows, cx, cy) = overhang_rows(
prompt,
textarea.lines(),
textarea.cursor(),
g,
area.width,
hint,
);
let last_row = area.height.saturating_sub(1);
let scroll_y = cy.saturating_sub(last_row);
f.render_widget(Paragraph::new(rows).scroll((scroll_y, 0)), area);
let cur_x = area.x + cx;
let cur_y = area.y + cy - scroll_y;
if show_cursor
&& cur_x <= area.right().saturating_sub(1)
&& cur_y <= area.bottom().saturating_sub(1)
{
f.set_cursor_position((cur_x, cur_y));
}
}