From d12844aff04b4b78e5aba3e79f0ba79da7326b4f Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 24 Apr 2018 14:38:41 +0200 Subject: [PATCH 1/2] Add markdown viewer functionality --- lib/entry/libimagentryview/Cargo.toml | 20 +++++ lib/entry/libimagentryview/src/builtin/md.rs | 73 +++++++++++++++++++ lib/entry/libimagentryview/src/builtin/mod.rs | 4 + lib/entry/libimagentryview/src/error.rs | 1 + lib/entry/libimagentryview/src/lib.rs | 12 +++ 5 files changed, 110 insertions(+) create mode 100644 lib/entry/libimagentryview/src/builtin/md.rs diff --git a/lib/entry/libimagentryview/Cargo.toml b/lib/entry/libimagentryview/Cargo.toml index 4c702043..b10dd040 100644 --- a/lib/entry/libimagentryview/Cargo.toml +++ b/lib/entry/libimagentryview/Cargo.toml @@ -29,3 +29,23 @@ libimagrt = { version = "0.8.0", path = "../../../lib/core/libimagrt" } libimagstore = { version = "0.8.0", path = "../../../lib/core/libimagstore" } libimagerror = { version = "0.8.0", path = "../../../lib/core/libimagerror" } libimagentryedit = { version = "0.8.0", path = "../../../lib/entry/libimagentryedit" } + +mdcat = { version = "0.8", optional = true } +failure = { version = "0.1", optional = true } + +[dependencies.pulldown-cmark] +version = "^0.1" +optional = true +default-features = false +features = [] + +[dependencies.syntect] +version = "^2" +optional = true +default-features = false +features = ["parsing", "assets", "dump-load"] + +[features] +default = [ "markdownviewer" ] +markdownviewer = ["mdcat", "failure", "pulldown-cmark", "syntect"] + diff --git a/lib/entry/libimagentryview/src/builtin/md.rs b/lib/entry/libimagentryview/src/builtin/md.rs new file mode 100644 index 00000000..630719e3 --- /dev/null +++ b/lib/entry/libimagentryview/src/builtin/md.rs @@ -0,0 +1,73 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015-2018 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 std::io::Write; + +use libimagstore::store::Entry; +use libimagrt::runtime::Runtime; + +use mdcat::{ResourceAccess, Terminal, TerminalSize}; +use pulldown_cmark::Parser; +use syntect::parsing::SyntaxSet; +use mdcat; + +use viewer::Viewer; +use error::Result; + +pub struct MarkdownViewer<'a> { + rt: &'a Runtime<'a>, + resource_access: ResourceAccess, + terminal: Terminal, + termsize: TerminalSize, +} + +impl<'a> MarkdownViewer<'a> { + pub fn new(rt: &'a Runtime) -> Self { + MarkdownViewer { + rt, + resource_access: ResourceAccess::LocalOnly, + terminal: Terminal::detect(), + termsize: TerminalSize::detect().unwrap_or(TerminalSize { + width: 80, + height: 20, + }), + } + } +} + +impl<'a> Viewer for MarkdownViewer<'a> { + fn view_entry(&self, e: &Entry, sink: &mut W) -> Result<()> + where W: Write + { + let parser = Parser::new(e.get_content()); + let base_dir = self.rt.rtp(); + let syntax_set = SyntaxSet::load_defaults_newlines(); + + mdcat::push_tty(sink, + self.terminal.clone(), + self.termsize.clone(), + parser, + base_dir, + self.resource_access.clone(), + syntax_set) + .map_err(|e| e.compat()) + .map_err(::error::ViewError::from) + } +} + diff --git a/lib/entry/libimagentryview/src/builtin/mod.rs b/lib/entry/libimagentryview/src/builtin/mod.rs index a859f8bf..bf9a5b6e 100644 --- a/lib/entry/libimagentryview/src/builtin/mod.rs +++ b/lib/entry/libimagentryview/src/builtin/mod.rs @@ -20,3 +20,7 @@ pub mod editor; pub mod plain; pub mod stdout; + +#[cfg(feature = "markdownviewer")] +pub mod md; + diff --git a/lib/entry/libimagentryview/src/error.rs b/lib/entry/libimagentryview/src/error.rs index e910fd87..c549fe88 100644 --- a/lib/entry/libimagentryview/src/error.rs +++ b/lib/entry/libimagentryview/src/error.rs @@ -23,6 +23,7 @@ error_chain! { } foreign_links { + Failure(::failure::Compat<::failure::Error>) #[cfg(feature = "markdownviewer")]; IO(::std::io::Error); } diff --git a/lib/entry/libimagentryview/src/lib.rs b/lib/entry/libimagentryview/src/lib.rs index 170f415c..52f30a0f 100644 --- a/lib/entry/libimagentryview/src/lib.rs +++ b/lib/entry/libimagentryview/src/lib.rs @@ -39,6 +39,18 @@ extern crate toml; #[macro_use] extern crate error_chain; extern crate textwrap; +#[cfg(feature = "markdownviewer")] +extern crate mdcat; + +#[cfg(feature = "markdownviewer")] +extern crate failure; + +#[cfg(feature = "markdownviewer")] +extern crate pulldown_cmark; + +#[cfg(feature = "markdownviewer")] +extern crate syntect; + extern crate libimagstore; extern crate libimagrt; extern crate libimagerror; From c2fd3fa94efaa1879e560fa17fe01a1be78ca8c1 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 24 Apr 2018 15:23:32 +0200 Subject: [PATCH 2/2] Add support for formatted markdown output This patch also removes the (unused) "compile" subcommand and replaces it with a flag "-c" / "--compile" which prints the output in formatted to the terminal. Compiling the output from markdown to HTML and showing in the browser is not yet possible. --- bin/core/imag-view/Cargo.toml | 5 +++- bin/core/imag-view/src/main.rs | 51 ++++++++++++++++++++-------------- bin/core/imag-view/src/ui.rs | 30 +++++++------------- 3 files changed, 44 insertions(+), 42 deletions(-) diff --git a/bin/core/imag-view/Cargo.toml b/bin/core/imag-view/Cargo.toml index 3e4c5125..ffd5706b 100644 --- a/bin/core/imag-view/Cargo.toml +++ b/bin/core/imag-view/Cargo.toml @@ -31,9 +31,12 @@ tempfile = "2.1" libimagstore = { version = "0.8.0", path = "../../../lib/core/libimagstore" } libimagrt = { version = "0.8.0", path = "../../../lib/core/libimagrt" } libimagerror = { version = "0.8.0", path = "../../../lib/core/libimagerror" } -libimagentryview = { version = "0.8.0", path = "../../../lib/entry/libimagentryview" } libimagutil = { version = "0.8.0", path = "../../../lib/etc/libimagutil" } +[dependencies.libimagentryview] +version = "0.8.0" +path = "../../../lib/entry/libimagentryview" + [dependencies.clap] version = "^2.29" default-features = false diff --git a/bin/core/imag-view/src/main.rs b/bin/core/imag-view/src/main.rs index 67fc6a07..bc9e83b4 100644 --- a/bin/core/imag-view/src/main.rs +++ b/bin/core/imag-view/src/main.rs @@ -62,6 +62,7 @@ use libimagerror::str::ErrFromStr; use libimagerror::trace::MapErrTrace; use libimagerror::iter::TraceIterator; use libimagentryview::builtin::stdout::StdoutViewer; +use libimagentryview::builtin::md::MarkdownViewer; use libimagentryview::viewer::Viewer; use libimagentryview::error::ViewError as VE; use libimagstore::storeid::IntoStoreId; @@ -171,24 +172,7 @@ fn main() { drop(files); } else { - let mut viewer = StdoutViewer::new(view_header, !hide_content); - - if rt.cli().occurrences_of("autowrap") != 0 { - let width = rt.cli().value_of("autowrap").unwrap(); // ensured by clap - let width = usize::from_str(width).unwrap_or_else(|e| { - error!("Failed to parse argument to number: autowrap = {:?}", - rt.cli().value_of("autowrap").map(String::from)); - error!("-> {:?}", e); - ::std::process::exit(1) - }); - - viewer.wrap_at(width); - } - - let output = rt.stdout(); - let mut lockout = output.lock(); - - entry_ids + let iter = entry_ids .into_iter() .into_get_iter(rt.store()) .map(|e| { @@ -196,10 +180,35 @@ fn main() { .ok_or_else(|| String::from("Entry not found")) .map_err(StoreError::from) .map_err_trace_exit_unwrap(1) - }) - .for_each(|e| { - viewer.view_entry(&e, &mut lockout).map_err_trace_exit_unwrap(1); }); + + let out = rt.stdout(); + let mut outlock = out.lock(); + + if rt.cli().is_present("compile-md") { + let viewer = MarkdownViewer::new(&rt); + for entry in iter { + viewer.view_entry(&entry, &mut outlock).map_err_trace_exit_unwrap(1); + } + } else { + let mut viewer = StdoutViewer::new(view_header, !hide_content); + + if rt.cli().occurrences_of("autowrap") != 0 { + let width = rt.cli().value_of("autowrap").unwrap(); // ensured by clap + let width = usize::from_str(width).unwrap_or_else(|e| { + error!("Failed to parse argument to number: autowrap = {:?}", + rt.cli().value_of("autowrap").map(String::from)); + error!("-> {:?}", e); + ::std::process::exit(1) + }); + + viewer.wrap_at(width); + } + + for entry in iter { + viewer.view_entry(&entry, &mut outlock).map_err_trace_exit_unwrap(1); + } + } } } diff --git a/bin/core/imag-view/src/ui.rs b/bin/core/imag-view/src/ui.rs index 7dbc9696..157a701f 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, ArgGroup, App, SubCommand}; +use clap::{Arg, ArgGroup, App}; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { app @@ -64,6 +64,15 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .required(false) .help("Do not view content")) + .arg(Arg::with_name("compile-md") + .long("compile") + .short("c") + .takes_value(false) + .required(false) + .help("Do compile markdown to be nice") + .conflicts_with("not-view-content") + .conflicts_with("autowrap")) // markdown viewer does not support wrapping + .arg(Arg::with_name("in") .long("in") .takes_value(true) @@ -71,23 +80,4 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .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") - .version("0.1") - .arg(Arg::with_name("from") - .long("from") - .short("f") - .takes_value(true) // "markdown" or "textile" or "restructuredtex" - .required(true) - .help("Compile from") - .value_name("FORMAT")) - - .arg(Arg::with_name("to") - .long("to") - .short("t") - .takes_value(true) // "html" or "HTML" or ... json maybe? - .required(true) - .help("Compile to") - .value_name("FORMAT")) - ) }