mirror of
https://github.com/Llewellynvdm/starship.git
synced 2024-12-01 01:04:00 +00:00
feat(hg_branch): Add support for mercurial topics and find hg root dir (#4771)
* feat(hg_branch): Add support for mercurial topics and find hg root dir * Fix clippy errors * Use crate::utils::read_file * Update config-schema.json * Extend PathExt to retrieve device ID of Path * Break hg root search when switching to another device * Fix clippy and formatting errors * Update docs/config/README.md Co-authored-by: David Knaack <davidkna@users.noreply.github.com> * Update src/modules/utils/path.rs Co-authored-by: David Knaack <davidkna@users.noreply.github.com> * Update src/configs/hg_branch.rs Co-authored-by: David Knaack <davidkna@users.noreply.github.com> * Update hg_branch description * Revert to lazy loading, use truncate_text from utils and use fake topic * Format code and fix clippy error * Revert to previous test string as topic is optional in the config * Fix doc formatting * Stub device_id for windows * Update config-schema.json * Update src/modules/hg_branch.rs Co-authored-by: David Knaack <davidkna@users.noreply.github.com> * Do not use unwrap in device_id * Fix formatter error * Use dev under non linux unixes Co-authored-by: David Knaack <davidkna@users.noreply.github.com>
This commit is contained in:
parent
8a8e09dd50
commit
8d2256ab1d
4
.github/config-schema.json
vendored
4
.github/config-schema.json
vendored
@ -750,7 +750,7 @@
|
||||
"hg_branch": {
|
||||
"default": {
|
||||
"disabled": true,
|
||||
"format": "on [$symbol$branch]($style) ",
|
||||
"format": "on [$symbol$branch(:$topic)]($style) ",
|
||||
"style": "bold purple",
|
||||
"symbol": " ",
|
||||
"truncation_length": 9223372036854775807,
|
||||
@ -3537,7 +3537,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"format": {
|
||||
"default": "on [$symbol$branch]($style) ",
|
||||
"default": "on [$symbol$branch(:$topic)]($style) ",
|
||||
"type": "string"
|
||||
},
|
||||
"truncation_length": {
|
||||
|
@ -2643,24 +2643,25 @@ style = 'bold dimmed green'
|
||||
|
||||
## Mercurial Branch
|
||||
|
||||
The `hg_branch` module shows the active branch of the repo in your current directory.
|
||||
The `hg_branch` module shows the active branch and topic of the repo in your current directory.
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Default | Description |
|
||||
| ------------------- | -------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| ------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| `symbol` | `' '` | The symbol used before the hg bookmark or branch name of the repo in your current directory. |
|
||||
| `style` | `'bold purple'` | The style for the module. |
|
||||
| `format` | `'on [$symbol$branch]($style) '` | The format for the module. |
|
||||
| `truncation_length` | `2^63 - 1` | Truncates the hg branch name to `N` graphemes |
|
||||
| `format` | `'on [$symbol$branch(:$topic)]($style) '` | The format for the module. |
|
||||
| `truncation_length` | `2^63 - 1` | Truncates the hg branch / topic name to `N` graphemes |
|
||||
| `truncation_symbol` | `'…'` | The symbol used to indicate a branch name was truncated. |
|
||||
| `disabled` | `true` | Disables the `hg_branch` module. |
|
||||
|
||||
### Variables
|
||||
|
||||
| Variable | Example | Description |
|
||||
| -------- | -------- | ------------------------------------ |
|
||||
| -------- | --------- | ------------------------------------ |
|
||||
| branch | `master` | The active mercurial branch |
|
||||
| topic | `feature` | The active mercurial topic |
|
||||
| symbol | | Mirrors the value of option `symbol` |
|
||||
| style\* | | Mirrors the value of option `style` |
|
||||
|
||||
|
@ -21,7 +21,7 @@ impl<'a> Default for HgBranchConfig<'a> {
|
||||
HgBranchConfig {
|
||||
symbol: " ",
|
||||
style: "bold purple",
|
||||
format: "on [$symbol$branch]($style) ",
|
||||
format: "on [$symbol$branch(:$topic)]($style) ",
|
||||
truncation_length: std::i64::MAX,
|
||||
truncation_symbol: "…",
|
||||
disabled: true,
|
||||
|
@ -1,20 +1,18 @@
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::path::Path;
|
||||
|
||||
use super::utils::truncate::truncate_text;
|
||||
use super::{Context, Module, ModuleConfig};
|
||||
|
||||
use crate::configs::hg_branch::HgBranchConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::modules::utils::path::PathExt;
|
||||
use crate::utils::read_file;
|
||||
|
||||
/// Creates a module with the Hg bookmark or branch in the current directory
|
||||
///
|
||||
/// Will display the bookmark or branch name if the current directory is an hg repo
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let is_hg_repo = context.try_begin_scan()?.set_folders(&[".hg"]).is_match();
|
||||
|
||||
if !is_hg_repo {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut module = context.new_module("hg_branch");
|
||||
let config: HgBranchConfig = HgBranchConfig::try_load(module.config);
|
||||
|
||||
@ -34,16 +32,16 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
config.truncation_length as usize
|
||||
};
|
||||
|
||||
let branch_name =
|
||||
get_hg_current_bookmark(context).unwrap_or_else(|| get_hg_branch_name(context));
|
||||
let repo_root = get_hg_repo_root(context).ok()?;
|
||||
let branch_name = get_hg_current_bookmark(repo_root).unwrap_or_else(|_| {
|
||||
get_hg_branch_name(repo_root).unwrap_or_else(|_| String::from("default"))
|
||||
});
|
||||
|
||||
let truncated_graphemes = get_graphemes(&branch_name, len);
|
||||
// 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.as_str()
|
||||
let branch_graphemes = truncate_text(&branch_name, len, config.truncation_symbol);
|
||||
let topic_graphemes = if let Ok(topic) = get_hg_topic_name(repo_root) {
|
||||
truncate_text(&topic, len, config.truncation_symbol)
|
||||
} else {
|
||||
truncated_graphemes
|
||||
String::from("")
|
||||
};
|
||||
|
||||
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
|
||||
@ -57,7 +55,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
"branch" => Some(Ok(truncated_and_symbol.as_str())),
|
||||
"branch" => Some(Ok(branch_graphemes.as_str())),
|
||||
"topic" => Some(Ok(topic_graphemes.as_str())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None, Some(context))
|
||||
@ -74,26 +73,33 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
Some(module)
|
||||
}
|
||||
|
||||
fn get_hg_branch_name(ctx: &Context) -> String {
|
||||
std::fs::read_to_string(ctx.current_dir.join(".hg").join("branch"))
|
||||
.map_or_else(|_| "default".to_string(), |s| s.trim().into())
|
||||
fn get_hg_repo_root<'a>(ctx: &'a Context) -> Result<&'a Path, Error> {
|
||||
let dir = ctx.current_dir.as_path();
|
||||
let dev_id = dir.device_id();
|
||||
for root_dir in dir.ancestors() {
|
||||
if dev_id != root_dir.device_id() {
|
||||
break;
|
||||
}
|
||||
if root_dir.join(".hg").is_dir() {
|
||||
return Ok(root_dir);
|
||||
}
|
||||
}
|
||||
Err(Error::new(ErrorKind::Other, "No .hg found!"))
|
||||
}
|
||||
|
||||
fn get_hg_current_bookmark(ctx: &Context) -> Option<String> {
|
||||
std::fs::read_to_string(ctx.current_dir.join(".hg").join("bookmarks.current"))
|
||||
.map(|s| s.trim().into())
|
||||
.ok()
|
||||
fn get_hg_branch_name(hg_root: &Path) -> Result<String, Error> {
|
||||
match read_file(hg_root.join(".hg").join("branch")) {
|
||||
Ok(b) => Ok(b.trim().to_string()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_graphemes(text: &str, length: usize) -> String {
|
||||
UnicodeSegmentation::graphemes(text, true)
|
||||
.take(length)
|
||||
.collect::<Vec<&str>>()
|
||||
.concat()
|
||||
fn get_hg_current_bookmark(hg_root: &Path) -> Result<String, Error> {
|
||||
read_file(hg_root.join(".hg").join("bookmarks.current"))
|
||||
}
|
||||
|
||||
fn graphemes_len(text: &str) -> usize {
|
||||
UnicodeSegmentation::graphemes(text, true).count()
|
||||
fn get_hg_topic_name(hg_root: &Path) -> Result<String, Error> {
|
||||
read_file(hg_root.join(".hg").join("topic"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -186,6 +192,26 @@ mod tests {
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_hg_topic() -> io::Result<()> {
|
||||
let tempdir = fixture_repo(FixtureProvider::Hg)?;
|
||||
let repo_dir = tempdir.path();
|
||||
fs::write(repo_dir.join(".hg").join("topic"), "feature")?;
|
||||
|
||||
let actual = ModuleRenderer::new("hg_branch")
|
||||
.path(repo_dir.to_str().unwrap())
|
||||
.config(toml::toml! {
|
||||
[hg_branch]
|
||||
format = "$topic"
|
||||
disabled = false
|
||||
})
|
||||
.collect();
|
||||
|
||||
assert_eq!(Some(String::from("feature")), actual);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_default_truncation_symbol() -> io::Result<()> {
|
||||
|
@ -249,7 +249,7 @@ pub fn description(module: &str) -> &'static str {
|
||||
"haskell" => "The selected version of the Haskell toolchain",
|
||||
"haxe" => "The currently installed version of Haxe",
|
||||
"helm" => "The currently installed version of Helm",
|
||||
"hg_branch" => "The active branch of the repo in your current directory",
|
||||
"hg_branch" => "The active branch and topic of the repo in your current directory",
|
||||
"hostname" => "The system hostname",
|
||||
"java" => "The currently installed version of Java",
|
||||
"jobs" => "The current number of jobs running",
|
||||
|
@ -13,6 +13,8 @@ pub trait PathExt {
|
||||
/// E.g. `\\?\UNC\server\share\foo` => `\foo`
|
||||
/// E.g. `/foo/bar` => `/foo/bar`
|
||||
fn without_prefix(&self) -> &Path;
|
||||
/// Get device / volume info
|
||||
fn device_id(&self) -> Option<u64>;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
@ -80,6 +82,11 @@ impl PathExt for Path {
|
||||
let (_, path) = normalize::normalize_path(self);
|
||||
path
|
||||
}
|
||||
|
||||
fn device_id(&self) -> Option<u64> {
|
||||
// Maybe it should use unimplemented!
|
||||
Some(42u64)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Windows path prefixes are only parsed on Windows.
|
||||
@ -100,6 +107,24 @@ impl PathExt for Path {
|
||||
fn without_prefix(&self) -> &Path {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn device_id(&self) -> Option<u64> {
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
match self.metadata() {
|
||||
Ok(m) => Some(m.st_dev()),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "linux")))]
|
||||
fn device_id(&self) -> Option<u64> {
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
match self.metadata() {
|
||||
Ok(m) => Some(m.dev()),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Loading…
Reference in New Issue
Block a user