separate Reference impl

This commit is contained in:
yggverse 2026-03-08 17:25:27 +02:00
parent df419181e6
commit 22c50161af
2 changed files with 80 additions and 54 deletions

View file

@ -2,6 +2,7 @@ mod ansi;
pub mod error;
mod gutter;
mod icon;
mod reference;
mod syntax;
mod tag;
@ -13,11 +14,12 @@ use gtk::{
UriLauncher, Window, WrapMode,
gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY, BUTTON_SECONDARY, RGBA},
gio::{Cancellable, SimpleAction, SimpleActionGroup},
glib::{Uri, UriFlags, uuid_string_random},
glib::{Uri, uuid_string_random},
prelude::{PopoverExt, TextBufferExt, TextBufferExtManual, TextTagExt, TextViewExt, WidgetExt},
};
use gutter::Gutter;
use icon::Icon;
use reference::Reference;
use regex::Regex;
use sourceview::prelude::{ActionExt, ActionMapExt, DisplayExt, ToVariant};
use std::{cell::Cell, collections::HashMap, rc::Rc};
@ -569,7 +571,7 @@ fn link(
buffer.set_text("");
let mut last_pos = 0;
for cap in Regex::new(r"(?P<is_img>!?)\[(?P<text>[^\]]+)\]\((?P<url>[^\)]+)\)")
for cap in Regex::new(r"(?P<is_img>!)\[(?P<text>[^\]]+)\]\((?P<url>[^\)]+)\)")
.unwrap()
.captures_iter(&full_content)
{
@ -578,41 +580,16 @@ fn link(
if !before.is_empty() {
buffer.insert(&mut buffer.end_iter(), before);
}
// Relative scheme patch
// https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
let unresolved_url = match cap["url"].strip_prefix("//") {
Some(p) => {
let s = p.trim_start_matches(":");
&format!(
"{}://{}",
base.scheme(),
if s.is_empty() {
format!("{}/", base.host().unwrap_or_default())
} else {
s.into()
}
)
}
None => &cap["url"],
};
// Convert address to the valid URI,
// resolve to absolute URL format if the target is relative
match Uri::resolve_relative(Some(&base.to_string()), unresolved_url, UriFlags::NONE) {
Ok(url) => match Uri::parse(&url, UriFlags::NONE) {
Ok(uri) => {
let alt = {
let mut a: Vec<&str> = Vec::with_capacity(2);
if uri.scheme() != base.scheme() {
a.push("");
}
match Reference::parse(
&cap["url"],
if cap["text"].is_empty() {
a.push(&cap["url"]);
None
} else {
a.push(&cap["text"]);
}
a.join(" ")
};
Some(&cap["text"])
},
base,
) {
Some(link) => {
let a = TextTag::builder()
.foreground_rgba(link_color)
// .foreground_rgba(&adw::StyleManager::default().accent_color_rgba())
@ -620,17 +597,13 @@ fn link(
.sentence(true)
.wrap_mode(WrapMode::Word)
.build();
if !tag.text_tag_table.add(&a) {
panic!()
}
buffer.insert_with_tags(&mut buffer.end_iter(), &alt, &[&a]);
links.insert(a, uri);
buffer.insert_with_tags(&mut buffer.end_iter(), &link.alt, &[&a]);
links.insert(a, link.uri);
}
Err(_) => todo!(),
},
Err(_) => continue,
None => continue,
}
last_pos = full_match.end();
}

View file

@ -0,0 +1,53 @@
use gtk::glib::{Uri, UriFlags};
pub struct Reference {
pub uri: Uri,
pub alt: String,
}
impl Reference {
pub fn parse(address: &str, alt: Option<&str>, base: &Uri) -> Option<Self> {
// Convert address to the valid URI,
// resolve to absolute URL format if the target is relative
match Uri::resolve_relative(
Some(&base.to_string()),
// Relative scheme patch
// https://datatracker.ietf.org/doc/html/rfc3986#section-4.2
&match address.strip_prefix("//") {
Some(p) => {
let s = p.trim_start_matches(":");
format!(
"{}://{}",
base.scheme(),
if s.is_empty() {
format!("{}/", base.host().unwrap_or_default())
} else {
s.into()
}
)
}
None => String::new(),
},
UriFlags::NONE,
) {
Ok(ref url) => match Uri::parse(url, UriFlags::NONE) {
Ok(uri) => {
let mut a: Vec<&str> = Vec::with_capacity(2);
if uri.scheme() != base.scheme() {
a.push("");
}
match alt {
Some(text) => a.push(text),
None => a.push(url),
}
Some(Self {
uri,
alt: a.join(" "),
})
}
Err(_) => todo!(),
},
Err(_) => None,
}
}
}