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::event::EventResult;
use cursive::traits::*;
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;
extern crate native_tls;
@ -20,25 +21,63 @@ use std::str;
use std::str::FromStr;
use url::Url;
struct Status {
#[derive(Debug)]
struct Header {
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;
// Parses a string into an instance of 'Status'
fn from_str(status_line: &str) -> Result<Self, Self::Err> {
let mut splits = status_line.split("\t");
let code: i8 = splits.nth(1).unwrap().parse().unwrap();
let meta: String = String::from(splits.nth(2).unwrap());
// Parses a string into an instance of 'Link'
fn from_str(line: &str) -> Result<Self, Self::Err> {
let text = format!("{}", line);
let link_regexp = Regex::new(r"^=>\s(\S*)\s*(.*)?$").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 = "
Welcome to Asuka Gemini browser!
Press g to visit an URL
Press b to go back
Press q to exit
@ -54,16 +93,6 @@ fn main() {
select.add_all_str(HELP.lines());
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(
Dialog::around(Panel::new(
select.with_id("main").scrollable().full_screen()
@ -74,11 +103,6 @@ fn main() {
.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
siv.add_global_callback('q', |s| s.quit());
// pressing g prompt for an URL
@ -97,12 +121,23 @@ fn prompt_for_url(s: &mut Cursive) {
.padding((1, 1, 1, 0))
.content(
EditView::new()
.on_submit(visit_url)
.on_submit(goto_url)
.fixed_width(20)
).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) {
// Close URL popup if any
if s.find_id::<Dialog>("url_popup").is_some() {
@ -113,13 +148,7 @@ fn visit_url(s: &mut Cursive, url_s: &str) {
Ok(url) => {
match get_data(url) {
Ok(new_content) => {
let mut main_view = s.find_id::<SelectView>("main").unwrap();
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);
draw_content(s, url_s, new_content);
}
Err(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> {
// Creates an absolute link if needed
match get_last_host() {
Some(host) => {
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> {
let host = url.host_str().unwrap();
let path = url.path();
@ -237,6 +307,7 @@ fn get_previous_url() -> Option<String> {
let lines_count = content.lines().count();
if lines_count > 1 {
// Return before last line
Some(content.lines().nth(lines_count - 2).unwrap().to_owned())
} else {
None

Loading…
Cancel
Save