Merge pull request #1398 from mario-kr/addsh2

Add shell completion via build script (again)
This commit is contained in:
Matthias Beyer 2018-04-14 15:13:27 +02:00 committed by GitHub
commit 90eb83a538
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 197 additions and 59 deletions

View file

@ -42,7 +42,6 @@ extern crate libimagstore;
use std::io::Write; use std::io::Write;
use filters::filter::Filter; use filters::filter::Filter;
use clap::{Arg, App};
use libimagrt::setup::generate_runtime_setup; use libimagrt::setup::generate_runtime_setup;
use libimagerror::trace::MapErrTrace; use libimagerror::trace::MapErrTrace;
@ -50,26 +49,9 @@ use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode; use libimagerror::io::ToExitCode;
use libimagstore::storeid::StoreId; use libimagstore::storeid::StoreId;
mod ui;
use ui::build_ui;
/// No special CLI
pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
app
.arg(Arg::with_name("print-storepath")
.long("with-storepath")
.takes_value(false)
.required(false)
.multiple(false)
.help("Print the storepath for each id"))
.arg(Arg::with_name("in-collection-filter")
.long("in-collection")
.short("c")
.required(false)
.takes_value(true)
.multiple(true)
.value_names(&["COLLECTION"])
.help("Filter for ids which are only in these collections"))
}
pub struct IsInCollectionsFilter<'a, A>(Option<A>, ::std::marker::PhantomData<&'a str>) pub struct IsInCollectionsFilter<'a, A>(Option<A>, ::std::marker::PhantomData<&'a str>)
where A: AsRef<[&'a str]>; where A: AsRef<[&'a str]>;

View file

@ -0,0 +1,40 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> 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 clap::{Arg, App};
pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
app
.arg(Arg::with_name("print-storepath")
.long("with-storepath")
.takes_value(false)
.required(false)
.multiple(false)
.help("Print the storepath for each id"))
.arg(Arg::with_name("in-collection-filter")
.long("in-collection")
.short("c")
.required(false)
.takes_value(true)
.multiple(true)
.value_names(&["COLLECTION"])
.help("Filter for ids which are only in these collections"))
}

View file

