Browse Source

use ggemtext crate

master
yggverse 1 month ago
parent
commit
7f2e5d2210
  1. 4
      Cargo.toml
  2. 11
      src/app/browser/window/tab/item/page/content/text/gemini/reader.rs
  3. 25
      src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/code.rs
  4. 29
      src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/code/inline.rs
  5. 46
      src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/code/multiline.rs
  6. 47
      src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/header.rs
  7. 89
      src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/link.rs
  8. 29
      src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/list.rs
  9. 29
      src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/quote.rs

4
Cargo.toml

@ -23,3 +23,7 @@ features = ["v4_10"] @@ -23,3 +23,7 @@ features = ["v4_10"]
package = "libadwaita"
version = "0.7.0"
features = ["v1_6"]
[dependencies.gemtext]
package = "ggemtext"
version = "0.1.1"

11
src/app/browser/window/tab/item/page/content/text/gemini/reader.rs

@ -1,18 +1,17 @@ @@ -1,18 +1,17 @@
mod parser;
mod tag;
mod widget;
use parser::{
use tag::Tag;
use widget::Widget;
use adw::StyleManager;
use gemtext::line::{
code::Code,
header::{Header, Level},
link::Link,
list::List,
quote::Quote,
};
use tag::Tag;
use widget::Widget;
use adw::StyleManager;
use gtk::{
gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY},
gio::{Cancellable, SimpleAction},

25
src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/code.rs

@ -1,25 +0,0 @@ @@ -1,25 +0,0 @@
pub mod inline;
pub mod multiline;
use inline::Inline;
use multiline::Multiline;
pub struct Code {
// nothing yet..
}
impl Code {
// Inline
pub fn inline_from(line: &str) -> Option<Inline> {
Inline::from(line)
}
// Multiline
pub fn multiline_begin_from(line: &str) -> Option<Multiline> {
Multiline::begin_from(line)
}
pub fn multiline_continue_from(this: &mut Multiline, line: &str) {
Multiline::continue_from(this, line)
}
}

29
src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/code/inline.rs

@ -1,29 +0,0 @@ @@ -1,29 +0,0 @@
use gtk::glib::{GString, Regex, RegexCompileFlags, RegexMatchFlags};
pub struct Inline {
pub value: GString,
}
impl Inline {
pub fn from(line: &str) -> Option<Self> {
// Parse line
let regex = Regex::split_simple(
r"^`{3}([^`]*)`{3}$",
line,
RegexCompileFlags::DEFAULT,
RegexMatchFlags::DEFAULT,
);
// Detect value
let value = regex.get(1)?;
if value.trim().is_empty() {
return None;
}
// Result
Some(Self {
value: GString::from(value.as_str()),
})
}
}

46
src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/code/multiline.rs

@ -1,46 +0,0 @@ @@ -1,46 +0,0 @@
use gtk::glib::GString;
pub struct Multiline {
pub alt: Option<GString>,
pub buffer: Vec<GString>,
pub completed: bool,
}
impl Multiline {
// Search in line for tag open,
// return Self constructed on success or None
pub fn begin_from(line: &str) -> Option<Self> {
if line.starts_with("```") {
let alt = line.trim_start_matches("```");
return Some(Self {
alt: match alt.trim().is_empty() {
true => None,
false => Some(GString::from(alt)),
},
buffer: Vec::new(),
completed: false,
});
}
None
}
// Continue preformatted buffer from line,
// set `completed` as True on close tag found
pub fn continue_from(&mut self, line: &str) {
// Make sure buffer not completed yet
if self.completed {
panic!("Could not continue as completed") // @TODO handle
}
// Line contain close tag
if line.ends_with("```") {
self.completed = true;
}
// Append data to the buffer, trim close tag on exists
self.buffer
.push(GString::from(line.trim_end_matches("```")));
}
}

47
src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/header.rs

@ -1,47 +0,0 @@ @@ -1,47 +0,0 @@
use gtk::glib::{GString, Regex, RegexCompileFlags, RegexMatchFlags};
pub enum Level {
H1,
H2,
H3,
}
pub struct Header {
pub value: GString,
pub level: Level,
}
impl Header {
pub fn from(line: &str) -> Option<Self> {
// Parse line
let regex = Regex::split_simple(
r"^(#{1,3})\s*(.+)$",
line,
RegexCompileFlags::DEFAULT,
RegexMatchFlags::DEFAULT,
);
// Detect header level
let level = regex.get(1)?;
let level = match level.len() {
1 => Level::H1,
2 => Level::H2,
3 => Level::H3,
_ => return None,
};
// Detect header value
let value = regex.get(2)?;
if value.trim().is_empty() {
return None;
}
// Result
Some(Self {
level,
value: GString::from(value.as_str()),
})
}
}

89
src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/link.rs

@ -1,89 +0,0 @@ @@ -1,89 +0,0 @@
use gtk::glib::{
DateTime, GString, Regex, RegexCompileFlags, RegexMatchFlags, TimeZone, Uri, UriFlags,
};
pub struct Link {
pub alt: Option<GString>, // [optional] alternative link description
pub is_external: Option<bool>, // [optional] external link indication, on base option provided
pub timestamp: Option<DateTime>, // [optional] valid link DateTime object
pub uri: Uri, // [required] valid link URI object
}
impl Link {
pub fn from(line: &str, base: Option<&Uri>, timezone: Option<&TimeZone>) -> Option<Self> {
// Define initial values
let mut alt = None;
let mut timestamp = None;
let mut is_external = None;
// Begin line parse
let regex = Regex::split_simple(
r"^=>\s*([^\s]+)\s*(\d{4}-\d{2}-\d{2})?\s*(.+)?$",
line,
RegexCompileFlags::DEFAULT,
RegexMatchFlags::DEFAULT,
);
// Detect address required to continue
let unresolved_address = regex.get(1)?;
// Convert address to the valid URI
let uri = match base {
// Base conversion requested
Some(base_uri) => {
// Convert relative address to absolute
match Uri::resolve_relative(
Some(&base_uri.to_str()),
unresolved_address.as_str(),
UriFlags::NONE,
) {
Ok(resolved_str) => {
// Try convert string to the valid URI
match Uri::parse(&resolved_str, UriFlags::NONE) {
Ok(resolved_uri) => {
// Change external status
is_external = Some(resolved_uri.scheme() != base_uri.scheme());
// Result
resolved_uri
}
Err(_) => return None,
}
}
Err(_) => return None,
}
}
// Base resolve not requested
None => {
// Just try convert address to valid URI
match Uri::parse(&unresolved_address, UriFlags::NONE) {
Ok(unresolved_uri) => unresolved_uri,
Err(_) => return None,
}
}
};
// Timestamp
if let Some(date) = regex.get(2) {
// @TODO even possible, but simpler to work with `DateTime` API
// await for new features in `Date` as better in Gemini context
// https://docs.gtk.org/glib/struct.Date.html
timestamp = match DateTime::from_iso8601(&format!("{date}T00:00:00"), timezone) {
Ok(value) => Some(value),
Err(_) => None,
}
}
// Alt
if let Some(value) = regex.get(3) {
alt = Some(GString::from(value.as_str()))
};
Some(Self {
alt,
is_external,
timestamp,
uri,
})
}
}

29
src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/list.rs

@ -1,29 +0,0 @@ @@ -1,29 +0,0 @@
use gtk::glib::{GString, Regex, RegexCompileFlags, RegexMatchFlags};
pub struct List {
pub value: GString,
}
impl List {
pub fn from(line: &str) -> Option<Self> {
// Parse line
let regex = Regex::split_simple(
r"^\*\s*(.+)$",
line,
RegexCompileFlags::DEFAULT,
RegexMatchFlags::DEFAULT,
);
// Detect value
let value = regex.get(1)?;
if value.trim().is_empty() {
return None;
}
// Result
Some(Self {
value: GString::from(value.as_str()),
})
}
}

29
src/app/browser/window/tab/item/page/content/text/gemini/reader/parser/quote.rs

@ -1,29 +0,0 @@ @@ -1,29 +0,0 @@
use gtk::glib::{GString, Regex, RegexCompileFlags, RegexMatchFlags};
pub struct Quote {
pub value: GString,
}
impl Quote {
pub fn from(line: &str) -> Option<Self> {
// Parse line
let regex = Regex::split_simple(
r"^>\s*(.+)$",
line,
RegexCompileFlags::DEFAULT,
RegexMatchFlags::DEFAULT,
);
// Detect value
let value = regex.get(1)?;
if value.trim().is_empty() {
return None;
}
// Result
Some(Self {
value: GString::from(value.as_str()),
})
}
}
Loading…
Cancel
Save