mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-01-15 17:20:08 +00:00
draft link parser
This commit is contained in:
parent
454feaeba5
commit
860f76ce49
@ -4,6 +4,7 @@ mod text;
|
|||||||
use text::Text;
|
use text::Text;
|
||||||
|
|
||||||
use gtk::{
|
use gtk::{
|
||||||
|
glib::Uri,
|
||||||
prelude::{BoxExt, WidgetExt},
|
prelude::{BoxExt, WidgetExt},
|
||||||
Box, Orientation,
|
Box, Orientation,
|
||||||
};
|
};
|
||||||
@ -29,7 +30,7 @@ impl Content {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
pub fn reset(&self, mime: Mime, data: &str) {
|
pub fn reset(&self, mime: Mime, base: &Uri, data: &str) {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
while let Some(child) = self.widget.last_child() {
|
while let Some(child) = self.widget.last_child() {
|
||||||
self.widget.remove(&child)
|
self.widget.remove(&child)
|
||||||
@ -38,7 +39,7 @@ impl Content {
|
|||||||
// Compose
|
// Compose
|
||||||
match mime {
|
match mime {
|
||||||
Mime::TextGemini => {
|
Mime::TextGemini => {
|
||||||
self.widget.append(Text::gemini(data).widget());
|
self.widget.append(Text::gemini(data, base).widget());
|
||||||
}
|
}
|
||||||
Mime::TextPlain => {
|
Mime::TextPlain => {
|
||||||
todo!()
|
todo!()
|
||||||
|
@ -2,17 +2,21 @@ mod reader;
|
|||||||
|
|
||||||
use reader::Reader;
|
use reader::Reader;
|
||||||
|
|
||||||
use gtk::Viewport;
|
use gtk::{
|
||||||
|
glib::{GString, Uri},
|
||||||
|
Viewport,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Gemini {
|
pub struct Gemini {
|
||||||
|
reader: Reader,
|
||||||
widget: Viewport,
|
widget: Viewport,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gemini {
|
impl Gemini {
|
||||||
// Construct
|
// Construct
|
||||||
pub fn new(gemtext: &str) -> Self {
|
pub fn new(gemtext: &str, base: &Uri) -> Self {
|
||||||
// Init components
|
// Init components
|
||||||
let reader = Reader::new(gemtext);
|
let reader = Reader::new(gemtext, base);
|
||||||
|
|
||||||
// Init widget
|
// Init widget
|
||||||
let widget = Viewport::builder().scroll_to_focus(false).build();
|
let widget = Viewport::builder().scroll_to_focus(false).build();
|
||||||
@ -20,10 +24,14 @@ impl Gemini {
|
|||||||
widget.set_child(Some(reader.widget()));
|
widget.set_child(Some(reader.widget()));
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
Self { widget }
|
Self { reader, widget }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
pub fn reader_title(&self) -> &Option<GString> {
|
||||||
|
&self.reader.title()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn widget(&self) -> &Viewport {
|
pub fn widget(&self) -> &Viewport {
|
||||||
&self.widget
|
&self.widget
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
use parser::header::Header;
|
use parser::header::Header;
|
||||||
|
use parser::link::Link;
|
||||||
use parser::plain::Plain;
|
use parser::plain::Plain;
|
||||||
|
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib::GString,
|
glib::{GString, Uri},
|
||||||
prelude::{StyleContextExt, WidgetExt},
|
prelude::{StyleContextExt, WidgetExt},
|
||||||
Align, CssProvider, Label, STYLE_PROVIDER_PRIORITY_APPLICATION,
|
Align, CssProvider, Label, STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||||
};
|
};
|
||||||
@ -17,7 +18,7 @@ pub struct Reader {
|
|||||||
|
|
||||||
impl Reader {
|
impl Reader {
|
||||||
// Construct
|
// Construct
|
||||||
pub fn new(gemtext: &str) -> Self {
|
pub fn new(gemtext: &str, base: &Uri) -> Self {
|
||||||
// Init title
|
// Init title
|
||||||
let mut title = None;
|
let mut title = None;
|
||||||
|
|
||||||
@ -38,7 +39,13 @@ impl Reader {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is link @TODO
|
// Is link
|
||||||
|
if let Some(link) = Link::from(line, base) {
|
||||||
|
// Format
|
||||||
|
markup.push_str(link.markup());
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Nothing match, escape string just
|
// Nothing match, escape string just
|
||||||
markup.push_str(Plain::from(line).markup())
|
markup.push_str(Plain::from(line).markup())
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
use gtk::glib::{
|
||||||
|
markup_escape_text, GString, Regex, RegexCompileFlags, RegexMatchFlags, Uri, UriFlags,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Link {
|
||||||
|
alt: Option<GString>, // [optional] alternative text
|
||||||
|
date: Option<GString>, // [optional] date @TODO store in UnixTime?
|
||||||
|
external: bool, // external link indicator
|
||||||
|
link: GString, // original link, wanted for title tooltip
|
||||||
|
markup: GString, // pango markup with escaped special chars
|
||||||
|
uri: Uri, // parsed link object (currently not in use)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Link {
|
||||||
|
// Link structure parser
|
||||||
|
// line - gemtext subject to parse
|
||||||
|
// base - Uri object, required for:
|
||||||
|
// 1. relative to absolute address conversion
|
||||||
|
// 2. external links indication
|
||||||
|
// returns new Link struct or None
|
||||||
|
pub fn from(line: &str, base: &Uri) -> Option<Link> {
|
||||||
|
// Init struct members
|
||||||
|
let alt: Option<GString> = None;
|
||||||
|
let date: Option<GString> = None;
|
||||||
|
let external: bool;
|
||||||
|
let link: GString;
|
||||||
|
let markup: GString;
|
||||||
|
let uri: Uri;
|
||||||
|
|
||||||
|
// Parse line
|
||||||
|
let parsed = Regex::split_simple(
|
||||||
|
r"^=>\s*([^\s]+)(\s(\d{4}-\d{2}-\d{2}))?(\s(.+))?$",
|
||||||
|
line,
|
||||||
|
RegexCompileFlags::DEFAULT,
|
||||||
|
RegexMatchFlags::DEFAULT,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Address
|
||||||
|
match parsed.get(1) {
|
||||||
|
Some(address) => {
|
||||||
|
// Define original link value (used in titles or when alt is empty)
|
||||||
|
link = GString::from(address.as_str());
|
||||||
|
// Links in document usually relative, make them absolute to base given
|
||||||
|
match Uri::resolve_relative(Some(&base.to_str()), address.as_str(), UriFlags::NONE)
|
||||||
|
{
|
||||||
|
Ok(resolved) => {
|
||||||
|
// Make URI parsed as always valid (no idea why does lib operate strings, not objects)
|
||||||
|
match Uri::parse(&resolved, UriFlags::NONE) {
|
||||||
|
Ok(object) => {
|
||||||
|
// Set external status
|
||||||
|
external = object.host() == base.host();
|
||||||
|
|
||||||
|
// Set struct URI
|
||||||
|
uri = object;
|
||||||
|
}
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Date
|
||||||
|
if let Some(date) = parsed.get(2) {
|
||||||
|
// date = date.as_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt
|
||||||
|
if let Some(alt) = parsed.get(3) {
|
||||||
|
// alt = alt.as_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Markup
|
||||||
|
markup = GString::from(format!(
|
||||||
|
"<a href=\"{}\" title=\"{}\"><span underline=\"none\">{}</span></a>\n",
|
||||||
|
markup_escape_text(&uri.to_str()), // use resolved address for href
|
||||||
|
markup_escape_text(&link), // show original address for title
|
||||||
|
markup_escape_text(&link), // @TODO
|
||||||
|
));
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
alt,
|
||||||
|
date,
|
||||||
|
external,
|
||||||
|
link,
|
||||||
|
markup,
|
||||||
|
uri,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
pub fn alt(&self) -> &Option<GString> {
|
||||||
|
&self.alt
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn date(&self) -> &Option<GString> {
|
||||||
|
&self.date
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn external(&self) -> &bool {
|
||||||
|
&self.external
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn link(&self) -> &GString {
|
||||||
|
&self.link
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn markup(&self) -> &GString {
|
||||||
|
&self.markup
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uri(&self) -> &Uri {
|
||||||
|
&self.uri
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
pub mod header;
|
pub mod header;
|
||||||
|
pub mod link;
|
||||||
pub mod plain;
|
pub mod plain;
|
||||||
|
@ -2,7 +2,7 @@ mod gemini;
|
|||||||
|
|
||||||
use gemini::Gemini;
|
use gemini::Gemini;
|
||||||
|
|
||||||
use gtk::ScrolledWindow;
|
use gtk::{glib::Uri, ScrolledWindow};
|
||||||
|
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
widget: ScrolledWindow,
|
widget: ScrolledWindow,
|
||||||
@ -10,9 +10,9 @@ pub struct Text {
|
|||||||
|
|
||||||
impl Text {
|
impl Text {
|
||||||
// Construct
|
// Construct
|
||||||
pub fn gemini(gemtext: &str) -> Self {
|
pub fn gemini(gemtext: &str, base: &Uri) -> Self {
|
||||||
// Init components
|
// Init components
|
||||||
let gemini = Gemini::new(gemtext);
|
let gemini = Gemini::new(gemtext, base);
|
||||||
|
|
||||||
// Init widget
|
// Init widget
|
||||||
let widget = ScrolledWindow::builder().build();
|
let widget = ScrolledWindow::builder().build();
|
||||||
|
@ -160,7 +160,7 @@ impl Page {
|
|||||||
meta.borrow_mut().mime = Mime::TextGemini;
|
meta.borrow_mut().mime = Mime::TextGemini;
|
||||||
// Select widget
|
// Select widget
|
||||||
match parts.get(4) {
|
match parts.get(4) {
|
||||||
Some(source) => content.reset(content::Mime::TextGemini, source),
|
Some(source) => content.reset(content::Mime::TextGemini, &uri, &source),
|
||||||
None => todo!(),
|
None => todo!(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user