@ -50,6 +50,7 @@ use std::process::Command;
use libimagerror::exit::ExitUnwrap; use libimagerror::exit::ExitUnwrap;
use libimagerror::io::ToExitCode; use libimagerror::io::ToExitCode;
use libimagrt::runtime::Runtime;
const CONFIGURATION_STR : &'static str = include_str!("../imagrc.toml"); const CONFIGURATION_STR : &'static str = include_str!("../imagrc.toml");
@ -68,7 +69,10 @@ imagrc.toml
fn main() { fn main() {
let version = make_imag_version!(); let version = make_imag_version!();
let app = ui::build_ui(&version); let app = ui::build_ui(Runtime::get_default_cli_builder(
"imag-init",
version.as_str(),
"Intializes the imag store, optionally with git"));
let matches = app.get_matches(); let matches = app.get_matches();
let mut out = ::std::io::stdout(); let mut out = ::std::io::stdout();

View file

@ -19,12 +19,8 @@
use clap::{Arg, App}; use clap::{Arg, App};
pub fn build_ui<'a>(version: &'a str) -> App<'a, 'a> { pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
App::new("imag-init") app
.version(version)
.author("Matthias Beyer <mail@beyermatthias.de>")
.about("Initialize a ~/.imag repository. Optionally with git")
.arg(Arg::with_name("devel") .arg(Arg::with_name("devel")
.long("dev") .long("dev")
.takes_value(false) .takes_value(false)

View file

@ -13,7 +13,14 @@ documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.h
repository = "https://github.com/matthiasbeyer/imag" repository = "https://github.com/matthiasbeyer/imag"
homepage = "http://imag-pim.org" homepage = "http://imag-pim.org"
build = "../../../build.rs" build = "build.rs"
[build-dependencies]
clap = ">=2.16.1"
version = "2.0"
libimagrt = { path = "../../../lib/core/libimagrt" }
libimagentrytag = { path = "../../../lib/entry/libimagentrytag" }
libimagutil = { path = "../../../lib/etc/libimagutil" }
[badges] [badges]
travis-ci = { repository = "matthiasbeyer/imag" } travis-ci = { repository = "matthiasbeyer/imag" }

138
bin/core/imag/build.rs Normal file
View file

@ -0,0 +1,138 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 Matthias Beyer <mail@beyermatthias.de> 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
//
extern crate clap;
extern crate libimagrt;
extern crate libimagentrytag;
extern crate libimagutil;
#[macro_use] extern crate version;
use clap::Shell;
use libimagrt::runtime::Runtime;
mod toplevelbuildscript {
include!("../../../build.rs");
pub fn build() {
self::main();
}
}
/// This macro generates mods with the given '$modulename',
/// whose content is the file given with `$path`.
/// In this case, It is used specifically to include the
/// `ui.rs` files of the imag binaries.
/// The imag project (accidentally?) followed the convention
/// to write a `ui.rs` containing the function
/// `fn build_ui(app : App) -> App`.
/// This macro allows us to use the same named functions by
/// putting them each into their own module.
macro_rules! gen_mods_buildui {
($(($path:expr, $modulename:ident)$(,)*)*) => (
$(
mod $modulename {
include!($path);
}
)*
)
}
/// This macro reduces boilerplate code.
///
/// For example: `build_subcommand!("counter", imagcounter)`
/// will result in the following code:
/// ```ignore
/// imagcounter::build_ui(Runtime::get_default_cli_builder(
/// "counter",
/// &version!()[..],
/// "counter"))
/// ```
/// As for the `&version!()[..]` part, it does not matter
/// which version the subcommand is getting here, as the
/// output of this script is a completion script, which
/// does not contain information about the version at all.
macro_rules! build_subcommand {
($name:expr, $module:ident) => (
$module::build_ui(Runtime::get_default_cli_builder($name, &version!()[..], $name))
)
}
// Actually generates the module.
gen_mods_buildui!(
("../../../bin/core/imag-annotate/src/ui.rs", imagannotate),
("../../../bin/core/imag-diagnostics/src/ui.rs", imagdiagnostics),
("../../../bin/core/imag-edit/src/ui.rs", imagedit),
("../../../bin/core/imag-gps/src/ui.rs", imaggps),
("../../../bin/core/imag-grep/src/ui.rs", imaggrep),
("../../../bin/core/imag-ids/src/ui.rs", imagids),
("../../../bin/core/imag-init/src/ui.rs", imaginit),
("../../../bin/core/imag-link/src/ui.rs", imaglink),
("../../../bin/core/imag-mv/src/ui.rs", imagmv),
("../../../bin/core/imag-ref/src/ui.rs", imagref),
("../../../bin/core/imag-store/src/ui.rs", imagstore),
("../../../bin/core/imag-tag/src/ui.rs", imagtag),
("../../../bin/core/imag-view/src/ui.rs", imagview)
("../../../bin/domain/imag-bookmark/src/ui.rs", imagbookmark),
("../../../bin/domain/imag-contact/src/ui.rs", imagcontact),
("../../../bin/domain/imag-diary/src/ui.rs", imagdiary),
("../../../bin/domain/imag-habit/src/ui.rs", imaghabit),
("../../../bin/domain/imag-log/src/ui.rs", imaglog),
("../../../bin/domain/imag-mail/src/ui.rs", imagmail),
("../../../bin/domain/imag-notes/src/ui.rs", imagnotes),
("../../../bin/domain/imag-timetrack/src/ui.rs", imagtimetrack),
("../../../bin/domain/imag-todo/src/ui.rs", imagtodo),
);
fn main() {
// Make the `imag`-App...
let mut app = Runtime::get_default_cli_builder(
"imag",
&version!()[..],
"imag")
// and add all the subapps as subcommands.
.subcommand(build_subcommand!("annotate", imagannotate))
.subcommand(build_subcommand!("diagnostics", imagdiagnostics))
.subcommand(build_subcommand!("edit", imagedit))
.subcommand(build_subcommand!("gps", imaggps))
.subcommand(build_subcommand!("grep", imaggrep))
.subcommand(build_subcommand!("ids", imagids))
.subcommand(build_subcommand!("init", imaginit))
.subcommand(build_subcommand!("link", imaglink))
.subcommand(build_subcommand!("mv", imagmv))
.subcommand(build_subcommand!("ref", imagref))
.subcommand(build_subcommand!("store", imagstore))
.subcommand(build_subcommand!("tag", imagtag))
.subcommand(build_subcommand!("view", imagview))
.subcommand(build_subcommand!("bookmark", imagbookmark))
.subcommand(build_subcommand!("contact", imagcontact))
.subcommand(build_subcommand!("diary", imagdiary))
.subcommand(build_subcommand!("habit", imaghabit))
.subcommand(build_subcommand!("log", imaglog))
.subcommand(build_subcommand!("mail", imagmail))
.subcommand(build_subcommand!("notes", imagnotes))
.subcommand(build_subcommand!("timetrack", imagtimetrack))
.subcommand(build_subcommand!("todo", imagtodo));
// Actually generates the completion files
app.gen_completions("imag", Shell::Bash, "../../../target/");
app.gen_completions("imag", Shell::Fish, "../../../target/");
app.gen_completions("imag", Shell::Zsh, "../../../target/");
toplevelbuildscript::build();
}

View file

@ -407,9 +407,6 @@ fn forward_commandline_arguments(m: &ArgMatches, scmd: &mut Vec<String>) {
push(Some("editor"), push(Some("editor"),
Runtime::arg_editor_name(), m , scmd); Runtime::arg_editor_name(), m , scmd);
push(Some("generate-commandline-completion"),
Runtime::arg_generate_compl(), m , scmd);
push(None , Runtime::arg_logdest_name() , m , scmd); push(None , Runtime::arg_logdest_name() , m , scmd);
} }

View file

@ -19,7 +19,7 @@
use std::process::Command; use std::process::Command;
fn main() { fn main() {
let profile = String::from(std::env::var("PROFILE").unwrap()); let profile = String::from(::std::env::var("PROFILE").unwrap());
let git_version = if profile == "debug" { let git_version = if profile == "debug" {
let output = Command::new("git") let output = Command::new("git")
.args(&["describe", "HEAD"]) .args(&["describe", "HEAD"])

View file

@ -111,27 +111,14 @@ impl<'a> Runtime<'a> {
Runtime::_new(cli_app, matches, config) Runtime::_new(cli_app, matches, config)
} }
fn _new<C>(mut cli_app: C, matches: ArgMatches<'a>, config: Option<Value>) fn _new<C>(cli_app: C, matches: ArgMatches<'a>, config: Option<Value>)
-> Result<Runtime<'a>, RuntimeError> -> Result<Runtime<'a>, RuntimeError>
where C: Clone + CliSpec<'a> + InternalConfiguration where C: Clone + CliSpec<'a> + InternalConfiguration
{ {
use std::io::stdout;
use clap::Shell;
if cli_app.enable_logging() { if cli_app.enable_logging() {
Runtime::init_logger(&matches, config.as_ref()) Runtime::init_logger(&matches, config.as_ref())
} }
match matches.value_of(Runtime::arg_generate_compl()) {
Some(shell) => {
debug!("Generating shell completion script, writing to stdout");
let shell = shell.parse::<Shell>().unwrap(); // clap has our back here.
let appname = String::from(cli_app.name());
cli_app.completions(appname, shell, &mut stdout());
},
_ => debug!("Not generating shell completion script"),
}
let rtp = get_rtp_match(&matches); let rtp = get_rtp_match(&matches);
let storepath = matches.value_of(Runtime::arg_storepath_name()) let storepath = matches.value_of(Runtime::arg_storepath_name())
@ -242,14 +229,6 @@ impl<'a> Runtime<'a> {
.required(false) .required(false)
.takes_value(true)) .takes_value(true))
.arg(Arg::with_name(Runtime::arg_generate_compl())
.long("generate-commandline-completion")
.help("Generate the commandline completion for bash or zsh or fish")
.required(false)
.takes_value(true)
.value_name("SHELL")
.possible_values(&["bash", "fish", "zsh"]))
.arg(Arg::with_name(Runtime::arg_logdest_name()) .arg(Arg::with_name(Runtime::arg_logdest_name())
.long(Runtime::arg_logdest_name()) .long(Runtime::arg_logdest_name())
.help("Override the logging destinations from the configuration: values can be seperated by ',', a value of '-' marks the stderr output, everything else is expected to be a path") .help("Override the logging destinations from the configuration: values can be seperated by ',', a value of '-' marks the stderr output, everything else is expected to be a path")
@ -320,11 +299,6 @@ impl<'a> Runtime<'a> {
"editor" "editor"
} }
/// Get the argument name for generating the completion
pub fn arg_generate_compl() -> &'static str {
"generate-completion"
}
/// Extract the Store object from the Runtime object, destroying the Runtime object /// Extract the Store object from the Runtime object, destroying the Runtime object
/// ///
/// # Warning /// # Warning