mod list;
mod preview;
use ratatui::Frame;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Clear, Padding, Paragraph};
use crate::app::{App, Prompt};
use crate::finder::FuzzyKind;
pub(super) fn draw_fuzzy(f: &mut Frame, app: &App, area: Rect) {
let Prompt::Fuzzy(finder) = &app.prompt.state else {
return;
};
let popup = centered_rect(90, 80, area);
f.render_widget(Clear, popup);
let title = match finder.kind {
FuzzyKind::Files { ignore } if !ignore.hidden => " fuzzy: files (+hidden) ",
FuzzyKind::Files { .. } => " fuzzy: files ",
FuzzyKind::Lines => " fuzzy: lines ",
FuzzyKind::Locations => " references ",
FuzzyKind::Jumps => " jumps ",
FuzzyKind::WorkspaceSearch => " fuzzy: workspace ",
FuzzyKind::Buffers => " fuzzy: buffers ",
FuzzyKind::Diagnostics { workspace: false } => " diagnostics ",
FuzzyKind::Diagnostics { workspace: true } => " diagnostics: workspace ",
FuzzyKind::Bookmarks => " bookmarks ",
FuzzyKind::GitChangedFiles => " git: changed files ",
};
let total = finder.matches.len();
let footer = format!(" {}/{} ", finder.selected + 1, total.max(1));
let panel = Style::default().bg(super::panel_bg()).fg(super::panel_fg());
let mut block = Block::default()
.borders(Borders::ALL)
.border_style(Style::default().fg(super::panel_border_fg()))
.title(title)
.title_bottom(Line::from(footer).right_aligned())
.style(panel)
.padding(Padding::horizontal(1));
let bookmark_filtering = matches!(finder.kind, FuzzyKind::Bookmarks)
&& app.prompt.bookmark_mode() == crate::prompt::BookmarkPickMode::Filter;
if matches!(finder.kind, FuzzyKind::Bookmarks) {
let hint = if bookmark_filtering {
" type to filter · ↵ jump · esc back "
} else {
" j/k move · d delete · / filter · ↵ jump · esc close "
};
block = block.title_bottom(Line::from(hint));
}
let inner = block.inner(popup);
f.render_widget(block, popup);
let panes = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(40),
Constraint::Length(1),
Constraint::Min(1),
])
.split(inner);
let show_query = !matches!(finder.kind, FuzzyKind::Bookmarks) || bookmark_filtering;
list::draw_fuzzy_list(f, finder, panes[0], show_query);
let sep_v: Vec<Line> = (0..panes[1].height)
.map(|_| Line::from(Span::styled("│", Style::default().fg(Color::DarkGray))))
.collect();
f.render_widget(Paragraph::new(sep_v), panes[1]);
f.render_widget(Clear, panes[2]);
f.render_widget(
Block::default().style(Style::default().bg(super::panel_bg())),
panes[2],
);
preview::draw_fuzzy_preview(f, app, finder, panes[2]);
}
pub(super) fn draw_explorer_preview(f: &mut Frame, app: &App, area: Rect, path: &std::path::Path) {
preview::preview_from_file(f, app, area, path, 0);
}
pub(super) fn split_with_header(
f: &mut Frame,
area: Rect,
label: &str,
label_style: Style,
) -> Rect {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1),
Constraint::Length(1),
Constraint::Min(1),
])
.split(area);
let label_w = label.chars().count();
let avail = chunks[0].width as usize;
let display: String = if label_w > avail && avail >= 2 {
let tail: String = label.chars().skip(label_w - (avail - 1)).collect();
format!("…{}", tail)
} else {
label.to_string()
};
f.render_widget(
Paragraph::new(Line::from(Span::styled(display, label_style))),
chunks[0],
);
let sep = "─".repeat(chunks[1].width as usize);
f.render_widget(
Paragraph::new(Span::styled(sep, Style::default().fg(Color::DarkGray))),
chunks[1],
);
chunks[2]
}
fn centered_rect(percent_x: u16, percent_y: u16, area: Rect) -> Rect {
let v = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
])
.split(area);
Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
])
.split(v[1])[1]
}