Skip to content

Commit bedc5a6

Browse files
authored
Merge pull request #45 from ranfdev/rewritten_parser
Rewritten parser, refactor pages, put hypertext rendering in Hypertext page
2 parents aabdb44 + 9919ec3 commit bedc5a6

File tree

14 files changed

+704
-522
lines changed

14 files changed

+704
-522
lines changed

data/resources/ui/download_page.blp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using Gtk 4.0;
22
using Adw 1;
33

4-
template DownloadPage: Gtk.Box {
4+
template Download: Gtk.Box {
55
margin-top: 8;
66
margin-bottom: 8;
77
margin-start: 8;

data/resources/ui/input_page.blp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using Gtk 4.0;
22
using Adw 1;
33

4-
template InputPage: Gtk.Box {
4+
template Input: Gtk.Box {
55
margin-top: 8;
66
margin-bottom: 8;
77
margin-start: 8;

data/resources/ui/tab.blp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,6 @@ template Tab: Adw.Bin {
99
Gtk.Stack stack {
1010
Gtk.ScrolledWindow scroll_win {
1111
vexpand: true;
12-
Gtk.TextView text_view {
13-
top-margin: 40;
14-
bottom-margin: 80;
15-
left-margin: 20;
16-
right-margin: 20;
17-
indent: 2;
18-
editable: false;
19-
cursor-visible: false;
20-
wrap-mode: word_char;
21-
}
2212
}
2313
}
2414
}

gemini/src/parser.rs

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,101 @@
11
use once_cell::sync::Lazy;
22
use regex::Regex;
33
static R_GEMINI_LINK: Lazy<Regex> =
4-
Lazy::new(|| Regex::new(r"^=>\s*(?P<href>\S*)\s*(?P<label>.*)").unwrap());
5-
6-
#[derive(Debug)]
7-
pub enum PageElement {
8-
Heading(String),
9-
Quote(String),
10-
Preformatted(String),
11-
Text(String),
4+
Lazy::new(|| Regex::new(r"^=>\s+(?P<href>\S+)(\s+(?P<label>.+))?").unwrap());
5+
6+
// See gemini://gemini.circumlunar.space/docs/cheatsheet.gmi
7+
8+
#[derive(Debug, Clone)]
9+
pub enum Tag {
10+
Paragraph, // Is just a text line
11+
Heading(u8),
12+
BlockQuote,
13+
CodeBlock,
14+
UnorderedList,
15+
Item,
1216
Link(String, Option<String>),
13-
ListItem(String),
14-
Empty,
17+
}
18+
19+
#[derive(Debug, Clone)]
20+
pub enum Event<'a> {
21+
Start(Tag),
22+
End,
23+
Text(&'a str),
24+
BlankLine,
1525
}
1626

1727
#[derive(Debug, Clone, Default)]
1828
pub struct Parser {
19-
inside_pre: bool,
29+
tag_stack: Vec<Tag>,
2030
}
2131

2232
impl Parser {
2333
pub fn new() -> Self {
24-
Self { inside_pre: false }
34+
Self { tag_stack: vec![] }
2535
}
26-
pub fn parse_line(&mut self, line: &str) -> PageElement {
36+
37+
/// Returns an `Event` when an event it's ready, else, `None`
38+
// TODO: Make this work on text input of any length, don't impose the "line" chunk requirement
39+
// some work has already been done, the pushed result is already structured to do so.
40+
pub fn parse_line<'a>(&mut self, line: &'a str, res: &mut Vec<Event<'a>>) {
41+
let parent_tag = self.tag_stack.last();
42+
43+
// Close pending multi-line tags
44+
if matches!(parent_tag, Some(Tag::BlockQuote)) && !line.starts_with('>')
45+
|| matches!(parent_tag, Some(Tag::UnorderedList))
46+
{
47+
res.push(Event::End);
48+
self.tag_stack.pop();
49+
}
50+
51+
let parent_tag = self.tag_stack.last();
52+
2753
if line.starts_with("```") {
28-
self.inside_pre = !self.inside_pre;
29-
PageElement::Empty
30-
} else if self.inside_pre {
31-
PageElement::Preformatted(line.to_string())
54+
let inner_res = if let Some(Tag::CodeBlock) = parent_tag {
55+
self.tag_stack.pop();
56+
Event::End
57+
} else {
58+
self.tag_stack.push(Tag::CodeBlock);
59+
Event::Start(Tag::CodeBlock)
60+
};
61+
res.push(inner_res);
62+
} else if let Some(Tag::CodeBlock) = parent_tag {
63+
res.push(Event::Text(line));
64+
} else if line.trim().is_empty() {
65+
res.push(Event::BlankLine);
3266
} else if line.starts_with('#') {
33-
PageElement::Heading(line.to_string())
67+
let line = line.trim_end();
68+
let lvl = line.chars().filter(|c| *c == '#').count();
69+
let heading = Tag::Heading(lvl as u8);
70+
res.push(Event::Start(heading));
71+
72+
let text = line.trim_start_matches('#').trim_start();
73+
res.push(Event::Text(text));
74+
res.push(Event::End);
3475
} else if line.starts_with('>') {
35-
PageElement::Quote(line.to_string())
76+
if !matches!(parent_tag, Some(Tag::BlockQuote)) {
77+
res.push(Event::Start(Tag::BlockQuote));
78+
}
79+
res.push(Event::Text(line.trim_start_matches('>')));
3680
} else if let Some(stripped) = line.strip_prefix("* ") {
37-
PageElement::ListItem(stripped.to_string())
38-
} else if let Some(captures) = R_GEMINI_LINK.captures(line) {
39-
match (captures.name("href"), captures.name("label")) {
40-
(Some(m_href), Some(m_label)) if !m_label.as_str().is_empty() => PageElement::Link(
41-
m_href.as_str().to_string(),
42-
Some(m_label.as_str().to_string()),
43-
),
44-
(Some(m_href), _) => PageElement::Link(m_href.as_str().to_string(), None),
45-
_ => PageElement::Empty,
81+
if !matches!(parent_tag, Some(Tag::UnorderedList)) {
82+
res.push(Event::Start(Tag::UnorderedList));
4683
}
84+
res.push(Event::Start(Tag::Item));
85+
res.push(Event::Text(stripped.trim_end()));
86+
res.push(Event::End);
87+
} else if let Some(captures) = R_GEMINI_LINK.captures(line.trim_end()) {
88+
let href = captures.name("href").unwrap();
89+
let label = captures.name("label").map(|x| x.as_str());
90+
res.push(Event::Start(Tag::Link(
91+
href.as_str().to_string(),
92+
label.map(|x| x.to_string()),
93+
)));
94+
res.push(Event::End);
4795
} else {
48-
PageElement::Text(line.to_string())
96+
res.push(Event::Start(Tag::Paragraph));
97+
res.push(Event::Text(line.trim_end()));
98+
res.push(Event::End);
4999
}
50100
}
51101
}

src/main.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ mod common;
1515
mod config;
1616
mod lossy_text_read;
1717
mod macros;
18-
mod text_extensions;
1918
mod widgets;
2019

2120
use gtk::prelude::*;

src/text_extensions/gemini.rs

Lines changed: 0 additions & 198 deletions
This file was deleted.

src/text_extensions/mod.rs

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/widgets/mod.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
mod download_page;
2-
mod input_page;
1+
mod pages;
32
#[allow(clippy::await_holding_refcell_ref)]
43
mod tab;
54
mod window;
65

7-
pub use download_page::DownloadPage;
8-
pub use input_page::InputPage;
96
pub use tab::Tab;
107
pub use window::Window;

0 commit comments

Comments
 (0)