use std::path::Path;
use karet_fileview::{FileDoc, FileView, FileViewState, Limits};
use ratatui::Frame;
use ratatui::layout::{Alignment, Rect};
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Paragraph};
use super::theme;
use crate::app::{Focus, Loaded};
const MAX_TEXT_BYTES: u64 = 256 * 1024;
const MAX_TEXT_LINES: usize = 500;
#[must_use]
pub fn preview_limits() -> Limits {
Limits::new(MAX_TEXT_BYTES, MAX_TEXT_LINES)
}
#[derive(Default)]
pub struct Preview {
pub doc: Option<FileDoc>,
pub note: Option<String>,
pub yank: Option<String>,
pub state: FileViewState,
}
impl Preview {
pub fn note(message: String) -> Self {
Self {
note: Some(message),
..Default::default()
}
}
#[cfg(test)]
pub fn from_doc(doc: FileDoc) -> Self {
Self {
doc: Some(doc),
note: None,
yank: None,
state: FileViewState::new(),
}
}
}
pub fn load(path: &Path) -> Preview {
let len = match std::fs::metadata(path) {
Ok(meta) => meta.len(),
Err(err) => return Preview::note(format!("cannot read file: {err}")),
};
let limits = preview_limits();
let bytes = match read_bounded(path, limits.max_bytes) {
Ok(bytes) => bytes,
Err(err) => return Preview::note(format!("cannot read file: {err}")),
};
let doc = FileDoc::prepare(path, &bytes, len, &limits);
let yank = doc
.language()
.is_some()
.then(|| String::from_utf8_lossy(&bytes).into_owned());
Preview {
doc: Some(doc),
note: None,
yank,
state: FileViewState::new(),
}
}
pub(super) fn read_bounded(path: &Path, max: u64) -> std::io::Result<Vec<u8>> {
use std::io::Read;
let file = std::fs::File::open(path)?;
let mut buf = Vec::new();
file.take(max).read_to_end(&mut buf)?;
Ok(buf)
}
pub fn render(frame: &mut Frame, loaded: &mut Loaded, area: Rect) {
let focused = loaded.focus == Focus::Preview;
let border = if focused { theme::ACCENT } else { theme::MUTED };
let block = Block::bordered()
.title(" preview ")
.border_style(Style::default().fg(border));
let inner = block.inner(area);
frame.render_widget(block, area);
if let Some(doc) = &loaded.preview.doc {
frame.render_stateful_widget(FileView::new(doc), inner, &mut loaded.preview.state);
} else if let Some(note) = &loaded.preview.note {
let note = Paragraph::new(Line::from(Span::styled(
note.clone(),
Style::default()
.fg(theme::MUTED)
.add_modifier(Modifier::ITALIC),
)))
.alignment(Alignment::Center);
frame.render_widget(note, inner);
}
}