diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.rs | 58 | ||||
| -rw-r--r-- | src/input.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 1 | ||||
| -rw-r--r-- | src/ui.rs | 124 |
4 files changed, 161 insertions, 24 deletions
@@ -18,13 +18,13 @@ use std::time::{Duration, Instant}; use crossterm::event; -use germ::request::Status; +use germ::{ast::Node, request::Status}; use url::Url; use crate::{input::Mode as InputMode, stateful_list::StatefulList}; pub struct App { - pub items: StatefulList<(Vec<String>, Option<String>)>, + pub items: StatefulList<(Vec<Node>, Option<String>, bool)>, pub input: String, pub input_mode: InputMode, pub command_stroke_history: Vec<event::KeyCode>, @@ -72,7 +72,7 @@ impl App { pub fn make_request(&mut self) { self.items = StatefulList::with_items({ - let mut items = vec![]; + let mut items: Vec<(Vec<Node>, Option<String>, bool)> = vec![]; match germ::request::request(&self.url) { Ok(mut response) => { @@ -111,30 +111,50 @@ impl App { items = self.items.items.clone(); } - items.push((vec![response.meta().to_string()], None)); - items.push((vec!["".to_string()], None)); + // items.push(( + // vec![Node::Text(response.meta().to_string())], + // None, + // false, + // )); + // items.push((vec![Node::Text("".to_string())], None, false)); + + let mut pre = false; if let Some(content) = response.content().clone() { - for line in content.lines().clone() { + let real_lines = content.lines(); + + for line in real_lines { let line = line.replace('\t', " "); - let mut parts = line.split_whitespace(); - let lines = if line.is_empty() { - vec![line.to_string()] + let pre_like = if line.starts_with("```") { + pre = !pre; + + true } else { - line - .as_bytes() - .chunks(self.wrap_at as usize) - .map(|buf| { - #[allow(unsafe_code)] - unsafe { std::str::from_utf8_unchecked(buf) }.to_string() - }) - .collect::<Vec<_>>() + false }; + let ast = germ::ast::Ast::from_string(&line); + let ast_node = ast.inner().first().map_or_else( + || { + if pre_like || pre { + if line == "```" { + Node::Text("sydney_abc_123".to_string()) + } else { + Node::Text(line.get(3..).unwrap_or("").to_string()) + } + } else { + Node::Whitespace + } + }, + Clone::clone, + ); + + let mut parts = line.split_whitespace(); + if let (Some("=>"), Some(to)) = (parts.next(), parts.next()) { - items.push((lines, Some(to.to_string()))); + items.push((vec![ast_node], Some(to.to_string()), false)); } else { - items.push((lines, None)); + items.push((vec![ast_node], None, pre)); } } } else if response.status() != &Status::Input diff --git a/src/input.rs b/src/input.rs index 3a44018..1fac12a 100644 --- a/src/input.rs +++ b/src/input.rs @@ -20,7 +20,7 @@ use url::Url; use crate::command::Command; -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub enum Mode { Normal, Editing, diff --git a/src/main.rs b/src/main.rs index ab628f8..060ca08 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,7 @@ clippy::nursery, clippy::pedantic )] +#![feature(iter_advance_by)] #![recursion_limit = "128"] mod app; @@ -15,13 +15,16 @@ // Copyright (C) 2022-2022 Fuwn <[email protected]> // SPDX-License-Identifier: GPL-3.0-only +use germ::ast::Node; use tui::{ layout::{Constraint, Direction, Layout, Rect}, - style::{Color, Style}, + style::{Color, Modifier, Style}, + text::{Span, Spans}, widgets, widgets::{ListItem, Paragraph}, }; +#[allow(clippy::too_many_lines)] pub fn ui<B: tui::backend::Backend>( f: &mut tui::Frame<'_, B>, app: &mut crate::App, @@ -42,11 +45,119 @@ pub fn ui<B: tui::backend::Backend>( .items .items .iter() - .map(|(text_lines, _link)| { + .map(|(text_lines, _link, pre)| { let mut spans = vec![]; for line in text_lines { - spans.push(tui::text::Spans::from(line.as_str())); + let mut line = line.clone(); + + if *pre { + if let Node::Text(text) = line { + line = Node::PreformattedText { + alt_text: None, + text: text.to_string(), + } + } + } + + match line { + germ::ast::Node::Text(text) => + if text != "sydney_abc_123" { + spans.push(tui::text::Spans::from(format!(" {}", text))); + }, + germ::ast::Node::Blockquote(text) => { + spans.push(Spans::from(vec![ + Span::styled(" > ", Style::default().fg(Color::LightBlue)), + Span::styled( + text, + Style::default().add_modifier(Modifier::ITALIC), + ), + ])); + } + germ::ast::Node::Link { + to, + text, + } => { + let mut span_list = + vec![Span::styled(" => ", Style::default().fg(Color::LightBlue))]; + + span_list.push(Span::styled( + text.unwrap_or_else(|| to.clone()), + Style::default().add_modifier(Modifier::UNDERLINED), + )); + span_list.push(Span::from(" ")); + span_list + .push(Span::styled(to, Style::default().fg(Color::LightBlue))); + + spans.push(Spans::from(span_list)); + } + germ::ast::Node::Heading { + text, + level, + } => { + spans.push(Spans::from(vec![ + Span::styled( + match level { + 1 => " # ", + 2 => " ## ", + 3 => "### ", + _ => unreachable!(), + }, + Style::default().fg(Color::LightBlue), + ), + Span::styled(text, { + let mut style = Style::default().add_modifier(Modifier::BOLD); + + match level { + 1 => { + style = style.add_modifier(Modifier::UNDERLINED); + } + 3 => { + style = style.add_modifier(Modifier::ITALIC); + } + _ => {} + } + + style + }), + ])); + } + germ::ast::Node::List(list_items) => { + let mut span_list = vec![]; + + for list_item in list_items { + span_list.push(Span::styled( + " * ", + Style::default().fg(Color::LightBlue), + )); + span_list.push(Span::from(format!("{}\n", list_item))); + } + + spans.push(Spans::from(span_list)); + } + germ::ast::Node::PreformattedText { + text, + alt_text, + } => { + let mut span_list = vec![]; + + span_list.push(Span::styled( + "``` ", + Style::default().fg(Color::LightBlue), + )); + span_list + .push(Span::from(alt_text.unwrap_or_else(|| "".to_string()))); + + if text != "sydney_abc_123" { + span_list.push(Span::from(text)); + } + + spans.push(Spans::from(span_list)); + } + germ::ast::Node::Whitespace => { + spans.push(Spans::from("".to_string())); + } + }; } ListItem::new(spans) @@ -54,7 +165,12 @@ pub fn ui<B: tui::backend::Backend>( .collect(); let items = widgets::List::new(items) - .highlight_style(Style::default().bg(Color::White).fg(Color::Black)) + .highlight_style( + Style::default() + .bg(Color::White) + .fg(Color::Black) + .remove_modifier(Modifier::BOLD), + ) .style(Style::default().bg(Color::Black).fg(Color::White)); f.render_stateful_widget(items, chunks[0], &mut app.items.state); |