From 969211174a318f65c0ad98472e852060843d6814 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 8 Oct 2017 12:55:41 +0200 Subject: [PATCH 1/2] Rewrite imag-view The imag-view command was redesigned with this change. It now looks up view-templates in the config and calls them with the "in" subcommand. This way, an entry can be viewed with editor, browser or whatever one likes. Compiling markdown is not supported yet. --- bin/core/imag-view/Cargo.toml | 4 + bin/core/imag-view/src/editor.rs | 43 ---------- bin/core/imag-view/src/main.rs | 130 ++++++++++++++++++++++++------- bin/core/imag-view/src/ui.rs | 58 ++------------ doc/src/09020-changelog.md | 3 + 5 files changed, 116 insertions(+), 122 deletions(-) delete mode 100644 bin/core/imag-view/src/editor.rs diff --git a/bin/core/imag-view/Cargo.toml b/bin/core/imag-view/Cargo.toml index 39b508b1..8bdd122c 100644 --- a/bin/core/imag-view/Cargo.toml +++ b/bin/core/imag-view/Cargo.toml @@ -16,7 +16,11 @@ homepage = "http://imag-pim.org" [dependencies] clap = ">=2.17" log = "0.3" +toml = "0.4" +toml-query = "0.3" version = "2.0.1" +handlebars = "0.29.0" +tempfile = "2.1" libimagstore = { version = "0.5.0", path = "../../../lib/core/libimagstore" } libimagrt = { version = "0.5.0", path = "../../../lib/core/libimagrt" } diff --git a/bin/core/imag-view/src/editor.rs b/bin/core/imag-view/src/editor.rs deleted file mode 100644 index bbd255f7..00000000 --- a/bin/core/imag-view/src/editor.rs +++ /dev/null @@ -1,43 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015, 2016 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -use libimagstore::store::FileLockEntry; -use libimagrt::runtime::Runtime; -use libimagentryview::builtin::editor::EditorView; -use libimagentryview::viewer::Viewer; -use libimagentryview::error::ViewError as VE; - -pub struct Editor<'a> { - rt: &'a Runtime<'a>, - fle: &'a FileLockEntry<'a>, -} - -impl<'a> Editor<'a> { - pub fn new(rt: &'a Runtime, fle: &'a FileLockEntry) -> Editor<'a> { - Editor{ - rt: rt, - fle: fle, - } - } - - pub fn show(self) -> Result<(), VE> { - EditorView::new(self.rt).view_entry(self.fle) - } -} - diff --git a/bin/core/imag-view/src/main.rs b/bin/core/imag-view/src/main.rs index e6b78092..318f4e22 100644 --- a/bin/core/imag-view/src/main.rs +++ b/bin/core/imag-view/src/main.rs @@ -35,25 +35,35 @@ extern crate clap; #[macro_use] extern crate log; #[macro_use] extern crate version; +extern crate handlebars; +extern crate tempfile; +extern crate toml; +extern crate toml_query; extern crate libimagentryview; extern crate libimagerror; extern crate libimagrt; extern crate libimagstore; -use std::process::exit; +use std::collections::BTreeMap; +use std::io::Write; use std::path::PathBuf; +use std::process::Command; +use std::process::exit; + +use handlebars::Handlebars; +use toml_query::read::TomlValueReadExt; +use toml::Value; use libimagrt::setup::generate_runtime_setup; use libimagerror::trace::trace_error_exit; +use libimagerror::trace::MapErrTrace; use libimagentryview::builtin::stdout::StdoutViewer; use libimagentryview::viewer::Viewer; +use libimagentryview::error::ViewError as VE; mod ui; -mod editor; - use ui::build_ui; -use editor::Editor; fn main() { let rt = generate_runtime_setup( "imag-view", @@ -76,34 +86,98 @@ fn main() { } }; - let res = { - match rt.cli().subcommand_matches("view-in") { - None => { - debug!("No commandline call"); - debug!("Assuming to view in cli (stdout)"); + if rt.cli().is_present("in") { + let viewer = rt + .cli() + .value_of("in") + .ok_or_else::(|| "No viewer given".to_owned().into()) + .map_err_trace_exit(1) + .unwrap(); // saved by above call + + let config = rt + .config() + .ok_or_else::(|| "No configuration, cannot continue".to_owned().into()) + .map_err_trace_exit(1) + .unwrap(); + + let query = format!("view.viewers.{}", viewer); + match config.config().read(&query) { + Err(e) => trace_error_exit(&e, 1), + Ok(None) => { + error!("Cannot find '{}' in config", query); + exit(1) }, - Some(s) => { - if s.is_present("view-in-stdout") { - } else if s.is_present("view-in-ui") { - warn!("Viewing in UI is currently not supported, switch to stdout"); - } else if s.is_present("view-in-browser") { - warn!("Viewing in browser is currently not supported, switch to stdout"); - } else if s.is_present("view-in-texteditor") { - if let Err(e) = Editor::new(&rt, &entry).show() { - error!("Cannot view in editor: {}", e); - trace_error_exit(&e, 1); + + Ok(Some(&Value::String(ref viewer_template))) => { + let mut handlebars = Handlebars::new(); + handlebars.register_escape_fn(::handlebars::no_escape); + + let _ = handlebars.register_template_string("template", viewer_template) + .map_err_trace_exit(1) + .unwrap(); + + let file = { + let mut tmpfile = tempfile::NamedTempFile::new() + .map_err_trace_exit(1) + .unwrap(); + if view_header { + let hdr = toml::ser::to_string_pretty(entry.get_header()) + .map_err_trace_exit(1) + .unwrap(); + let _ = tmpfile.write(format!("---\n{}---\n", hdr).as_bytes()) + .map_err_trace_exit(1) + .unwrap(); } - } else if s.is_present("view-in-custom") { - warn!("Viewing in custom is currently not supported, switch to stdout"); + + if view_content { + let _ = tmpfile.write(entry.get_content().as_bytes()) + .map_err_trace_exit(1) + .unwrap(); + } + + tmpfile + }; + + let file_path = file + .path() + .to_str() + .map(String::from) + .ok_or::("Cannot build path".to_owned().into()) + .map_err_trace_exit(1).unwrap(); + + let mut command = { + let mut data = BTreeMap::new(); + data.insert("entry", file_path); + + let call = handlebars.render("template", &data).map_err_trace_exit(1).unwrap(); + let mut elems = call.split_whitespace(); + let command_string = elems + .next() + .ok_or::("No command".to_owned().into()) + .map_err_trace_exit(1) + .unwrap(); + let mut cmd = Command::new(command_string); + + for arg in elems { + cmd.arg(arg); + } + + cmd + }; + + if !command.status().map_err_trace_exit(1).unwrap().success() { + exit(1) } }, - }; - - StdoutViewer::new(view_header, view_content).view_entry(&entry) - }; - - if let Err(e) = res { - trace_error_exit(&e, 1); + Ok(Some(_)) => { + error!("Type error: Expected String at {}, found non-string", query); + exit(1) + }, + } + } else { + let _ = StdoutViewer::new(view_header, view_content) + .view_entry(&entry) + .map_err_trace_exit(1); } } diff --git a/bin/core/imag-view/src/ui.rs b/bin/core/imag-view/src/ui.rs index d47973d4..cde06012 100644 --- a/bin/core/imag-view/src/ui.rs +++ b/bin/core/imag-view/src/ui.rs @@ -17,7 +17,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // -use clap::{Arg, App, ArgGroup, SubCommand}; +use clap::{Arg, App, SubCommand}; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app @@ -41,56 +41,12 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .required(false) .help("View content")) - .subcommand(SubCommand::with_name("view-in") - .about("View the entry in ...") - .version("0.1") - - .arg(Arg::with_name("view-in-stdout") - .long("stdout") - .short("s") - .takes_value(false) - .required(false) - .help("View by printing to stdout")) - - .arg(Arg::with_name("view-in-ui") - .long("ui") - .short("u") - .takes_value(false) - .required(false) - .help("View by opening own curses-like UI (default)")) - - .arg(Arg::with_name("view-in-browser") - .long("browser") - .short("b") - .takes_value(true) // optional, which browser - .required(false) - .help("View content in $BROWSER (fails if no env variable $BROWSER)") - .value_name("BROWSER")) - - .arg(Arg::with_name("view-in-texteditor") - .long("in-editor") - .short("e") - .takes_value(false) - .required(false) - .help("View content in $EDITOR (can be passed via --editor)")) - - .arg(Arg::with_name("view-in-custom") - .long("custom") - .short("c") - .takes_value(true) // non-optional, call-string - .required(false) - .help("View content in custom program, for example 'libreoffice %e', replace '%e' with entry path") - .value_name("PROGRAM")) - - .group(ArgGroup::with_name("viewer") - .args(&["view-in-stdout", - "view-in-ui", - "view-in-browser", - "view-in-texteditor", - "view-in-custom", - ]) - .required(false)) - ) + .arg(Arg::with_name("in") + .long("in") + .takes_value(true) + .required(false) + .multiple(false) + .help("View content. If no value is given, this fails. Possible viewers are configured via the config file.")) .subcommand(SubCommand::with_name("compile") .about("Compile content to other format for viewing, implies that the entry gets copied to /tmp") diff --git a/doc/src/09020-changelog.md b/doc/src/09020-changelog.md index fa234ed4..79edabf9 100644 --- a/doc/src/09020-changelog.md +++ b/doc/src/09020-changelog.md @@ -22,6 +22,9 @@ This section contains the changelog from the last release to the next release. * `imag-counter` and `libimagcounter` was removed. * `imag-mv` was introduced * `imag-view` uses positional args now + * `imag-view` uses the configuration file now to find the command to call + for viewing the entry. This way one can view the entry in an editor or the + browser or on the toaster. ## 0.4.0 From 9dcb323c4501cf090b708e6421efbc2cd8702bf2 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 8 Oct 2017 12:56:52 +0200 Subject: [PATCH 2/2] Add example configuration for imag-view --- imagrc.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/imagrc.toml b/imagrc.toml index fdcced6f..85830d51 100644 --- a/imagrc.toml +++ b/imagrc.toml @@ -205,3 +205,8 @@ timed = "minutely" [bookmark] default_collection = "default" +[view.viewers] +# Configure which viewers there are for `imag view in `. +editor = "vim -R {{entry}}" +web = "chromium {{entry}}" +