feat: Add the hg_branch module (#569)
This commit is contained in:
parent
33df8ac063
commit
337f213753
|
@ -124,6 +124,17 @@ jobs:
|
||||||
with:
|
with:
|
||||||
dotnet-version: "2.2.402"
|
dotnet-version: "2.2.402"
|
||||||
|
|
||||||
|
# Install Mercurial (pre-installed on linux, installed from pip on macos
|
||||||
|
# and from choco on windows),
|
||||||
|
- name: Install Mercurial (macos)
|
||||||
|
if: matrix.os == 'macOS-latest'
|
||||||
|
env:
|
||||||
|
HGPYTHON3: 1
|
||||||
|
run: pip install mercurial
|
||||||
|
- name: Install Mercurial (windows)
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
run: choco install hg
|
||||||
|
|
||||||
# Run the ignored tests that expect the above setup
|
# Run the ignored tests that expect the above setup
|
||||||
- name: Run all tests
|
- name: Run all tests
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
|
|
|
@ -93,6 +93,7 @@ prompt_order = [
|
||||||
"git_branch",
|
"git_branch",
|
||||||
"git_state",
|
"git_state",
|
||||||
"git_status",
|
"git_status",
|
||||||
|
"hg_branch",
|
||||||
"package",
|
"package",
|
||||||
"dotnet",
|
"dotnet",
|
||||||
"golang",
|
"golang",
|
||||||
|
@ -559,6 +560,31 @@ The module will be shown if any of the following conditions are met:
|
||||||
symbol = "🏎💨 "
|
symbol = "🏎💨 "
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Mercurial Branch
|
||||||
|
|
||||||
|
The `hg_branch` module shows the active branch of the repo in your current directory.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
| ------------------- | --------------- | -------------------------------------------------------------------------------------------- |
|
||||||
|
| `symbol` | `" "` | The symbol used before the hg bookmark or branch name of the repo in your current directory. |
|
||||||
|
| `truncation_length` | `2^63 - 1` | Truncates the hg branch name to X graphemes |
|
||||||
|
| `truncation_symbol` | `"…"` | The symbol used to indicate a branch name was truncated. |
|
||||||
|
| `style` | `"bold purple"` | The style for the module. |
|
||||||
|
| `disabled` | `true` | Disables the `hg_branch` module. |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# ~/.config/starship.toml
|
||||||
|
|
||||||
|
[hg_branch]
|
||||||
|
symbol = "🌱 "
|
||||||
|
truncation_length = 4
|
||||||
|
truncation_symbol = ""
|
||||||
|
```
|
||||||
|
|
||||||
## Hostname
|
## Hostname
|
||||||
|
|
||||||
The `hostname` module shows the system hostname.
|
The `hostname` module shows the system hostname.
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
|
||||||
|
|
||||||
|
use ansi_term::{Color, Style};
|
||||||
|
use starship_module_config_derive::ModuleConfig;
|
||||||
|
|
||||||
|
#[derive(Clone, ModuleConfig)]
|
||||||
|
pub struct HgBranchConfig<'a> {
|
||||||
|
pub symbol: SegmentConfig<'a>,
|
||||||
|
pub truncation_length: i64,
|
||||||
|
pub truncation_symbol: &'a str,
|
||||||
|
pub branch_name: SegmentConfig<'a>,
|
||||||
|
pub style: Style,
|
||||||
|
pub disabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RootModuleConfig<'a> for HgBranchConfig<'a> {
|
||||||
|
fn new() -> Self {
|
||||||
|
HgBranchConfig {
|
||||||
|
symbol: SegmentConfig::new(" "),
|
||||||
|
truncation_length: std::i64::MAX,
|
||||||
|
truncation_symbol: "…",
|
||||||
|
branch_name: SegmentConfig::default(),
|
||||||
|
style: Color::Purple.bold(),
|
||||||
|
disabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ pub mod git_branch;
|
||||||
pub mod git_state;
|
pub mod git_state;
|
||||||
pub mod git_status;
|
pub mod git_status;
|
||||||
pub mod go;
|
pub mod go;
|
||||||
|
pub mod hg_branch;
|
||||||
pub mod hostname;
|
pub mod hostname;
|
||||||
pub mod java;
|
pub mod java;
|
||||||
pub mod jobs;
|
pub mod jobs;
|
||||||
|
|
|
@ -24,6 +24,7 @@ impl<'a> RootModuleConfig<'a> for StarshipRootConfig<'a> {
|
||||||
"git_branch",
|
"git_branch",
|
||||||
"git_state",
|
"git_state",
|
||||||
"git_status",
|
"git_status",
|
||||||
|
"hg_branch",
|
||||||
"package",
|
"package",
|
||||||
// ↓ Toolchain version modules ↓
|
// ↓ Toolchain version modules ↓
|
||||||
// (Let's keep these sorted alphabetically)
|
// (Let's keep these sorted alphabetically)
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub const ALL_MODULES: &[&str] = &[
|
||||||
"git_state",
|
"git_state",
|
||||||
"git_status",
|
"git_status",
|
||||||
"golang",
|
"golang",
|
||||||
|
"hg_branch",
|
||||||
"hostname",
|
"hostname",
|
||||||
"java",
|
"java",
|
||||||
"jobs",
|
"jobs",
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
use std::process::Command;
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
|
use super::{Context, Module, RootModuleConfig};
|
||||||
|
|
||||||
|
use crate::configs::hg_branch::HgBranchConfig;
|
||||||
|
|
||||||
|
/// 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_files(&[".hgignore"])
|
||||||
|
.set_folders(&[".hg"])
|
||||||
|
.is_match();
|
||||||
|
|
||||||
|
if !is_hg_repo {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut module = context.new_module("hg_branch");
|
||||||
|
let config = HgBranchConfig::try_load(module.config);
|
||||||
|
module.set_style(config.style);
|
||||||
|
|
||||||
|
module.get_prefix().set_value("on ");
|
||||||
|
|
||||||
|
let truncation_symbol = get_graphemes(config.truncation_symbol, 1);
|
||||||
|
module.create_segment("symbol", &config.symbol);
|
||||||
|
|
||||||
|
// TODO: Once error handling is implemented, warn the user if their config
|
||||||
|
// truncation length is nonsensical
|
||||||
|
let len = if config.truncation_length <= 0 {
|
||||||
|
log::warn!(
|
||||||
|
"\"truncation_length\" should be a positive value, found {}",
|
||||||
|
config.truncation_length
|
||||||
|
);
|
||||||
|
std::usize::MAX
|
||||||
|
} else {
|
||||||
|
config.truncation_length as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
let get_branch_name = |tmpl| get_hg_log_template(tmpl, context);
|
||||||
|
|
||||||
|
let branch_name = get_branch_name("{activebookmark}")
|
||||||
|
.or_else(|| get_branch_name("{branch}"))
|
||||||
|
.unwrap_or_else(|| "(no branch)".to_string());
|
||||||
|
|
||||||
|
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) {
|
||||||
|
truncated_graphemes + &truncation_symbol
|
||||||
|
} else {
|
||||||
|
truncated_graphemes
|
||||||
|
};
|
||||||
|
|
||||||
|
module.create_segment(
|
||||||
|
"name",
|
||||||
|
&config.branch_name.with_value(&truncated_and_symbol),
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(module)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_hg_log_template(hgtmpl: &str, ctx: &Context) -> Option<String> {
|
||||||
|
let output = Command::new("hg")
|
||||||
|
.args(&["log", "-r", ".", "--template", hgtmpl])
|
||||||
|
.current_dir(&ctx.current_dir)
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.and_then(|output| String::from_utf8(output.stdout).ok())?;
|
||||||
|
|
||||||
|
if output.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_graphemes(text: &str, length: usize) -> String {
|
||||||
|
UnicodeSegmentation::graphemes(text, true)
|
||||||
|
.take(length)
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
.concat()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn graphemes_len(text: &str) -> usize {
|
||||||
|
UnicodeSegmentation::graphemes(&text[..], true).count()
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ mod git_branch;
|
||||||
mod git_state;
|
mod git_state;
|
||||||
mod git_status;
|
mod git_status;
|
||||||
mod golang;
|
mod golang;
|
||||||
|
mod hg_branch;
|
||||||
mod hostname;
|
mod hostname;
|
||||||
mod java;
|
mod java;
|
||||||
mod jobs;
|
mod jobs;
|
||||||
|
@ -50,6 +51,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
|
||||||
"git_state" => git_state::module(context),
|
"git_state" => git_state::module(context),
|
||||||
"git_status" => git_status::module(context),
|
"git_status" => git_status::module(context),
|
||||||
"golang" => golang::module(context),
|
"golang" => golang::module(context),
|
||||||
|
"hg_branch" => hg_branch::module(context),
|
||||||
"hostname" => hostname::module(context),
|
"hostname" => hostname::module(context),
|
||||||
"java" => java::module(context),
|
"java" => java::module(context),
|
||||||
"jobs" => jobs::module(context),
|
"jobs" => jobs::module(context),
|
||||||
|
|
|
@ -61,6 +61,11 @@ RUN mkdir -p "$DOTNET_HOME" \
|
||||||
ENV PATH $DOTNET_HOME:$PATH
|
ENV PATH $DOTNET_HOME:$PATH
|
||||||
RUN dotnet help
|
RUN dotnet help
|
||||||
|
|
||||||
|
# Install Mercurial
|
||||||
|
RUN HGPYTHON3=1 pip install mercurial
|
||||||
|
# Check that Mercurial was correctly installed
|
||||||
|
RUN hg --version
|
||||||
|
|
||||||
# Create blank project
|
# Create blank project
|
||||||
RUN USER=nonroot cargo new --bin /src/starship
|
RUN USER=nonroot cargo new --bin /src/starship
|
||||||
WORKDIR /src/starship
|
WORKDIR /src/starship
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,219 @@
|
||||||
|
use ansi_term::{Color, Style};
|
||||||
|
use std::fs;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::Command;
|
||||||
|
use std::{env, io};
|
||||||
|
use tempfile;
|
||||||
|
|
||||||
|
use crate::common::{self, TestCommand};
|
||||||
|
|
||||||
|
enum Expect<'a> {
|
||||||
|
BranchName(&'a str),
|
||||||
|
Empty,
|
||||||
|
NoTruncation,
|
||||||
|
Symbol(&'a str),
|
||||||
|
Style(Style),
|
||||||
|
TruncationSymbol(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_hg_get_branch_fails() -> io::Result<()> {
|
||||||
|
let tempdir = tempfile::tempdir()?;
|
||||||
|
|
||||||
|
// Create a fake corrupted mercurial repo.
|
||||||
|
let hgdir = tempdir.path().join(".hg");
|
||||||
|
fs::create_dir(&hgdir)?;
|
||||||
|
fs::write(&hgdir.join("requires"), "fake-corrupted-repo")?;
|
||||||
|
|
||||||
|
expect_hg_branch_with_config(
|
||||||
|
tempdir.path(),
|
||||||
|
"",
|
||||||
|
&[Expect::BranchName(&"(no branch)"), Expect::NoTruncation],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_hg_get_branch_autodisabled() -> io::Result<()> {
|
||||||
|
let tempdir = tempfile::tempdir()?;
|
||||||
|
expect_hg_branch_with_config(tempdir.path(), "", &[Expect::Empty])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_hg_bookmark() -> io::Result<()> {
|
||||||
|
let tempdir = tempfile::tempdir()?;
|
||||||
|
let repo_dir = create_fixture_hgrepo(&tempdir)?;
|
||||||
|
run_hg(&["bookmark", "bookmark-101"], &repo_dir)?;
|
||||||
|
expect_hg_branch_with_config(
|
||||||
|
&repo_dir,
|
||||||
|
"",
|
||||||
|
&[Expect::BranchName(&"bookmark-101"), Expect::NoTruncation],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_default_truncation_symbol() -> io::Result<()> {
|
||||||
|
let tempdir = tempfile::tempdir()?;
|
||||||
|
let repo_dir = create_fixture_hgrepo(&tempdir)?;
|
||||||
|
run_hg(&["branch", "-f", "branch-name-101"], &repo_dir)?;
|
||||||
|
run_hg(
|
||||||
|
&[
|
||||||
|
"commit",
|
||||||
|
"-m",
|
||||||
|
"empty commit 101",
|
||||||
|
"-u",
|
||||||
|
"fake user <fake@user>",
|
||||||
|
],
|
||||||
|
&repo_dir,
|
||||||
|
)?;
|
||||||
|
expect_hg_branch_with_config(
|
||||||
|
&repo_dir,
|
||||||
|
"truncation_length = 14",
|
||||||
|
&[Expect::BranchName(&"branch-name-10")],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_configured_symbols() -> io::Result<()> {
|
||||||
|
let tempdir = tempfile::tempdir()?;
|
||||||
|
let repo_dir = create_fixture_hgrepo(&tempdir)?;
|
||||||
|
run_hg(&["branch", "-f", "branch-name-121"], &repo_dir)?;
|
||||||
|
run_hg(
|
||||||
|
&[
|
||||||
|
"commit",
|
||||||
|
"-m",
|
||||||
|
"empty commit 121",
|
||||||
|
"-u",
|
||||||
|
"fake user <fake@user>",
|
||||||
|
],
|
||||||
|
&repo_dir,
|
||||||
|
)?;
|
||||||
|
expect_hg_branch_with_config(
|
||||||
|
&repo_dir,
|
||||||
|
r#"
|
||||||
|
symbol = "B "
|
||||||
|
truncation_length = 14
|
||||||
|
truncation_symbol = "%"
|
||||||
|
"#,
|
||||||
|
&[
|
||||||
|
Expect::BranchName(&"branch-name-12"),
|
||||||
|
Expect::Symbol(&"B"),
|
||||||
|
Expect::TruncationSymbol(&"%"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_configured_style() -> io::Result<()> {
|
||||||
|
let tempdir = tempfile::tempdir()?;
|
||||||
|
let repo_dir = create_fixture_hgrepo(&tempdir)?;
|
||||||
|
run_hg(&["branch", "-f", "branch-name-131"], &repo_dir)?;
|
||||||
|
run_hg(
|
||||||
|
&[
|
||||||
|
"commit",
|
||||||
|
"-m",
|
||||||
|
"empty commit 131",
|
||||||
|
"-u",
|
||||||
|
"fake user <fake@user>",
|
||||||
|
],
|
||||||
|
&repo_dir,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
expect_hg_branch_with_config(
|
||||||
|
&repo_dir,
|
||||||
|
r#"
|
||||||
|
style = "underline blue"
|
||||||
|
"#,
|
||||||
|
&[
|
||||||
|
Expect::BranchName(&"branch-name-131"),
|
||||||
|
Expect::Style(Color::Blue.underline()),
|
||||||
|
Expect::TruncationSymbol(&""),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expect_hg_branch_with_config(
|
||||||
|
repo_dir: &Path,
|
||||||
|
config_options: &str,
|
||||||
|
expectations: &[Expect],
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let output = common::render_module("hg_branch")
|
||||||
|
.use_config(toml::from_str(&format!(
|
||||||
|
r#"
|
||||||
|
[hg_branch]
|
||||||
|
{}
|
||||||
|
"#,
|
||||||
|
config_options
|
||||||
|
))?)
|
||||||
|
.arg("--path")
|
||||||
|
.arg(repo_dir.to_str().unwrap())
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
let actual = String::from_utf8(output.stdout).unwrap();
|
||||||
|
|
||||||
|
let mut expect_branch_name = "(no branch)";
|
||||||
|
let mut expect_style = Color::Purple.bold();
|
||||||
|
let mut expect_symbol = "\u{e0a0}";
|
||||||
|
let mut expect_truncation_symbol = "…";
|
||||||
|
|
||||||
|
for expect in expectations {
|
||||||
|
match expect {
|
||||||
|
Expect::Empty => {
|
||||||
|
assert_eq!("", actual);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Expect::Symbol(symbol) => {
|
||||||
|
expect_symbol = symbol;
|
||||||
|
}
|
||||||
|
Expect::TruncationSymbol(truncation_symbol) => {
|
||||||
|
expect_truncation_symbol = truncation_symbol;
|
||||||
|
}
|
||||||
|
Expect::NoTruncation => {
|
||||||
|
expect_truncation_symbol = "";
|
||||||
|
}
|
||||||
|
Expect::BranchName(branch_name) => {
|
||||||
|
expect_branch_name = branch_name;
|
||||||
|
}
|
||||||
|
Expect::Style(style) => expect_style = *style,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected = format!(
|
||||||
|
"on {} ",
|
||||||
|
expect_style.paint(format!(
|
||||||
|
"{} {}{}",
|
||||||
|
expect_symbol, expect_branch_name, expect_truncation_symbol
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_fixture_hgrepo(tempdir: &tempfile::TempDir) -> io::Result<PathBuf> {
|
||||||
|
let repo_path = tempdir.path().join("hg-repo");
|
||||||
|
let fixture_path = env::current_dir()?.join("tests/fixtures/hg-repo.bundle");
|
||||||
|
|
||||||
|
run_hg(
|
||||||
|
&[
|
||||||
|
"clone",
|
||||||
|
fixture_path.to_str().unwrap(),
|
||||||
|
repo_path.to_str().unwrap(),
|
||||||
|
],
|
||||||
|
&tempdir.path(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(repo_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_hg(args: &[&str], repo_dir: &Path) -> io::Result<()> {
|
||||||
|
Command::new("hg")
|
||||||
|
.args(args)
|
||||||
|
.current_dir(&repo_dir)
|
||||||
|
.output()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ mod git_branch;
|
||||||
mod git_state;
|
mod git_state;
|
||||||
mod git_status;
|
mod git_status;
|
||||||
mod golang;
|
mod golang;
|
||||||
|
mod hg_branch;
|
||||||
mod hostname;
|
mod hostname;
|
||||||
mod jobs;
|
mod jobs;
|
||||||
mod line_break;
|
mod line_break;
|
||||||
|
|
Loading…
Reference in New Issue