mirror of
https://github.com/starship/starship.git
synced 2024-12-15 03:40:45 +01:00
Merge branch 'master' into conditional-style
This commit is contained in:
commit
bde21e5fb8
@ -4,8 +4,4 @@ ignore = [
|
||||
# chrono dependency, but vulnerable function is never called
|
||||
# Tacked in #3163
|
||||
"RUSTSEC-2020-0071",
|
||||
# chrono: Potential segfault in localtime_r invocations
|
||||
# starship avoids setting any environment variables to avoid this issue
|
||||
# Tracked in #3166
|
||||
"RUSTSEC-2020-0159",
|
||||
]
|
||||
|
906
Cargo.lock
generated
906
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
Cargo.toml
@ -22,9 +22,8 @@ keywords = ["prompt", "shell", "bash", "fish", "zsh"]
|
||||
license = "ISC"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/starship/starship"
|
||||
# MSRV is specified due to our dependency in git2
|
||||
# Note: MSRV is only intended as a hint, and only the latest version is officially supported in starship.
|
||||
rust-version = "1.60"
|
||||
rust-version = "1.59"
|
||||
description = """
|
||||
The minimal, blazing-fast, and infinitely customizable prompt for any shell! ☄🌌️
|
||||
"""
|
||||
@ -37,13 +36,13 @@ notify = ["notify-rust"]
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.12.1"
|
||||
chrono = "0.4.19"
|
||||
chrono = { version = "0.4.20", features = ["clock", "std"] }
|
||||
clap = { version = "=3.2.16", features = ["derive", "cargo", "unicode", "unstable-v4"] }
|
||||
clap_complete = "3.2.3"
|
||||
dirs-next = "2.0.0"
|
||||
dunce = "1.0.2"
|
||||
gethostname = "0.2.3"
|
||||
git2 = { version = "0.14.4", default-features = false }
|
||||
git-repository = "0.20.0"
|
||||
indexmap = { version = "1.9.1", features = ["serde"] }
|
||||
local_ipaddress = "0.1.3"
|
||||
log = { version = "0.4.16", features = ["std"] }
|
||||
@ -52,20 +51,20 @@ log = { version = "0.4.16", features = ["std"] }
|
||||
notify-rust = { version = "4.5.8", optional = true }
|
||||
once_cell = "1.13.0"
|
||||
open = "3.0.2"
|
||||
os_info = "3.4.0"
|
||||
path-slash = "0.2.0"
|
||||
os_info = "3.5.0"
|
||||
path-slash = "0.2.1"
|
||||
pest = "2.2.1"
|
||||
pest_derive = "2.1.0"
|
||||
pest_derive = "2.2.1"
|
||||
quick-xml = "0.23.0"
|
||||
rand = "0.8.5"
|
||||
rayon = "1.5.3"
|
||||
regex = "1.6.0"
|
||||
rust-ini = "0.18.0"
|
||||
semver = "1.0.12"
|
||||
serde = { version = "1.0.140", features = ["derive"] }
|
||||
serde_json = "1.0.82"
|
||||
semver = "1.0.13"
|
||||
serde = { version = "1.0.143", features = ["derive"] }
|
||||
serde_json = "1.0.83"
|
||||
sha-1 = "0.10.0"
|
||||
shadow-rs = "0.16.1"
|
||||
shadow-rs = { version = "0.16.1", default-features = false }
|
||||
# battery is optional (on by default) because the crate doesn't currently build for Termux
|
||||
# see: https://github.com/svartalf/rust-battery/issues/33
|
||||
starship-battery = { version = "0.7.9", optional = true }
|
||||
@ -109,7 +108,8 @@ features = [
|
||||
nix = { version = "0.24.2", default-features = false, features = ["feature", "fs", "user"] }
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.16.1"
|
||||
shadow-rs = { version = "0.16.1", default-features = false }
|
||||
dunce = "1.0.2"
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
winres = "0.1.12"
|
||||
|
60
build.rs
60
build.rs
@ -1,7 +1,11 @@
|
||||
use std::error::Error;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
use shadow_rs::SdResult;
|
||||
|
||||
fn main() -> SdResult<()> {
|
||||
shadow_rs::new().map_err(|err| err.to_string())?;
|
||||
shadow_rs::new_hook(gen_presets_hook)?;
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
@ -13,3 +17,55 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gen_presets_hook(mut file: &File) -> SdResult<()> {
|
||||
println!("cargo:rerun-if-changed=docs/.vuepress/public/presets/toml");
|
||||
let paths = fs::read_dir("docs/.vuepress/public/presets/toml")?;
|
||||
|
||||
let mut presets = String::new();
|
||||
let mut match_arms = String::new();
|
||||
for path in paths {
|
||||
let unwrapped = path?;
|
||||
let file_name = unwrapped.file_name();
|
||||
let full_path = dunce::canonicalize(unwrapped.path())?;
|
||||
let full_path = full_path.to_str().expect("failed to convert to string");
|
||||
let name = file_name
|
||||
.to_str()
|
||||
.and_then(|v| v.strip_suffix(".toml"))
|
||||
.expect("Failed to process filename");
|
||||
presets.push_str(format!("print::Preset(\"{}\"),\n", name).as_str());
|
||||
match_arms.push_str(
|
||||
format!(
|
||||
r#"
|
||||
"{name}" => {{
|
||||
let mut stdout = io::stdout().lock();
|
||||
let _ = stdout.write_all(include_bytes!(r"{full_path}"));
|
||||
}}
|
||||
"#
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
writeln!(
|
||||
file,
|
||||
r#"
|
||||
use std::io::{{self, Write}};
|
||||
use crate::print;
|
||||
|
||||
pub fn get_preset_list<'a>() -> &'a [print::Preset] {{
|
||||
&[
|
||||
{presets}
|
||||
]
|
||||
}}
|
||||
|
||||
pub fn print_preset_content(name: &str) {{
|
||||
match name {{
|
||||
{match_arms}
|
||||
_ => {{}}
|
||||
}}
|
||||
}}
|
||||
"#
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -9,6 +9,10 @@ in brackets instead of using the default Starship wording ("via", "on", etc.).
|
||||
|
||||
### Configuration
|
||||
|
||||
```sh
|
||||
starship preset bracketed-segments > ~/.config/starship.toml
|
||||
```
|
||||
|
||||
[Click to download TOML](/presets/toml/bracketed-segments.toml)
|
||||
|
||||
<<< @/.vuepress/public/presets/toml/bracketed-segments.toml
|
||||
|
@ -12,6 +12,10 @@ This preset changes the symbols for each module to use Nerd Font symbols.
|
||||
|
||||
### Configuration
|
||||
|
||||
```sh
|
||||
starship preset nerd-font-symbols > ~/.config/starship.toml
|
||||
```
|
||||
|
||||
[Click to download TOML](/presets/toml/nerd-font-symbols.toml)
|
||||
|
||||
<<< @/.vuepress/public/presets/toml/nerd-font-symbols.toml
|
||||
|
@ -8,6 +8,10 @@ This preset hides the version of language runtimes. If you work in containers or
|
||||
|
||||
### Configuration
|
||||
|
||||
```sh
|
||||
starship preset no-runtime-versions > ~/.config/starship.toml
|
||||
```
|
||||
|
||||
[Click to download TOML](/presets/toml/no-runtime-versions.toml)
|
||||
|
||||
<<< @/.vuepress/public/presets/toml/no-runtime-versions.toml
|
||||
|
@ -13,6 +13,10 @@ It also shows how path substitution works in starship.
|
||||
|
||||
### Configuration
|
||||
|
||||
```sh
|
||||
starship preset pastel-powerline > ~/.config/starship.toml
|
||||
```
|
||||
|
||||
[Click to download TOML](/presets/toml/pastel-powerline.toml)
|
||||
|
||||
<<< @/.vuepress/public/presets/toml/pastel-powerline.toml
|
||||
|
@ -9,6 +9,10 @@ don't have access to Unicode.
|
||||
|
||||
### Configuration
|
||||
|
||||
```sh
|
||||
starship preset plain-text-symbols > ~/.config/starship.toml
|
||||
```
|
||||
|
||||
[Click to download TOML](/presets/toml/plain-text-symbols.toml)
|
||||
|
||||
<<< @/.vuepress/public/presets/toml/plain-text-symbols.toml
|
||||
|
@ -8,6 +8,10 @@ This preset emulates the look and behavior of [Pure](https://github.com/sindreso
|
||||
|
||||
### Configuration
|
||||
|
||||
```sh
|
||||
starship preset pure-preset > ~/.config/starship.toml
|
||||
```
|
||||
|
||||
[Click to download TOML](/presets/toml/pure-preset.toml)
|
||||
|
||||
<<< @/.vuepress/public/presets/toml/pure-preset.toml
|
||||
|
133
src/context.rs
133
src/context.rs
@ -6,7 +6,11 @@ use crate::utils::{create_command, exec_timeout, read_file, CommandOutput};
|
||||
use crate::modules;
|
||||
use crate::utils::{self, home_dir};
|
||||
use clap::Parser;
|
||||
use git2::{ErrorCode::UnbornBranch, Repository, RepositoryState};
|
||||
use git_repository::{
|
||||
self as git,
|
||||
sec::{self as git_sec, trust::DefaultForLevel},
|
||||
state as git_state, Repository, ThreadSafeRepository,
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
#[cfg(test)]
|
||||
use std::collections::HashMap;
|
||||
@ -255,22 +259,51 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
|
||||
/// Will lazily get repo root and branch when a module requests it.
|
||||
pub fn get_repo(&self) -> Result<&Repo, git2::Error> {
|
||||
self.repo.get_or_try_init(|| -> Result<Repo, git2::Error> {
|
||||
let repository = if env::var("GIT_DIR").is_ok() {
|
||||
Repository::open_from_env()
|
||||
} else {
|
||||
let dirs: [PathBuf; 0] = [];
|
||||
Repository::open_ext(&self.current_dir, git2::RepositoryOpenFlags::FROM_ENV, dirs)
|
||||
}?;
|
||||
Ok(Repo {
|
||||
branch: get_current_branch(&repository),
|
||||
workdir: repository.workdir().map(Path::to_path_buf),
|
||||
path: Path::to_path_buf(repository.path()),
|
||||
state: repository.state(),
|
||||
remote: get_remote_repository_info(&repository),
|
||||
pub fn get_repo(&self) -> Result<&Repo, git::discover::Error> {
|
||||
self.repo
|
||||
.get_or_try_init(|| -> Result<Repo, git::discover::Error> {
|
||||
// custom open options
|
||||
let mut git_open_opts_map =
|
||||
git_sec::trust::Mapping::<git::open::Options>::default();
|
||||
|
||||
// don't use the global git configs
|
||||
let config = git::permissions::Config {
|
||||
system: false,
|
||||
git: false,
|
||||
user: false,
|
||||
env: true,
|
||||
includes: true,
|
||||
};
|
||||
// change options for config permissions without touching anything else
|
||||
git_open_opts_map.reduced =
|
||||
git_open_opts_map.reduced.permissions(git::Permissions {
|
||||
config,
|
||||
..git::Permissions::default_for_level(git_sec::Trust::Reduced)
|
||||
});
|
||||
git_open_opts_map.full = git_open_opts_map.full.permissions(git::Permissions {
|
||||
config,
|
||||
..git::Permissions::default_for_level(git_sec::Trust::Full)
|
||||
});
|
||||
|
||||
let shared_repo = ThreadSafeRepository::discover_with_environment_overrides_opts(
|
||||
&self.current_dir,
|
||||
Default::default(),
|
||||
git_open_opts_map,
|
||||
)?;
|
||||
|
||||
let repository = shared_repo.to_thread_local();
|
||||
let branch = get_current_branch(&repository);
|
||||
let remote = get_remote_repository_info(&repository, branch.as_deref());
|
||||
let path = repository.path().to_path_buf();
|
||||
Ok(Repo {
|
||||
repo: shared_repo,
|
||||
branch,
|
||||
workdir: repository.work_dir().map(PathBuf::from),
|
||||
path,
|
||||
state: repository.state(),
|
||||
remote,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn dir_contents(&self) -> Result<&DirContents, std::io::Error> {
|
||||
@ -494,6 +527,8 @@ impl DirContents {
|
||||
}
|
||||
|
||||
pub struct Repo {
|
||||
pub repo: ThreadSafeRepository,
|
||||
|
||||
/// If `current_dir` is a git repository or is contained within one,
|
||||
/// this is the current branch name of that repo.
|
||||
pub branch: Option<String>,
|
||||
@ -506,7 +541,7 @@ pub struct Repo {
|
||||
pub path: PathBuf,
|
||||
|
||||
/// State
|
||||
pub state: RepositoryState,
|
||||
pub state: Option<git_state::InProgress>,
|
||||
|
||||
/// Remote repository
|
||||
pub remote: Option<Remote>,
|
||||
@ -514,8 +549,8 @@ pub struct Repo {
|
||||
|
||||
impl Repo {
|
||||
/// Opens the associated git repository.
|
||||
pub fn open(&self) -> Result<Repository, git2::Error> {
|
||||
Repository::open(&self.path)
|
||||
pub fn open(&self) -> Repository {
|
||||
self.repo.to_thread_local()
|
||||
}
|
||||
}
|
||||
|
||||
@ -570,54 +605,26 @@ impl<'a> ScanDir<'a> {
|
||||
}
|
||||
|
||||
fn get_current_branch(repository: &Repository) -> Option<String> {
|
||||
let head = match repository.head() {
|
||||
Ok(reference) => reference,
|
||||
Err(e) => {
|
||||
return if e.code() == UnbornBranch {
|
||||
// HEAD should only be an unborn branch if the repository is fresh,
|
||||
// in that case read directly from `.git/HEAD`
|
||||
let mut head_path = repository.path().to_path_buf();
|
||||
head_path.push("HEAD");
|
||||
let name = repository.head_name().ok()??;
|
||||
let shorthand = name.shorten();
|
||||
|
||||
// get first line, then last path segment
|
||||
fs::read_to_string(&head_path)
|
||||
.ok()?
|
||||
.lines()
|
||||
.next()?
|
||||
.trim()
|
||||
.split('/')
|
||||
.last()
|
||||
.map(std::borrow::ToOwned::to_owned)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let shorthand = head.shorthand();
|
||||
|
||||
shorthand.map(std::string::ToString::to_string)
|
||||
Some(shorthand.to_string())
|
||||
}
|
||||
|
||||
fn get_remote_repository_info(repository: &Repository) -> Option<Remote> {
|
||||
if let Ok(head) = repository.head() {
|
||||
if let Some(local_branch_ref) = head.name() {
|
||||
let remote_ref = match repository.branch_upstream_name(local_branch_ref) {
|
||||
Ok(remote_ref) => remote_ref.as_str()?.to_owned(),
|
||||
Err(_) => return None,
|
||||
};
|
||||
fn get_remote_repository_info(
|
||||
repository: &Repository,
|
||||
branch_name: Option<&str>,
|
||||
) -> Option<Remote> {
|
||||
let branch_name = branch_name?;
|
||||
let branch = repository
|
||||
.remote_ref(branch_name)
|
||||
.and_then(|r| r.ok())
|
||||
.map(|r| r.shorten().to_string());
|
||||
let name = repository
|
||||
.branch_remote_name(branch_name)
|
||||
.map(|n| n.to_string());
|
||||
|
||||
let mut v = remote_ref.splitn(4, '/');
|
||||
let remote_name = v.nth(2)?.to_owned();
|
||||
let remote_branch = v.last()?.to_owned();
|
||||
|
||||
return Some(Remote {
|
||||
branch: Some(remote_branch),
|
||||
name: Some(remote_name),
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
Some(Remote { branch, name })
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
10
src/main.rs
10
src/main.rs
@ -63,6 +63,15 @@ enum Commands {
|
||||
#[clap(flatten)]
|
||||
properties: Properties,
|
||||
},
|
||||
/// Prints a preset config
|
||||
Preset {
|
||||
/// The name of preset to be printed
|
||||
#[clap(required_unless_present("list"), value_enum)]
|
||||
name: Option<print::Preset>,
|
||||
/// List out all preset names
|
||||
#[clap(short, long)]
|
||||
list: bool,
|
||||
},
|
||||
/// Prints the computed starship configuration
|
||||
PrintConfig {
|
||||
/// Print the default instead of the computed config
|
||||
@ -182,6 +191,7 @@ fn main() {
|
||||
print::module(&module_name, properties);
|
||||
}
|
||||
}
|
||||
Commands::Preset { name, list } => print::preset_command(name, list),
|
||||
Commands::Config { name, value } => {
|
||||
if let Some(name) = name {
|
||||
if let Some(value) = value {
|
||||
|
@ -113,12 +113,12 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
{
|
||||
let root = repo_path_vec[0];
|
||||
let before = dir_string.replace(&contracted_path, "");
|
||||
[prefix + &before, root.to_string(), after_repo_root]
|
||||
[prefix + before.as_str(), root.to_string(), after_repo_root]
|
||||
} else {
|
||||
["".to_string(), "".to_string(), prefix + &dir_string]
|
||||
["".to_string(), "".to_string(), prefix + dir_string.as_str()]
|
||||
}
|
||||
}
|
||||
_ => ["".to_string(), "".to_string(), prefix + &dir_string],
|
||||
_ => ["".to_string(), "".to_string(), prefix + dir_string.as_str()],
|
||||
};
|
||||
|
||||
let path_vec = if config.use_os_path_sep {
|
||||
|
@ -26,12 +26,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
let repo = context.get_repo().ok()?;
|
||||
|
||||
if config.only_attached {
|
||||
if let Ok(git_repo) = repo.open() {
|
||||
if git_repo.head_detached().ok()? {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if config.only_attached && repo.open().head().ok()?.is_detached() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let branch_name = repo.branch.as_ref()?;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use super::{Context, Module, ModuleConfig};
|
||||
use crate::formatter::string_formatter::StringFormatterError;
|
||||
use git2::Time;
|
||||
use git_repository::commit::describe::SelectRef::AnnotatedTags;
|
||||
|
||||
use crate::configs::git_commit::GitCommitConfig;
|
||||
use crate::context::Repo;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the Git commit in the current directory
|
||||
@ -13,45 +13,14 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let config: GitCommitConfig = GitCommitConfig::try_load(module.config);
|
||||
|
||||
let repo = context.get_repo().ok()?;
|
||||
let git_repo = repo.open().ok()?;
|
||||
let git_repo = repo.open();
|
||||
let git_head = git_repo.head().ok()?;
|
||||
|
||||
let is_detached = git_repo.head_detached().ok()?;
|
||||
let is_detached = git_head.is_detached();
|
||||
if config.only_detached && !is_detached {
|
||||
return None;
|
||||
};
|
||||
|
||||
let git_head = git_repo.head().ok()?;
|
||||
let head_commit = git_head.peel_to_commit().ok()?;
|
||||
let commit_oid = head_commit.id();
|
||||
|
||||
let mut tag_name = String::new();
|
||||
if !config.tag_disabled {
|
||||
// Let's get repo tags names
|
||||
let tag_names = git_repo.tag_names(None).ok()?;
|
||||
let tag_and_refs = tag_names.iter().flatten().flat_map(|name| {
|
||||
let full_tag = format!("refs/tags/{}", name);
|
||||
let tag_obj = git_repo.find_reference(&full_tag)?.peel_to_tag()?;
|
||||
let sig_obj = tag_obj.tagger();
|
||||
git_repo.find_reference(&full_tag).map(|reference| {
|
||||
(
|
||||
String::from(name),
|
||||
// fall back to oldest + 1s time if sig_obj is unavailable
|
||||
sig_obj.map_or(git2::Time::new(1, 0), |s| s.when()),
|
||||
reference,
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
let mut oldest = Time::new(0, 0);
|
||||
// Let's check if HEAD has some tag. If several, gets last created one...
|
||||
for (name, timestamp, reference) in tag_and_refs.rev() {
|
||||
if commit_oid == reference.peel_to_commit().ok()?.id() && timestamp > oldest {
|
||||
tag_name = name;
|
||||
oldest = timestamp;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
formatter
|
||||
.map_style(|variable| match variable {
|
||||
@ -59,11 +28,12 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"hash" => Some(Ok(id_to_hex_abbrev(
|
||||
commit_oid.as_bytes(),
|
||||
config.commit_hash_length,
|
||||
"hash" => Some(Ok(git_hash(context.get_repo().ok()?, &config)?)),
|
||||
"tag" => Some(Ok(format!(
|
||||
"{}{}",
|
||||
config.tag_symbol,
|
||||
git_tag(context.get_repo().ok()?)?
|
||||
))),
|
||||
"tag" => format_tag(config.tag_symbol, &tag_name),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None, Some(context))
|
||||
@ -80,23 +50,24 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
Some(module)
|
||||
}
|
||||
|
||||
fn format_tag(symbol: &str, tag_name: &str) -> Option<Result<String, StringFormatterError>> {
|
||||
if tag_name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Ok(format!("{}{}", &symbol, &tag_name)))
|
||||
}
|
||||
fn git_tag(repo: &Repo) -> Option<String> {
|
||||
let git_repo = repo.open();
|
||||
let head_commit = git_repo.head_commit().ok()?;
|
||||
|
||||
let describe_platform = head_commit.describe().names(AnnotatedTags);
|
||||
let formatter = describe_platform.try_format().ok()??;
|
||||
|
||||
Some(formatter.name?.to_string())
|
||||
}
|
||||
|
||||
/// len specifies length of hex encoded string
|
||||
fn id_to_hex_abbrev(bytes: &[u8], len: usize) -> String {
|
||||
bytes
|
||||
.iter()
|
||||
.map(|b| format!("{:02x}", b))
|
||||
.collect::<String>()
|
||||
.chars()
|
||||
.take(len)
|
||||
.collect()
|
||||
fn git_hash(repo: &Repo, config: &GitCommitConfig) -> Option<String> {
|
||||
let git_repo = repo.open();
|
||||
let head_id = git_repo.head_id().ok()?;
|
||||
|
||||
Some(format!(
|
||||
"{}",
|
||||
head_id.to_hex_with_len(config.commit_hash_length)
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
use git2::RepositoryState;
|
||||
use git_repository::state::InProgress;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{Context, Module, ModuleConfig};
|
||||
@ -54,54 +54,53 @@ fn get_state_description<'a>(
|
||||
repo: &'a Repo,
|
||||
config: &GitStateConfig<'a>,
|
||||
) -> Option<StateDescription<'a>> {
|
||||
match repo.state {
|
||||
RepositoryState::Clean => None,
|
||||
RepositoryState::Merge => Some(StateDescription {
|
||||
match repo.state.as_ref()? {
|
||||
InProgress::Merge => Some(StateDescription {
|
||||
label: config.merge,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::Revert => Some(StateDescription {
|
||||
InProgress::Revert => Some(StateDescription {
|
||||
label: config.revert,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::RevertSequence => Some(StateDescription {
|
||||
InProgress::RevertSequence => Some(StateDescription {
|
||||
label: config.revert,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::CherryPick => Some(StateDescription {
|
||||
InProgress::CherryPick => Some(StateDescription {
|
||||
label: config.cherry_pick,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::CherryPickSequence => Some(StateDescription {
|
||||
InProgress::CherryPickSequence => Some(StateDescription {
|
||||
label: config.cherry_pick,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::Bisect => Some(StateDescription {
|
||||
InProgress::Bisect => Some(StateDescription {
|
||||
label: config.bisect,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::ApplyMailbox => Some(StateDescription {
|
||||
InProgress::ApplyMailbox => Some(StateDescription {
|
||||
label: config.am,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::ApplyMailboxOrRebase => Some(StateDescription {
|
||||
InProgress::ApplyMailboxRebase => Some(StateDescription {
|
||||
label: config.am_or_rebase,
|
||||
current: None,
|
||||
total: None,
|
||||
}),
|
||||
RepositoryState::Rebase => Some(describe_rebase(repo, config.rebase)),
|
||||
RepositoryState::RebaseInteractive => Some(describe_rebase(repo, config.rebase)),
|
||||
RepositoryState::RebaseMerge => Some(describe_rebase(repo, config.rebase)),
|
||||
InProgress::Rebase => Some(describe_rebase(repo, config.rebase)),
|
||||
InProgress::RebaseInteractive => Some(describe_rebase(repo, config.rebase)),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use future gitoxide API to get the state of the rebase
|
||||
fn describe_rebase<'a>(repo: &'a Repo, rebase_config: &'a str) -> StateDescription<'a> {
|
||||
/*
|
||||
* Sadly, libgit2 seems to have some issues with reading the state of
|
||||
|
@ -34,6 +34,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
|
||||
//Return None if not in git repository
|
||||
context.get_repo().ok()?;
|
||||
|
||||
if let Some(git_status) = git_status_wsl(context, &config) {
|
||||
if git_status.is_empty() {
|
||||
return None;
|
||||
|
@ -41,7 +41,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
// The truncation symbol should only be added if we truncated
|
||||
let truncated_and_symbol = if len < graphemes_len(&branch_name) {
|
||||
let truncation_symbol = get_graphemes(config.truncation_symbol, 1);
|
||||
truncated_graphemes + &truncation_symbol
|
||||
truncated_graphemes + truncation_symbol.as_str()
|
||||
} else {
|
||||
truncated_graphemes
|
||||
};
|
||||
|
@ -150,7 +150,7 @@ mod tests {
|
||||
.collect();
|
||||
let expected = Some(format!(
|
||||
"{} in ",
|
||||
style().paint("🌐 ".to_owned() + &hostname)
|
||||
style().paint("🌐 ".to_owned() + hostname.as_str())
|
||||
));
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
|
44
src/print.rs
44
src/print.rs
@ -1,4 +1,5 @@
|
||||
use ansi_term::ANSIStrings;
|
||||
use clap::{PossibleValue, ValueEnum};
|
||||
use rayon::prelude::*;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt::{self, Debug, Write as FmtWrite};
|
||||
@ -15,6 +16,7 @@ use crate::module::Module;
|
||||
use crate::module::ALL_MODULES;
|
||||
use crate::modules;
|
||||
use crate::segment::Segment;
|
||||
use crate::shadow;
|
||||
|
||||
pub struct Grapheme<'a>(pub &'a str);
|
||||
|
||||
@ -450,6 +452,35 @@ pub fn print_schema() {
|
||||
println!("{}", serde_json::to_string_pretty(&schema).unwrap());
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Preset(pub &'static str);
|
||||
|
||||
impl ValueEnum for Preset {
|
||||
fn value_variants<'a>() -> &'a [Self] {
|
||||
shadow::get_preset_list()
|
||||
}
|
||||
|
||||
fn to_possible_value<'a>(&self) -> Option<clap::PossibleValue<'a>> {
|
||||
Some(PossibleValue::new(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn preset_command(name: Option<Preset>, list: bool) {
|
||||
if list {
|
||||
println!("{}", preset_list());
|
||||
return;
|
||||
}
|
||||
let variant = name.expect("name argument must be specified");
|
||||
shadow::print_preset_content(variant.0);
|
||||
}
|
||||
|
||||
fn preset_list() -> String {
|
||||
Preset::value_variants()
|
||||
.iter()
|
||||
.map(|v| format!("{}\n", v.0))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@ -490,6 +521,19 @@ mod test {
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preset_list_returns_one_or_more_items() {
|
||||
assert!(preset_list().trim().split('\n').count() > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preset_command_does_not_panic_on_correct_inputs() {
|
||||
preset_command(None, true);
|
||||
Preset::value_variants()
|
||||
.iter()
|
||||
.for_each(|v| preset_command(Some(v.clone()), false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "config-schema")]
|
||||
fn print_schema_does_not_panic() {
|
||||
|
Loading…
Reference in New Issue
Block a user