refactor: update clap to v3 (#3370)

This commit is contained in:
David Knaack 2022-01-04 10:49:42 +01:00 committed by GitHub
parent 35eae3fb4a
commit 20cf200c3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 394 additions and 401 deletions

116
Cargo.lock generated
View File

@ -218,17 +218,42 @@ dependencies = [
[[package]]
name = "clap"
version = "2.34.0"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
checksum = "d17bf219fcd37199b9a29e00ba65dfb8cd5b2688b7297ec14ff829c40ac50ca9"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim 0.8.0",
"clap_derive",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
"unicode-width",
"vec_map",
"unicase",
]
[[package]]
name = "clap_complete"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60d123fbea4c5d9799cffd44051e2125c880efd23b3b7c529baf3ea5508c8736"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1b9752c030a14235a0bd5ef3ad60a1dcac8468c30921327fc8af36b20c790b9"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
@ -1191,6 +1216,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "parking"
version = "2.0.0"
@ -1344,6 +1378,30 @@ dependencies = [
"toml",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
@ -1719,6 +1777,7 @@ dependencies = [
"byte-unit",
"chrono",
"clap",
"clap_complete",
"directories-next",
"gethostname",
"git2",
@ -1748,7 +1807,7 @@ dependencies = [
"shell-words",
"starship-battery",
"starship_module_config_derive",
"strsim 0.10.0",
"strsim",
"sys-info",
"tempfile",
"terminal_size",
@ -1795,12 +1854,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strsim"
version = "0.10.0"
@ -1863,6 +1916,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
@ -1875,9 +1937,9 @@ dependencies = [
[[package]]
name = "textwrap"
version = "0.11.0"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
dependencies = [
"unicode-width",
]
@ -1968,6 +2030,15 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.6"
@ -2041,12 +2112,6 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.3"
@ -2129,6 +2194,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View File

@ -30,7 +30,8 @@ battery = ["starship-battery"]
tls-vendored = ["native-tls/vendored"]
[dependencies]
clap = "2.34.0"
clap = { version = "3.0.0", features = ["derive", "cargo", "unicode"] }
clap_complete = "3.0.0"
ansi_term = "0.12.1"
directories-next = "2.0.0"
git2 = { version = "0.13.25", default-features = false }

View File

@ -73,7 +73,7 @@ fn handle_update_configuration(doc: &mut Document, name: &str, value: &str) -> R
Ok(())
}
pub fn print_configuration(use_default: bool, paths: &[&str]) {
pub fn print_configuration(use_default: bool, paths: &[String]) {
let config = if use_default {
// Get default config
let default_config = crate::configs::FullConfig::default();
@ -94,7 +94,7 @@ pub fn print_configuration(use_default: bool, paths: &[&str]) {
if paths.is_empty()
|| paths
.iter()
.any(|&path| path == "format" || path == "right_format")
.any(|path| path == "format" || path == "right_format")
{
println!(
"# $all is shorthand for {}",
@ -128,7 +128,7 @@ pub fn print_configuration(use_default: bool, paths: &[&str]) {
println!("{}", string_config);
}
fn extract_toml_paths(mut config: toml::Value, paths: &[&str]) -> toml::Value {
fn extract_toml_paths(mut config: toml::Value, paths: &[String]) -> toml::Value {
// Extract all the requested sections into a new configuration.
let mut subset = toml::value::Table::new();
let config = if let Some(config) = config.as_table_mut() {
@ -138,7 +138,7 @@ fn extract_toml_paths(mut config: toml::Value, paths: &[&str]) -> toml::Value {
return toml::Value::Table(subset);
};
'paths: for &path in paths {
'paths: for path in paths {
let path_segments: Vec<_> = path.split('.').collect();
let (&end, parents) = path_segments.split_last().unwrap_or((&"", &[]));
@ -414,9 +414,9 @@ mod tests {
let actual_config = extract_toml_paths(
config,
&[
"extract_root",
"extract_section",
"extract_subsection.extracted",
"extract_root".to_owned(),
"extract_section".to_owned(),
"extract_subsection.extracted".to_owned(),
],
);

View File

@ -5,14 +5,17 @@ use crate::utils::{create_command, exec_timeout, CommandOutput};
use crate::modules;
use crate::utils::{self, home_dir};
use clap::ArgMatches;
use clap::Parser;
use git2::{ErrorCode::UnbornBranch, Repository, RepositoryState};
use once_cell::sync::OnceCell;
use std::collections::{HashMap, HashSet};
#[cfg(test)]
use std::collections::HashMap;
use std::collections::HashSet;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt::Debug;
use std::fs;
use std::num::ParseIntError;
use std::path::{Path, PathBuf};
use std::string::String;
use std::time::{Duration, Instant};
@ -37,7 +40,7 @@ pub struct Context<'a> {
dir_contents: OnceCell<DirContents>,
/// Properties to provide to modules.
pub properties: HashMap<&'a str, String>,
pub properties: Properties,
/// Pipestatus of processes in pipe
pub pipestatus: Option<Vec<String>>,
@ -48,11 +51,8 @@ pub struct Context<'a> {
/// The shell the user is assumed to be running
pub shell: Shell,
/// Construct the right prompt instead of the left prompt
pub right: bool,
/// Construct the continuation prompt instead of the normal prompt
pub continuation: bool,
/// Which prompt to print (main, right, ...)
pub target: Target,
/// Width of terminal, or zero if width cannot be detected.
pub width: usize,
@ -76,53 +76,46 @@ impl<'a> Context<'a> {
/// Identify the current working directory and create an instance of Context
/// for it. "logical-path" is used when a shell allows the "current working directory"
/// to be something other than a file system path (like powershell provider specific paths).
pub fn new(arguments: ArgMatches) -> Context {
pub fn new(arguments: Properties, target: Target) -> Context<'a> {
let shell = Context::get_shell();
// Retrieve the "current directory".
// If the path argument is not set fall back to the OS current directory.
let path = arguments
.value_of("path")
.map(PathBuf::from)
.path
.clone()
.or_else(|| env::current_dir().ok())
.or_else(|| env::var("PWD").map(PathBuf::from).ok())
.or_else(|| arguments.value_of("logical_path").map(PathBuf::from))
.or_else(|| arguments.logical_path.clone())
.unwrap_or_default();
// Retrieve the "logical directory".
// If the path argument is not set fall back to the PWD env variable set by many shells
// or to the other path.
let logical_path = arguments
.value_of("logical_path")
.map(PathBuf::from)
.logical_path
.clone()
.or_else(|| env::var("PWD").map(PathBuf::from).ok())
.unwrap_or_else(|| path.clone());
Context::new_with_shell_and_path(arguments, shell, path, logical_path)
Context::new_with_shell_and_path(arguments, shell, target, path, logical_path)
}
/// Create a new instance of Context for the provided directory
pub fn new_with_shell_and_path(
arguments: ArgMatches,
properties: Properties,
shell: Shell,
target: Target,
path: PathBuf,
logical_path: PathBuf,
) -> Context {
) -> Context<'a> {
let config = StarshipConfig::initialize();
// Unwrap the clap arguments into a simple hashtable
let properties: HashMap<&str, std::string::String> = arguments
.args
.iter()
.filter(|(_, v)| !v.vals.is_empty())
.map(|(a, b)| (*a, b.vals.first().cloned().unwrap().into_string().unwrap()))
.collect();
let pipestatus = arguments
.values_of("pipestatus")
let pipestatus = properties
.pipestatus
.as_deref()
.map(Context::get_and_flatten_pipestatus)
.flatten();
log::trace!("Received completed pipestatus of {:?}", pipestatus);
// Canonicalize the current path to resolve symlinks, etc.
@ -136,14 +129,7 @@ impl<'a> Context<'a> {
.as_ref()
.map_or_else(StarshipRootConfig::default, StarshipRootConfig::load);
let right = arguments.is_present("right");
let continuation = arguments.is_present("continuation");
let width = arguments
.value_of("terminal_width")
.and_then(|w| w.parse().ok())
.or_else(|| terminal_size().map(|(w, _)| w.0 as usize))
.unwrap_or(80);
let width = properties.terminal_width;
Context {
config,
@ -154,8 +140,7 @@ impl<'a> Context<'a> {
dir_contents: OnceCell::new(),
repo: OnceCell::new(),
shell,
right,
continuation,
target,
width,
#[cfg(test)]
env: HashMap::new(),
@ -212,12 +197,13 @@ impl<'a> Context<'a> {
}
/// Reads and appropriately flattens multiple args for pipestatus
pub fn get_and_flatten_pipestatus(args: clap::Values) -> Option<Vec<String>> {
// TODO: Replace with value_delimiter = ' ' clap option?
pub fn get_and_flatten_pipestatus(args: &[String]) -> Option<Vec<String>> {
// Due to shell differences, we can potentially receive individual or space
// separated inputs, e.g. "0","1","2","0" is the same as "0 1 2 0" and
// "0 1", "2 0". We need to accept all these formats and return a Vec<String>
let parsed_vals = args
.into_iter()
.iter()
.map(|x| x.split_ascii_whitespace())
.flatten()
.map(|x| x.to_string())
@ -310,8 +296,12 @@ impl<'a> Context<'a> {
}
}
// TODO: This should be used directly by clap parse
pub fn get_cmd_duration(&self) -> Option<u128> {
self.properties.get("cmd_duration")?.parse::<u128>().ok()
self.properties
.cmd_duration
.as_deref()
.and_then(|cd| cd.parse::<u128>().ok())
}
/// Execute a command and return the output on stdout and stderr if successful
@ -574,6 +564,67 @@ pub enum Shell {
Unknown,
}
fn default_width() -> usize {
terminal_size().map_or(80, |(w, _)| w.0 as usize)
}
/// Which kind of prompt target to print (main prompt, rprompt, ...)
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Target {
Main,
Right,
Continuation,
}
/// Properties as passed on from the shell as arguments
#[derive(Parser, Debug)]
pub struct Properties {
/// The status code of the previously run command
#[clap(short = 's', long = "status")]
pub status_code: Option<i32>,
/// Bash and Zsh support returning codes for each process in a pipeline.
#[clap(long)]
pipestatus: Option<Vec<String>>,
/// The width of the current interactive terminal.
#[clap(short = 'w', long, default_value_t=default_width())]
terminal_width: usize,
/// The path that the prompt should render for.
#[clap(short, long)]
path: Option<PathBuf>,
/// The logical path that the prompt should render for.
/// This path should be a virtual/logical representation of the PATH argument.
#[clap(short = 'P', long)]
logical_path: Option<PathBuf>,
/// The execution duration of the last command, in milliseconds
#[clap(short = 'd', long)]
pub cmd_duration: Option<String>,
/// The keymap of fish/zsh
#[clap(short = 'k', long, default_value = "viins")]
pub keymap: String,
/// The number of currently running jobs
#[clap(short, long, default_value_t, parse(try_from_str=parse_jobs))]
pub jobs: i64,
}
impl Default for Properties {
fn default() -> Self {
Properties {
status_code: None,
pipestatus: None,
terminal_width: default_width(),
path: None,
logical_path: None,
cmd_duration: None,
keymap: "viins".to_string(),
jobs: 0,
}
}
}
fn parse_jobs(jobs: &str) -> Result<i64, ParseIntError> {
jobs.trim().parse::<i64>()
}
#[cfg(test)]
mod tests {
use super::*;
@ -660,8 +711,9 @@ mod tests {
// Mock navigation into the symlink path
let test_path = path_symlink.join("yyy");
let context = Context::new_with_shell_and_path(
ArgMatches::default(),
Default::default(),
Shell::Unknown,
Target::Main,
test_path.clone(),
test_path.clone(),
);
@ -685,8 +737,9 @@ mod tests {
// Mock navigation to a directory which does not exist on disk
let test_path = Path::new("/path_which_does_not_exist").to_path_buf();
let context = Context::new_with_shell_and_path(
ArgMatches::default(),
Default::default(),
Shell::Unknown,
Target::Main,
test_path.clone(),
test_path.clone(),
);
@ -705,8 +758,9 @@ mod tests {
// Mock navigation to a directory which does not exist on disk
let test_path = Path::new("~/path_which_does_not_exist").to_path_buf();
let context = Context::new_with_shell_and_path(
ArgMatches::default(),
Default::default(),
Shell::Unknown,
Target::Main,
test_path.clone(),
test_path.clone(),
);

View File

@ -4,297 +4,165 @@ use clap::crate_authors;
use std::io;
use std::time::SystemTime;
use clap::{App, AppSettings, Arg, Shell, SubCommand};
use clap::{AppSettings, IntoApp, Parser, Subcommand};
use clap_complete::{generate, Shell as CompletionShell};
use rand::distributions::Alphanumeric;
use rand::Rng;
use starship::context::{Properties, Target};
use starship::module::ALL_MODULES;
use starship::*;
fn long_version() -> &'static str {
let ver = Box::new(crate::shadow::clap_version());
Box::leak(ver).as_str()
}
#[derive(Parser, Debug)]
#[clap(
author=crate_authors!(),
version=shadow::PKG_VERSION,
long_version=long_version(),
about="The cross-shell prompt for astronauts. ☄🌌️"
)]
#[clap(setting(AppSettings::SubcommandRequiredElseHelp))]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
/// Create a pre-populated GitHub issue with information about your configuration
BugReport,
/// Generate starship shell completions for your shell to stdout
Completions {
#[clap(arg_enum)]
shell: CompletionShell,
},
/// Edit the starship configuration
Config {
/// Configuration key to edit
#[clap(requires = "value")]
name: Option<String>,
/// Value to place into that key
value: Option<String>,
},
/// Explains the currently showing modules
Explain(Properties),
/// Prints the shell function used to execute starship
Init {
shell: String,
#[clap(long)]
print_full_init: bool,
},
/// Prints a specific prompt module
Module {
/// The name of the module to be printed
#[clap(required = true, required_unless_present = "list")]
name: Option<String>,
/// List out all supported modules
#[clap(short, long)]
list: bool,
#[clap(flatten)]
properties: Properties,
},
/// Prints the computed starship configuration
PrintConfig {
/// Print the default instead of the computed config
#[clap(short, long)]
default: bool,
/// Configuration keys to print
name: Vec<String>,
},
/// Prints the full starship prompt
Prompt {
/// Print the right prompt (instead of the standard left prompt)
#[clap(long)]
right: bool,
/// Print the continuation prompt (instead of the standard left prompt)
#[clap(long, conflicts_with = "right")]
continuation: bool,
#[clap(flatten)]
properties: Properties,
},
/// Generate random session key
Session,
/// Prints time in milliseconds
#[clap(setting=AppSettings::Hidden)]
Time,
/// Prints timings of all active modules
Timings(Properties),
/// Toggle a given starship module
Toggle {
/// The name of the module to be toggled
name: String,
/// The key of the config to be toggled
#[clap(default_value = "disabled")]
value: String,
},
}
fn main() {
// Configure the current terminal on windows to support ANSI escape sequences.
#[cfg(windows)]
let _ = ansi_term::enable_ansi_support();
logger::init();
let status_code_arg = Arg::with_name("status_code")
.short("s")
.long("status")
.value_name("STATUS_CODE")
.help("The status code of the previously run command")
.takes_value(true);
let args = Cli::parse();
log::trace!("Parsed arguments: {:#?}", args);
let pipestatus_arg = Arg::with_name("pipestatus")
.long("pipestatus")
.value_name("PIPESTATUS")
.help("Status codes from a command pipeline")
.long_help("Bash and Zsh supports returning codes for each process in a pipeline.")
.multiple(true);
let terminal_width_arg = Arg::with_name("terminal_width")
.short("w")
.long("terminal-width")
.value_name("TERMINAL_WIDTH")
.help("The width of the current interactive terminal.")
.takes_value(true);
let path_arg = Arg::with_name("path")
.short("p")
.long("path")
.value_name("PATH")
.help("The path that the prompt should render for.")
.takes_value(true);
let logical_path_arg = Arg::with_name("logical_path")
.short("P")
.long("logical-path")
.value_name("LOGICAL_PATH")
.help(concat!(
"The logical path that the prompt should render for. ",
"This path should be a virtual/logical representation of the PATH argument."
))
.takes_value(true);
let shell_arg = Arg::with_name("shell")
.value_name("SHELL")
.help(
"The name of the currently running shell\nCurrently supported options: bash, zsh, fish, powershell, ion, elvish, tcsh, nu, xonsh",
)
.required(true);
let cmd_duration_arg = Arg::with_name("cmd_duration")
.short("d")
.long("cmd-duration")
.value_name("CMD_DURATION")
.help("The execution duration of the last command, in milliseconds")
.takes_value(true);
let keymap_arg = Arg::with_name("keymap")
.short("k")
.long("keymap")
.value_name("KEYMAP")
// fish/zsh only
.help("The keymap of fish/zsh")
.takes_value(true);
let jobs_arg = Arg::with_name("jobs")
.short("j")
.long("jobs")
.value_name("JOBS")
.help("The number of currently running jobs")
.takes_value(true);
let init_scripts_arg = Arg::with_name("print_full_init")
.long("print-full-init")
.help("Print the main initialization script (as opposed to the init stub)");
let long_version = crate::shadow::clap_version();
let mut app = App::new("starship")
.about("The cross-shell prompt for astronauts. ☄🌌️")
// pull the version number from Cargo.toml
.version(shadow::PKG_VERSION)
.long_version(long_version.as_str())
// pull the authors from Cargo.toml
.author(crate_authors!())
.after_help("https://github.com/starship/starship")
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("init")
.about("Prints the shell function used to execute starship")
.arg(&shell_arg)
.arg(&init_scripts_arg),
)
.subcommand(
SubCommand::with_name("prompt")
.about("Prints the full starship prompt")
.arg(
Arg::with_name("right")
.long("right")
.help("Print the right prompt (instead of the standard left prompt)"),
)
.arg(
Arg::with_name("continuation")
.long("continuation")
.help("Print the continuation prompt (instead of the standard left prompt)")
.conflicts_with("right"),
)
.arg(&status_code_arg)
.arg(&pipestatus_arg)
.arg(&terminal_width_arg)
.arg(&path_arg)
.arg(&logical_path_arg)
.arg(&cmd_duration_arg)
.arg(&keymap_arg)
.arg(&jobs_arg),
)
.subcommand(
SubCommand::with_name("module")
.about("Prints a specific prompt module")
.arg(
Arg::with_name("name")
.help("The name of the module to be printed")
.required(true)
.required_unless("list"),
)
.arg(
Arg::with_name("list")
.short("l")
.long("list")
.help("List out all supported modules"),
)
.arg(&status_code_arg)
.arg(&pipestatus_arg)
.arg(&terminal_width_arg)
.arg(&path_arg)
.arg(&logical_path_arg)
.arg(&cmd_duration_arg)
.arg(&keymap_arg)
.arg(&jobs_arg),
)
.subcommand(
SubCommand::with_name("config")
.alias("configure")
.about("Edit the starship configuration")
.arg(
Arg::with_name("name")
.help("Configuration key to edit")
.required(false)
.requires("value"),
)
.arg(Arg::with_name("value").help("Value to place into that key")),
)
.subcommand(
SubCommand::with_name("print-config")
.about("Prints the computed starship configuration")
.arg(
Arg::with_name("default")
.short("d")
.long("default")
.help("Print the default instead of the computed config")
.takes_value(false),
)
.arg(
Arg::with_name("name")
.help("Configuration keys to print")
.multiple(true)
.required(false),
),
)
.subcommand(
SubCommand::with_name("toggle")
.about("Toggle a given starship module")
.arg(
Arg::with_name("name")
.help("The name of the module to be toggled")
.required(true),
)
.arg(
Arg::with_name("key")
.help("The key of the config to be toggled")
.required(false)
.required_unless("name"),
),
)
.subcommand(
SubCommand::with_name("bug-report").about(
"Create a pre-populated GitHub issue with information about your configuration",
),
)
.subcommand(
SubCommand::with_name("time")
.about("Prints time in milliseconds")
.settings(&[AppSettings::Hidden]),
)
.subcommand(
SubCommand::with_name("explain")
.about("Explains the currently showing modules")
.arg(&status_code_arg)
.arg(&pipestatus_arg)
.arg(&terminal_width_arg)
.arg(&path_arg)
.arg(&logical_path_arg)
.arg(&cmd_duration_arg)
.arg(&keymap_arg)
.arg(&jobs_arg),
)
.subcommand(
SubCommand::with_name("timings")
.about("Prints timings of all active modules")
.arg(&status_code_arg)
.arg(&pipestatus_arg)
.arg(&terminal_width_arg)
.arg(&path_arg)
.arg(&logical_path_arg)
.arg(&cmd_duration_arg)
.arg(&keymap_arg)
.arg(&jobs_arg),
)
.subcommand(
SubCommand::with_name("completions")
.about("Generate starship shell completions for your shell to stdout")
.arg(
Arg::with_name("shell")
.takes_value(true)
.possible_values(&Shell::variants())
.help("the shell to generate completions for")
.value_name("SHELL")
.required(true)
.env("STARSHIP_SHELL"),
),
)
.subcommand(SubCommand::with_name("session").about("Generate random session key"));
let matches = app.clone().get_matches();
match matches.subcommand() {
("init", Some(sub_m)) => {
let shell_name = sub_m.value_of("shell").expect("Shell name missing.");
if sub_m.is_present("print_full_init") {
init::init_main(shell_name).expect("can't init_main");
match args.command {
Commands::Init {
shell,
print_full_init,
} => {
if print_full_init {
init::init_main(&shell).expect("can't init_main");
} else {
init::init_stub(shell_name).expect("can't init_stub");
init::init_stub(&shell).expect("can't init_stub");
}
}
("prompt", Some(sub_m)) => print::prompt(sub_m.clone()),
("module", Some(sub_m)) => {
if sub_m.is_present("list") {
Commands::Prompt {
properties,
right,
continuation,
} => {
let target = match (right, continuation) {
(true, _) => Target::Right,
(_, true) => Target::Continuation,
(_, _) => Target::Main,
};
print::prompt(properties, target)
}
Commands::Module {
name,
list,
properties,
} => {
if list {
println!("Supported modules list");
println!("----------------------");
for modules in ALL_MODULES {
println!("{}", modules);
}
}
if let Some(module_name) = sub_m.value_of("name") {
print::module(module_name, sub_m.clone());
if let Some(module_name) = name {
print::module(&module_name, properties);
}
}
("config", Some(sub_m)) => {
if let Some(name) = sub_m.value_of("name") {
if let Some(value) = sub_m.value_of("value") {
configure::update_configuration(name, value)
Commands::Config { name, value } => {
if let Some(name) = name {
if let Some(value) = value {
configure::update_configuration(&name, &value)
}
} else {
configure::edit_configuration()
}
}
("print-config", Some(sub_m)) => {
let print_default = sub_m.is_present("default");
let paths = sub_m
.values_of("name")
.map(|paths| paths.collect::<Vec<&str>>())
.unwrap_or_default();
configure::print_configuration(print_default, &paths)
}
("toggle", Some(sub_m)) => {
if let Some(name) = sub_m.value_of("name") {
if let Some(value) = sub_m.value_of("key") {
configure::toggle_configuration(name, value)
} else {
configure::toggle_configuration(name, "disabled")
}
}
}
("bug-report", Some(_)) => bug_report::create(),
("time", _) => {
Commands::PrintConfig { default, name } => configure::print_configuration(default, &name),
Commands::Toggle { name, value } => configure::toggle_configuration(&name, &value),
Commands::BugReport => bug_report::create(),
Commands::Time => {
match SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
@ -303,18 +171,15 @@ fn main() {
None => println!("{}", -1),
}
}
("explain", Some(sub_m)) => print::explain(sub_m.clone()),
("timings", Some(sub_m)) => print::timings(sub_m.clone()),
("completions", Some(sub_m)) => {
let shell: Shell = sub_m
.value_of("shell")
.expect("Shell name missing.")
.parse()
.expect("Invalid shell");
app.gen_completions_to("starship", shell, &mut io::stdout().lock());
}
("session", _) => println!(
Commands::Explain(props) => print::explain(props),
Commands::Timings(props) => print::timings(props),
Commands::Completions { shell } => generate(
shell,
&mut Cli::into_app(),
"starship",
&mut io::stdout().lock(),
),
Commands::Session => println!(
"{}",
rand::thread_rng()
.sample_iter(&Alphanumeric)
@ -322,6 +187,5 @@ fn main() {
.map(char::from)
.collect::<String>()
),
(command, _) => unreachable!("Invalid subcommand: {}", command),
}
}

View File

@ -22,9 +22,9 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let config: CharacterConfig = CharacterConfig::try_load(module.config);
let props = &context.properties;
let exit_code = props.get("status_code").map_or("0", String::as_str);
let keymap = props.get("keymap").map_or("viins", String::as_str);
let exit_success = exit_code == "0";
let exit_code = props.status_code;
let keymap = props.keymap.as_str();
let exit_success = exit_code.unwrap_or_default() == 0;
// Match shell "keymap" names to normalized vi modes
// NOTE: in vi mode, fish reports normal mode as "default".

View File

@ -33,12 +33,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}
let props = &context.properties;
let num_of_jobs = props
.get("jobs")
.map_or("0", String::as_str)
.trim()
.parse::<i64>()
.ok()?;
let num_of_jobs = props.jobs;
if num_of_jobs == 0
&& config.threshold > 0

View File

@ -168,9 +168,9 @@ mod tests {
use std::io;
use super::*;
use crate::context::Target;
use crate::test::ModuleRenderer;
use ansi_term::Color;
use clap::ArgMatches;
#[test]
fn pulumi_version_release() {
@ -192,7 +192,7 @@ mod tests {
#[test]
fn get_home_dir() {
let mut context = Context::new(ArgMatches::default());
let mut context = Context::new(Default::default(), Target::Main);
context.env.insert("HOME", "/home/sweet/home".to_string());
assert_eq!(
pulumi_home_dir(&context),
@ -204,7 +204,7 @@ mod tests {
#[test]
fn test_get_pulumi_workspace() {
let mut context = Context::new(ArgMatches::default());
let mut context = Context::new(Default::default(), Target::Main);
context.env.insert("HOME", "/home/sweet/home".to_string());
let name = "foobar";
let project_file = PathBuf::from("/hello/Pulumi.yaml");

View File

@ -268,7 +268,7 @@ enum RustupRunRustcVersionOutcome {
#[cfg(test)]
mod tests {
use crate::context::Shell;
use crate::context::{Shell, Target};
use once_cell::sync::Lazy;
use std::io;
use std::process::{ExitStatus, Output};
@ -424,6 +424,7 @@ mod tests {
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
Target::Main,
dir.path().into(),
dir.path().into(),
);
@ -444,6 +445,7 @@ mod tests {
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
Target::Main,
dir.path().into(),
dir.path().into(),
);
@ -464,6 +466,7 @@ mod tests {
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
Target::Main,
dir.path().into(),
dir.path().into(),
);
@ -486,6 +489,7 @@ mod tests {
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
Target::Main,
child_dir_path.clone(),
child_dir_path,
);
@ -505,6 +509,7 @@ mod tests {
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
Target::Main,
dir.path().into(),
dir.path().into(),
);
@ -522,6 +527,7 @@ mod tests {
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
Target::Main,
dir.path().into(),
dir.path().into(),
);
@ -542,6 +548,7 @@ mod tests {
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
Target::Main,
dir.path().into(),
dir.path().into(),
);
@ -564,6 +571,7 @@ mod tests {
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
Target::Main,
child_dir_path.clone(),
child_dir_path,
);

View File

@ -11,7 +11,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
return None;
}
let shell = context.shell;
let shell = &context.shell;
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
formatter

View File

@ -28,10 +28,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
return None;
};
let exit_code = context
.properties
.get("status_code")
.map_or("0", String::as_str);
let exit_code = context.properties.status_code.unwrap_or_default();
let pipestatus_status = match &context.pipestatus {
None => PipeStatusStatus::Disabled,
@ -47,7 +44,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
};
// Exit code is zero and pipestatus is all zero or disabled/missing
if exit_code == "0"
if exit_code == 0
&& (match pipestatus_status {
PipeStatusStatus::Pipe(ps) => ps.iter().all(|s| s == "0"),
_ => true,
@ -79,7 +76,13 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
PipeStatusStatus::Pipe(_) => config.pipestatus_format,
_ => config.format,
};
let parsed = format_exit_code(exit_code, main_format, Some(&pipestatus), &config, context);
let parsed = format_exit_code(
&exit_code.to_string(),
main_format,
Some(&pipestatus),
&config,
context,
);
module.set_segments(match parsed {
Ok(segments) => segments,

View File

@ -1,5 +1,4 @@
use ansi_term::ANSIStrings;
use clap::ArgMatches;
use rayon::prelude::*;
use std::collections::BTreeSet;
use std::fmt::{self, Debug, Write as FmtWrite};
@ -10,7 +9,7 @@ use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthChar;
use crate::configs::PROMPT_ORDER;
use crate::context::{Context, Shell};
use crate::context::{Context, Properties, Shell, Target};
use crate::formatter::{StringFormatter, VariableHolder};
use crate::module::Module;
use crate::module::ALL_MODULES;
@ -54,8 +53,8 @@ fn test_grapheme_aware_width() {
assert_eq!(11, "normal text".width_graphemes());
}
pub fn prompt(args: ArgMatches) {
let context = Context::new(args);
pub fn prompt(args: Properties, target: Target) {
let context = Context::new(args, target);
let stdout = io::stdout();
let mut handle = stdout.lock();
write!(handle, "{}", get_prompt(context)).unwrap();
@ -114,13 +113,13 @@ pub fn get_prompt(context: Context) -> String {
);
let module_strings = root_module.ansi_strings_for_shell(context.shell, Some(context.width));
if config.add_newline && !context.continuation {
if config.add_newline && context.target != Target::Continuation {
// continuation prompts normally do not include newlines, but they can
writeln!(buf).unwrap();
}
write!(buf, "{}", ANSIStrings(&module_strings)).unwrap();
if context.right {
if context.target == Target::Right {
// right prompts generally do not allow newlines
buf = buf.replace('\n', "");
}
@ -135,8 +134,8 @@ pub fn get_prompt(context: Context) -> String {
buf
}
pub fn module(module_name: &str, args: ArgMatches) {
let context = Context::new(args);
pub fn module(module_name: &str, args: Properties) {
let context = Context::new(args, Target::Main);
let module = get_module(module_name, context).unwrap_or_default();
print!("{}", module);
}
@ -145,8 +144,8 @@ pub fn get_module(module_name: &str, context: Context) -> Option<String> {
modules::handle(module_name, &context).map(|m| m.to_string())
}
pub fn timings(args: ArgMatches) {
let context = Context::new(args);
pub fn timings(args: Properties) {
let context = Context::new(args, Target::Main);
struct ModuleTiming {
name: String,
@ -191,8 +190,8 @@ pub fn timings(args: ArgMatches) {
}
}
pub fn explain(args: ArgMatches) {
let context = Context::new(args);
pub fn explain(args: Properties) {
let context = Context::new(args, Target::Main);
struct ModuleInfo {
value: String,
@ -431,16 +430,14 @@ fn load_formatter_and_modules<'a>(context: &'a Context) -> (StringFormatter<'a>,
match (lformatter, rformatter, cformatter) {
(Ok(lf), Ok(rf), Ok(cf)) => {
let mut modules: BTreeSet<String> = BTreeSet::new();
if !context.continuation {
if context.target != Target::Continuation {
modules.extend(lf.get_variables());
modules.extend(rf.get_variables());
}
if context.continuation {
(cf, modules)
} else if context.right {
(rf, modules)
} else {
(lf, modules)
match context.target {
Target::Main => (lf, modules),
Target::Right => (rf, modules),
Target::Continuation => (cf, modules),
}
}
_ => (StringFormatter::raw(">"), BTreeSet::new()),
@ -464,7 +461,7 @@ mod test {
}),
};
context.root_config.right_format = "$character".to_string();
context.right = true;
context.target = Target::Right;
let expected = String::from(">>"); // should strip new lines
let actual = get_prompt(context);
@ -480,7 +477,7 @@ mod test {
}),
};
context.root_config.continuation_prompt = "><>".to_string();
context.continuation = true;
context.target = Target::Continuation;
let expected = String::from("><>");
let actual = get_prompt(context);

View File

@ -1,4 +1,4 @@
use crate::context::{Context, Shell};
use crate::context::{Context, Shell, Target};
use crate::logger::StarshipLogger;
use crate::{
config::{RootModuleConfig, StarshipConfig},
@ -34,8 +34,9 @@ static LOGGER: Lazy<()> = Lazy::new(|| {
pub fn default_context() -> Context<'static> {
let mut context = Context::new_with_shell_and_path(
clap::ArgMatches::default(),
Default::default(),
Shell::Unknown,
Target::Main,
PathBuf::new(),
PathBuf::new(),
);
@ -103,15 +104,13 @@ impl<'a> ModuleRenderer<'a> {
self
}
pub fn jobs(mut self, jobs: u64) -> Self {
self.context.properties.insert("jobs", jobs.to_string());
pub fn jobs(mut self, jobs: i64) -> Self {
self.context.properties.jobs = jobs;
self
}
pub fn cmd_duration(mut self, duration: u64) -> Self {
self.context
.properties
.insert("cmd_duration", duration.to_string());
self.context.properties.cmd_duration = Some(duration.to_string());
self
}
@ -119,14 +118,12 @@ impl<'a> ModuleRenderer<'a> {
where
T: Into<String>,
{
self.context.properties.insert("keymap", keymap.into());
self.context.properties.keymap = keymap.into();
self
}
pub fn status(mut self, status: i32) -> Self {
self.context
.properties
.insert("status_code", status.to_string());
self.context.properties.status_code = Some(status);
self
}