diff --git a/src/main.rs b/src/main.rs index 85b3bc1..19551a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -119,6 +119,7 @@ fn run_single_process() -> Result<()> { let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen)?; execute!(stdout, crossterm::event::EnableBracketedPaste)?; + execute!(stdout, crossterm::event::EnableMouseCapture)?; let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; terminal.clear()?; @@ -127,6 +128,7 @@ fn run_single_process() -> Result<()> { let mut restore_stdout = io::stdout(); let _ = execute!(restore_stdout, crossterm::event::DisableBracketedPaste); + let _ = execute!(restore_stdout, crossterm::event::DisableMouseCapture); let _ = execute!(restore_stdout, LeaveAlternateScreen); let _ = disable_raw_mode(); @@ -500,6 +502,7 @@ fn run_attach(session_id: &str) -> Result<()> { let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen)?; execute!(stdout, crossterm::event::EnableBracketedPaste)?; + execute!(stdout, crossterm::event::EnableMouseCapture)?; let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; terminal.clear()?; @@ -591,6 +594,7 @@ fn run_attach(session_id: &str) -> Result<()> { } let _ = execute!(io::stdout(), crossterm::event::DisableBracketedPaste); + let _ = execute!(io::stdout(), crossterm::event::DisableMouseCapture); let _ = execute!(io::stdout(), LeaveAlternateScreen); let _ = disable_raw_mode(); @@ -617,6 +621,7 @@ fn run_loop( let _ = disable_raw_mode(); let _ = execute!(io::stdout(), crossterm::event::DisableBracketedPaste); + let _ = execute!(io::stdout(), crossterm::event::DisableMouseCapture); let _ = execute!(io::stdout(), LeaveAlternateScreen); } result diff --git a/src/view/markdown.rs b/src/view/markdown.rs index f705d6a..441975d 100644 --- a/src/view/markdown.rs +++ b/src/view/markdown.rs @@ -30,10 +30,18 @@ use super::theme::Theme; #[allow(clippy::too_many_lines)] pub fn render_markdown(text: &str, width: u16) -> Vec> { let mut spans = Vec::new(); - let parser = pulldown_cmark::Parser::new(text); + let mut options = pulldown_cmark::Options::empty(); + options.insert(pulldown_cmark::Options::ENABLE_TABLES); + let parser = pulldown_cmark::Parser::new_ext(text, options); let mut in_code_block = false; let mut in_heading = false; let mut heading_level = 0; + + let mut in_table = false; + let mut in_table_cell = false; + let mut table_rows: Vec>>> = Vec::new(); + let mut current_row: Vec>> = Vec::new(); + let mut current_cell: Vec> = Vec::new(); for event in parser { match event { @@ -90,6 +98,17 @@ pub fn render_markdown(text: &str, width: u16) -> Vec> { Style::default().fg(Theme::BLOCKQUOTE_BAR), )); } + pulldown_cmark::Tag::Table(_) => { + in_table = true; + table_rows.clear(); + } + pulldown_cmark::Tag::TableHead | pulldown_cmark::Tag::TableRow => { + current_row.clear(); + } + pulldown_cmark::Tag::TableCell => { + in_table_cell = true; + current_cell.clear(); + } _ => {} } } @@ -114,6 +133,52 @@ pub fn render_markdown(text: &str, width: u16) -> Vec> { pulldown_cmark::TagEnd::Item | pulldown_cmark::TagEnd::BlockQuote(_) => { spans.push(Span::raw("\n")); } + pulldown_cmark::TagEnd::TableCell => { + in_table_cell = false; + current_row.push(std::mem::take(&mut current_cell)); + } + pulldown_cmark::TagEnd::TableHead | pulldown_cmark::TagEnd::TableRow => { + table_rows.push(std::mem::take(&mut current_row)); + } + pulldown_cmark::TagEnd::Table => { + in_table = false; + + let mut col_widths = Vec::new(); + for row in &table_rows { + for (i, cell) in row.iter().enumerate() { + let width: usize = cell.iter().map(|s| s.content.chars().count()).sum(); + if i >= col_widths.len() { + col_widths.push(width); + } else if width > col_widths[i] { + col_widths[i] = width; + } + } + } + + spans.push(Span::raw("\n")); + for (r, row) in table_rows.iter().enumerate() { + spans.push(Span::styled(" | ", Style::default().fg(Theme::BORDER))); + for (i, cell) in row.iter().enumerate() { + let width: usize = cell.iter().map(|s| s.content.chars().count()).sum(); + let pad = col_widths.get(i).copied().unwrap_or(0).saturating_sub(width); + for span in cell { + spans.push(span.clone()); + } + spans.push(Span::raw(" ".repeat(pad))); + spans.push(Span::styled(" | ", Style::default().fg(Theme::BORDER))); + } + spans.push(Span::raw("\n")); + + if r == 0 { + spans.push(Span::styled(" |", Style::default().fg(Theme::BORDER))); + for width in &col_widths { + spans.push(Span::styled(format!("{}-|", "-".repeat(*width + 2)), Style::default().fg(Theme::BORDER))); + } + spans.push(Span::raw("\n")); + } + } + spans.push(Span::raw("\n")); + } _ => {} } } @@ -136,19 +201,25 @@ pub fn render_markdown(text: &str, width: u16) -> Vec> { s, Style::default().fg(color).add_modifier(Modifier::BOLD), )); + } else if in_table_cell { + current_cell.push(Span::raw(s)); } else { spans.push(Span::raw(s)); } } pulldown_cmark::Event::Code(text) => { - // Inline code with background - spans.push(Span::styled( + let span = Span::styled( format!(" {text} "), Style::default() .fg(Theme::ACCENT_TEAL) .bg(Theme::CODE_BAR) .add_modifier(Modifier::BOLD), - )); + ); + if in_table_cell { + current_cell.push(span); + } else { + spans.push(span); + } } pulldown_cmark::Event::SoftBreak => { spans.push(Span::raw(" ")); diff --git a/test_mouse.rs b/test_mouse.rs new file mode 100644 index 0000000..0275cfb --- /dev/null +++ b/test_mouse.rs @@ -0,0 +1,2 @@ +use crossterm::event::{EnableMouseCapture, EnableMouseScrollCapture}; +fn main() {}