Browse Source

Highlight links

openbsd
Julien Blanchard 7 years ago
parent
commit
01cde99006
  1. 167
      src/main.rs

167
src/main.rs

@ -1,8 +1,9 @@
use cursive::align::HAlign; use cursive::align::HAlign;
use cursive::event::EventResult;
use cursive::traits::*; use cursive::traits::*;
use cursive::view::Scrollable; use cursive::view::Scrollable;
use cursive::views::{Dialog, EditView, OnEventView, Panel, SelectView, TextView}; use cursive::views::{Dialog, EditView, Panel, SelectView};
use cursive::utils::markup::StyledString;
use cursive::theme::Effect;
use cursive::Cursive; use cursive::Cursive;
extern crate native_tls; extern crate native_tls;
@ -20,25 +21,63 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use url::Url; use url::Url;
struct Status { #[derive(Debug)]
struct Header {
code: i8, code: i8,
meta: String, meta: String
} }
impl FromStr for Status { #[derive(Debug)]
struct Link {
url: String,
label: String
}
impl FromStr for Header {
type Err = std::string::ParseError;
// Parses a string into an instance of 'Header'
fn from_str(line: &str) -> Result<Self, Self::Err> {
let text = format!("{}", line);
let link_regexp = Regex::new(r"^(\d*)\t(.*)$").unwrap();
let caps = link_regexp.captures(&text).unwrap();
let code_str = caps.get(1).map_or("", |m| m.as_str());
let meta_str = caps.get(2).map_or("", |m| m.as_str());
let code = code_str.parse::<i8>().unwrap();
let meta = meta_str.to_string();
Ok(Header {code, meta})
}
}
impl FromStr for Link {
type Err = std::string::ParseError; type Err = std::string::ParseError;
// Parses a string into an instance of 'Status' // Parses a string into an instance of 'Link'
fn from_str(status_line: &str) -> Result<Self, Self::Err> { fn from_str(line: &str) -> Result<Self, Self::Err> {
let mut splits = status_line.split("\t"); let text = format!("{}", line);
let code: i8 = splits.nth(1).unwrap().parse().unwrap(); let link_regexp = Regex::new(r"^=>\s(\S*)\s*(.*)?$").unwrap();
let meta: String = String::from(splits.nth(2).unwrap());
let caps = link_regexp.captures(&text).unwrap();
let url_str = caps.get(1).map_or("", |m| m.as_str());
let label_str = caps.get(2).map_or("", |m| m.as_str());
let url = url_str.to_string();
let label = if label_str.is_empty() {
url_str.to_string()
} else {
label_str.to_string()
};
Ok(Status {code, meta}) Ok(Link {url, label})
} }
} }
const HELP: &'static str = " const HELP: &'static str = "
Welcome to Asuka Gemini browser!
Press g to visit an URL Press g to visit an URL
Press b to go back Press b to go back
Press q to exit Press q to exit
@ -54,16 +93,6 @@ fn main() {
select.add_all_str(HELP.lines()); select.add_all_str(HELP.lines());
select.set_on_submit(follow_link); select.set_on_submit(follow_link);
// let select_view = OnEventView::new(select)
// .on_pre_event_inner('k', |s, _| {
// s.select_up(1);
// Some(EventResult::Consumed(None))
// })
// .on_pre_event_inner('j', |s, _| {
// s.select_down(1);
// Some(EventResult::Consumed(None))
// });
siv.add_fullscreen_layer( siv.add_fullscreen_layer(
Dialog::around(Panel::new( Dialog::around(Panel::new(
select.with_id("main").scrollable().full_screen() select.with_id("main").scrollable().full_screen()
@ -74,11 +103,6 @@ fn main() {
.with_id("container") .with_id("container")
); );
// Show a welcome popup
// siv.add_layer(Dialog::info(
// "Welcome to Asuka, the first ncurses Gemini browser!\n",
// ));
// We can quit by pressing q // We can quit by pressing q
siv.add_global_callback('q', |s| s.quit()); siv.add_global_callback('q', |s| s.quit());
// pressing g prompt for an URL // pressing g prompt for an URL
@ -97,12 +121,23 @@ fn prompt_for_url(s: &mut Cursive) {
.padding((1, 1, 1, 0)) .padding((1, 1, 1, 0))
.content( .content(
EditView::new() EditView::new()
.on_submit(visit_url) .on_submit(goto_url)
.fixed_width(20) .fixed_width(20)
).with_id("url_popup") ).with_id("url_popup")
); );
} }
fn goto_url(s: &mut Cursive, url: &str) {
// Prepend gemini scheme if needed
let url_s = if url.starts_with("gemini://") {
url.to_owned()
} else {
format!("gemini://{}", url)
};
visit_url(s, &url_s)
}
fn visit_url(s: &mut Cursive, url_s: &str) { fn visit_url(s: &mut Cursive, url_s: &str) {
// Close URL popup if any // Close URL popup if any
if s.find_id::<Dialog>("url_popup").is_some() { if s.find_id::<Dialog>("url_popup").is_some() {
@ -113,13 +148,7 @@ fn visit_url(s: &mut Cursive, url_s: &str) {
Ok(url) => { Ok(url) => {
match get_data(url) { match get_data(url) {
Ok(new_content) => { Ok(new_content) => {
let mut main_view = s.find_id::<SelectView>("main").unwrap(); draw_content(s, url_s, new_content);
let mut container = s.find_id::<Dialog>("container").unwrap();
container.set_title(url_s);
main_view.clear();
main_view.add_all_str(new_content.lines());
main_view.set_on_submit(follow_link);
} }
Err(msg) => { Err(msg) => {
s.add_layer(Dialog::info(msg)); s.add_layer(Dialog::info(msg));
@ -132,21 +161,8 @@ fn visit_url(s: &mut Cursive, url_s: &str) {
} }
} }
fn follow_link(s: &mut Cursive, line: &str) {
let text = format!("{}", line);
let link_regexp = Regex::new(r"^=>\s(\S*)(.*)?$").unwrap();
if link_regexp.is_match(&text) {
let caps = link_regexp.captures(&text).unwrap();
let url = caps.get(1).map_or("", |m| m.as_str());
let next_url = parse_link(url);
visit_url(s, next_url.expect("Not an URL").as_str())
} else {
()
}
}
fn parse_link(url: &str) -> Result<url::Url, url::ParseError> { fn parse_link(url: &str) -> Result<url::Url, url::ParseError> {
// Creates an absolute link if needed
match get_last_host() { match get_last_host() {
Some(host) => { Some(host) => {
let url_s = if url.starts_with("gemini://") { let url_s = if url.starts_with("gemini://") {
@ -171,6 +187,60 @@ fn parse_link(url: &str) -> Result<url::Url, url::ParseError> {
} }
} }
fn draw_content(s: &mut Cursive, url: &str, content: String) {
let mut main_view = s.find_id::<SelectView>("main").unwrap();
let mut container = s.find_id::<Dialog>("container").unwrap();
container.set_title(url);
main_view.clear();
for line in content.lines() {
if is_header(line) {
// let _header = Header::from_str(line);
} else if is_link(line) {
let link = Link::from_str(line).unwrap();
let mut formatted = StyledString::new();
formatted.append(StyledString::styled(
link.label,
Effect::Underline,
));
main_view.add_item(formatted, link.url)
} else {
main_view.add_item(line, "0".to_owned())
}
}
main_view.set_on_submit(follow_link);
}
fn is_header(line: &str) -> bool {
let text = format!("{}", line);
let header_regexp = Regex::new(r"^(\d*)\t(\S*)$").unwrap();
header_regexp.is_match(&text)
}
fn is_link(line: &str) -> bool {
let text = format!("{}", line);
let link_regexp = Regex::new(r"^=>\s(\S*)(.*)?$").unwrap();
link_regexp.is_match(&text)
}
fn is_gemini_link(line: &str) -> bool {
line != "0"
}
fn follow_link (s: &mut Cursive, line: &str) {
if is_gemini_link(&line) {
let next_url = parse_link(&line);
visit_url(s, next_url.expect("Not an URL").as_str())
} else {
()
}
}
fn get_data(url: url::Url) -> Result<String, String> { fn get_data(url: url::Url) -> Result<String, String> {
let host = url.host_str().unwrap(); let host = url.host_str().unwrap();
let path = url.path(); let path = url.path();
@ -237,6 +307,7 @@ fn get_previous_url() -> Option<String> {
let lines_count = content.lines().count(); let lines_count = content.lines().count();
if lines_count > 1 { if lines_count > 1 {
// Return before last line
Some(content.lines().nth(lines_count - 2).unwrap().to_owned()) Some(content.lines().nth(lines_count - 2).unwrap().to_owned())
} else { } else {
None None

Loading…
Cancel
Save