summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Évrard <nicoe@users.noreply.github.com>2022-12-31 15:53:55 +0100
committerGitHub <noreply@github.com>2022-12-31 15:53:55 +0100
commit8d2256ab1d0ba288fb6ba9b9248bc2210ca01059 (patch)
treef95db36e0a7bee1b41cb589445aa1bbd1d45378e
parent8a8e09dd507b77194c52bb3f2e1826aed2e13390 (diff)
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>
-rw-r--r--.github/config-schema.json4
-rw-r--r--docs/config/README.md29
-rw-r--r--src/configs/hg_branch.rs2
-rw-r--r--src/modules/hg_branch.rs86
-rw-r--r--src/modules/mod.rs2
-rw-r--r--src/modules/utils/path.rs25
6 files changed, 100 insertions, 48 deletions
diff --git a/.github/config-schema.json b/.github/config-schema.json
index 7924c0cea..e4111cbc3 100644
--- a/.github/config-schema.json
+++ b/.github/config-schema.json
@@ -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": {
diff --git a/docs/config/README.md b/docs/config/README.md
index f8b27bfd1..1fc4968fe 100644
--- a/docs/config/README.md
+++ b/docs/config/README.md
@@ -2643,26 +2643,27 @@ 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 |
-| `truncation_symbol` | `'…'` | The symbol used to indicate a branch name was truncated. |
-| `disabled` | `true` | Disables the `hg_branch` module. |
+| 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(:$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 |
-| symbol | | Mirrors the value of option `symbol` |
-| style\* | | Mirrors the value of option `style` |
+| 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` |
*: This variable can only be used as a part of a style string
diff --git a/src/configs/hg_branch.rs b/src/configs/hg_branch.rs
index 9e45fe5ff..fff150c96 100644
--- a/src/configs/hg_branch.rs
+++ b/src/configs/hg_branch.rs
@@ -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,
diff --git a/src/modules/hg_branch.rs b/src/modules/hg_branch.rs
index 3aefd4b65..cb2adecd9 100644
--- a/src/modules/hg_branch.rs
+++ b/src/modules/hg_branch.rs
@@ -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)]
@@ -188,6 +194,26 @@ mod tests {
#[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<()> {
let tempdir = fixture_repo(FixtureProvider::Hg)?;
let repo_dir = tempdir.path();
diff --git a/src/modules/mod.rs b/src/modules/mod.rs
index 64442937f..53c36ec97 100644
--- a/src/modules/mod.rs
+++ b/src/modules/mod.rs
@@ -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",
diff --git a/src/modules/utils/path.rs b/src/modules/utils/path.rs
index 2bbb02b51..5f5ad4847 100644
--- a/src/modules/utils/path.rs
+++ b/src/modules/utils/path.rs
@@ -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)]