use clankerdiff_markdown::{MarkdownDocument, MarkdownReviewEvent, MarkdownReviewSubmission};
use clankerdiff_ratatui::{
MarkdownReviewState, MarkdownReviewWidget, ThemeChoice, handle_markdown_crossterm_event,
};
use crossterm::{
event, execute,
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
};
use ratatui::{Terminal, backend::CrosstermBackend};
use std::{env, error::Error, fs, io::stdout, path::Path, sync::Arc};
struct TerminalGuard;
impl TerminalGuard {
fn enter() -> std::io::Result<Self> {
enable_raw_mode()?;
execute!(stdout(), EnterAlternateScreen)?;
Ok(Self)
}
}
impl Drop for TerminalGuard {
fn drop(&mut self) {
let _ = disable_raw_mode();
let _ = execute!(stdout(), LeaveAlternateScreen);
}
}
fn main() -> Result<(), Box<dyn Error>> {
let path = env::args()
.nth(1)
.unwrap_or_else(|| "markdown-plan.md".into());
let source = fs::read_to_string(&path)?;
let title = Path::new(&path)
.file_name()
.map(|name| name.to_string_lossy().into_owned());
let document = MarkdownDocument::parse_with_metadata(Some(path), title, source);
let outcome = run(Arc::new(document))?;
if let Some(submission) = outcome {
println!("{}", submission.formatted);
}
Ok(())
}
fn run(
document: Arc<MarkdownDocument>,
) -> Result<Option<MarkdownReviewSubmission>, Box<dyn Error>> {
let _guard = TerminalGuard::enter()?;
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
let mut state = MarkdownReviewState::new(document);
state.set_theme_choices(ThemeChoice::catalog());
loop {
if state.is_dirty() {
terminal.draw(|frame| {
frame.render_stateful_widget(MarkdownReviewWidget::new(), frame.area(), &mut state);
if let Some(position) = state.cursor_position() {
frame.set_cursor_position(position);
}
})?;
}
match handle_markdown_crossterm_event(&mut state, event::read()?)?.into_event() {
Some(MarkdownReviewEvent::Submit(submission)) => return Ok(Some(submission)),
Some(MarkdownReviewEvent::Cancel) => return Ok(None),
Some(MarkdownReviewEvent::CopyFormatted(_)) | None => {}
}
}
}