Auto merge of #52 - matthiasbeyer:notes-open, r=matthiasbeyer
Notes open Open notes as HTML pages in browser. Does not work fully by now, as one note should be opened, but two are opened, one in browser, one in libreoffice. Closes #47
This commit is contained in:
commit
3b31fbfa3e
7 changed files with 178 additions and 0 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -12,6 +12,7 @@ dependencies = [
|
|||
"log 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"open 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"prettytable-rs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -28,4 +28,5 @@ open = "1.1.0"
|
|||
itertools = "0.4.5"
|
||||
hoedown = "3.0.3"
|
||||
ansi_term = "0.7.1"
|
||||
rand = "0.3"
|
||||
|
||||
|
|
39
etc/cli.yml
39
etc/cli.yml
|
@ -314,6 +314,45 @@ subcommands:
|
|||
required: false
|
||||
takes_value: true
|
||||
|
||||
- open:
|
||||
about: Open notes as HTML page in browser (via XDG-open)
|
||||
version: 0.1
|
||||
author: Matthias Beyer <mail@beyermatthias.de>
|
||||
args:
|
||||
- onepage:
|
||||
long: onepage
|
||||
short: o
|
||||
help: Open notes as one page
|
||||
required: false
|
||||
takes_value: false
|
||||
|
||||
- id:
|
||||
long: id
|
||||
help: Open note with this ID
|
||||
required: false
|
||||
takes_value: true
|
||||
|
||||
- namegrep:
|
||||
short: n
|
||||
long: name
|
||||
help: Open where name matches this regex
|
||||
required: false
|
||||
takes_value: true
|
||||
|
||||
- grep:
|
||||
short: g
|
||||
long: grep
|
||||
help: Open where grep with regex finds something
|
||||
required: false
|
||||
takes_value: true
|
||||
|
||||
- tags:
|
||||
short: t
|
||||
long: tags
|
||||
help: Open all notes with these tags
|
||||
required: false
|
||||
takes_value: true
|
||||
|
||||
- list:
|
||||
about: List notes
|
||||
version: 0.1
|
||||
|
|
|
@ -12,6 +12,7 @@ extern crate config;
|
|||
extern crate open;
|
||||
extern crate itertools;
|
||||
extern crate ansi_term;
|
||||
extern crate rand;
|
||||
|
||||
pub use cli::CliConfig;
|
||||
pub use configuration::Configuration;
|
||||
|
|
|
@ -68,6 +68,30 @@ pub mod markdown {
|
|||
renderer.extract()
|
||||
}
|
||||
|
||||
pub fn to_html(self) -> String {
|
||||
use hoedown::renderer::html::Html;
|
||||
use hoedown::renderer::html;
|
||||
|
||||
String::from(
|
||||
Html::new(html::Flags::empty(), 0)
|
||||
.render(&self.text)
|
||||
.to_str()
|
||||
.unwrap_or("UTF8Error"))
|
||||
}
|
||||
|
||||
pub fn to_html_page(self) -> String {
|
||||
let header = "
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='content-type' content='text/html; charset=utf-8'>
|
||||
</head>
|
||||
<body>
|
||||
";
|
||||
let content = self.to_html();
|
||||
let footer = "</body></html>";
|
||||
format!("{}{}{}", header, content, footer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -124,6 +124,101 @@ impl<'a> Notes<'a> {
|
|||
return failed == 0;
|
||||
}
|
||||
|
||||
fn command_open(&self, matches: &ArgMatches) -> bool {
|
||||
use std::io::Write;
|
||||
use std::process::exit;
|
||||
|
||||
use open;
|
||||
|
||||
use self::header::get_name_from_header;
|
||||
use ui::external::get_tempfile;
|
||||
|
||||
use module::helpers::content::markdown::MarkdownParser;
|
||||
|
||||
let parser = Parser::new(JsonHeaderParser::new(None));
|
||||
|
||||
let filter = {
|
||||
let hash_filter = create_hash_filter(matches, "id", true);
|
||||
let head_filter = create_text_header_field_grep_filter(matches, "match", "NAME", true);
|
||||
let text_filter = create_content_grep_filter(matches, "match", true);
|
||||
let tags_filter = create_tag_filter(matches, "tags", true);
|
||||
hash_filter.and(Box::new(head_filter)).and(Box::new(text_filter)).and(Box::new(tags_filter))
|
||||
};
|
||||
|
||||
let files = self.rt
|
||||
.store()
|
||||
.load_for_module(self, &parser)
|
||||
.into_iter()
|
||||
.filter(|file| {
|
||||
let res = filter.filter_file(file);
|
||||
debug!("Filter: {} -> {}", file.deref().borrow().id(), res);
|
||||
res
|
||||
});
|
||||
|
||||
if matches.is_present("onepage") {
|
||||
let tmpcontent = files.fold(String::new(), |acc, file| {
|
||||
let heading = {
|
||||
let name = get_name_from_header(file.deref().borrow().header());
|
||||
if name.len() == 0 {
|
||||
format!("# {}", file.deref().borrow().id)
|
||||
} else {
|
||||
format!("# {} <small>({})</small>", name, file.deref().borrow().id())
|
||||
}
|
||||
};
|
||||
|
||||
format!("{}\n\n{}\n\n{}", acc, heading, file.deref().borrow().data())
|
||||
});
|
||||
|
||||
let (temppath, mut tempfile) = match get_tempfile("html") {
|
||||
Some(tpl) => tpl,
|
||||
None => {
|
||||
error!("Could not create tempfile");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let html = MarkdownParser::new(&tmpcontent).to_html_page();
|
||||
|
||||
tempfile.write_all(html.as_ref());
|
||||
open::that(&temppath[..]).is_ok()
|
||||
} else {
|
||||
let result = files.map(|file| {
|
||||
let (temppath, mut tempfile) = match get_tempfile("html") {
|
||||
Some(tpl) => tpl,
|
||||
None => {
|
||||
error!("Could not create tempfile");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let content = format!("# {}\n\n{}",
|
||||
get_name_from_header(file.deref().borrow().header()),
|
||||
file.deref().borrow().data());
|
||||
|
||||
let html = MarkdownParser::new(&content).to_html_page();
|
||||
|
||||
tempfile.write_all(html.as_ref());
|
||||
open::that(&temppath[..]).is_ok()
|
||||
})
|
||||
.fold((0, 0), |acc, succeeded| {
|
||||
let (worked, failed) = acc;
|
||||
if succeeded {
|
||||
(worked + 1, failed)
|
||||
} else {
|
||||
(worked, failed + 1)
|
||||
}
|
||||
});
|
||||
|
||||
let (worked, failed) = result;
|
||||
|
||||
info!("Opening as HTML page succeeded for {} files", worked);
|
||||
info!("Opening as HTML page failed for {} files", failed);
|
||||
|
||||
failed == 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn command_list(&self, matches: &ArgMatches) -> bool {
|
||||
use ansi_term::Colour::{Red, Green};
|
||||
use ui::file::{FilePrinter, TablePrinter};
|
||||
|
@ -361,6 +456,10 @@ impl<'a> Module<'a> for Notes<'a> {
|
|||
self.command_edit(matches.subcommand_matches("edit").unwrap())
|
||||
},
|
||||
|
||||
Some("open") => {
|
||||
self.command_open(matches.subcommand_matches("open").unwrap())
|
||||
},
|
||||
|
||||
Some("list") => {
|
||||
self.command_list(matches.subcommand_matches("list").unwrap())
|
||||
},
|
||||
|
|
13
src/ui/external/mod.rs
vendored
13
src/ui/external/mod.rs
vendored
|
@ -1 +1,14 @@
|
|||
use std::fs::File;
|
||||
|
||||
pub mod editor;
|
||||
|
||||
pub fn get_tempfile(ext: &str) -> Option<(String, File)> {
|
||||
use rand::random;
|
||||
|
||||
let randomname = format!("/tmp/imag-{}.{}", random::<u64>(), ext);
|
||||
debug!("Attempting to create tempfile at {}", randomname);
|
||||
File::create(randomname.clone())
|
||||
.map_err(|e| debug!(" Error -> {}", e))
|
||||
.ok()
|
||||
.map(|f| (randomname, f))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue