feat(fossil_branch): add fossil_branch module (#4806)

Co-authored-by: David Knaack <davidkna@users.noreply.github.com>
This commit is contained in:
Vegard Skui 2023-01-31 09:06:46 +01:00 committed by GitHub
parent 3d76a987d6
commit 41eb98b310
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 367 additions and 0 deletions

View File

@ -504,6 +504,21 @@
}
]
},
"fossil_branch": {
"default": {
"disabled": true,
"format": "on [$symbol$branch]($style) ",
"style": "bold purple",
"symbol": " ",
"truncation_length": 9223372036854775807,
"truncation_symbol": "…"
},
"allOf": [
{
"$ref": "#/definitions/FossilBranchConfig"
}
]
},
"gcloud": {
"default": {
"disabled": false,
@ -2981,6 +2996,37 @@
},
"additionalProperties": false
},
"FossilBranchConfig": {
"type": "object",
"properties": {
"format": {
"default": "on [$symbol$branch]($style) ",
"type": "string"
},
"symbol": {
"default": " ",
"type": "string"
},
"style": {
"default": "bold purple",
"type": "string"
},
"truncation_length": {
"default": 9223372036854775807,
"type": "integer",
"format": "int64"
},
"truncation_symbol": {
"default": "…",
"type": "string"
},
"disabled": {
"default": true,
"type": "boolean"
}
},
"additionalProperties": false
},
"GcloudConfig": {
"type": "object",
"properties": {

View File

@ -49,6 +49,9 @@ format = '\[[$symbol($version)]($style)\]'
[fennel]
format = '\[[$symbol($version)]($style)\]'
[fossil_branch]
format = '\[[$symbol$branch]($style)\]'
[gcloud]
format = '\[[$symbol$account(@$domain)(\($region\))]($style)\]'

View File

@ -25,6 +25,9 @@ symbol = " "
[elm]
symbol = " "
[fossil_branch]
symbol = " "
[git_branch]
symbol = " "

View File

@ -61,6 +61,9 @@ symbol = "elm "
[fennel]
symbol = "fnl "
[fossil_branch]
symbol = "fossil "
[git_branch]
symbol = "git "

View File

@ -265,6 +265,7 @@ $singularity\
$kubernetes\
$directory\
$vcsh\
$fossil_branch\
$git_branch\
$git_commit\
$git_state\
@ -1556,6 +1557,42 @@ Produces a prompt that looks like:
AA -------------------------------------------- BB -------------------------------------------- CC
```
## Fossil Branch
The `fossil_branch` module shows the name of the active branch of the check-out in your current directory.
### Options
| Option | Default | Description |
| ------------------- | -------------------------------- | ---------------------------------------------------------------------------------------- |
| `format` | `'on [$symbol$branch]($style) '` | The format for the module. Use `'$branch'` to refer to the current branch name. |
| `symbol` | `' '` | The symbol used before the branch name of the check-out in your current directory. |
| `style` | `'bold purple'` | The style for the module. |
| `truncation_length` | `2^63 - 1` | Truncates a Fossil branch name to `N` graphemes |
| `truncation_symbol` | `'…'` | The symbol used to indicate a branch name was truncated. You can use `''` for no symbol. |
| `disabled` | `true` | Disables the `fossil_branch` module. |
### Variables
| Variable | Example | Description |
| -------- | ------- | ------------------------------------ |
| branch | `trunk` | The active Fossil branch |
| 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
### Example
```toml
# ~/.config/starship.toml
[fossil_branch]
symbol = '🦎 '
truncation_length = 4
truncation_symbol = ''
```
## Google Cloud (`gcloud`)
The `gcloud` module shows the current configuration for [`gcloud`](https://cloud.google.com/sdk/gcloud) CLI.

View File

@ -0,0 +1,30 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Deserialize, Serialize)]
#[cfg_attr(
feature = "config-schema",
derive(schemars::JsonSchema),
schemars(deny_unknown_fields)
)]
#[serde(default)]
pub struct FossilBranchConfig<'a> {
pub format: &'a str,
pub symbol: &'a str,
pub style: &'a str,
pub truncation_length: i64,
pub truncation_symbol: &'a str,
pub disabled: bool,
}
impl<'a> Default for FossilBranchConfig<'a> {
fn default() -> Self {
FossilBranchConfig {
format: "on [$symbol$branch]($style) ",
symbol: "",
style: "bold purple",
truncation_length: std::i64::MAX,
truncation_symbol: "",
disabled: true,
}
}
}

View File

@ -27,6 +27,7 @@ pub mod env_var;
pub mod erlang;
pub mod fennel;
pub mod fill;
pub mod fossil_branch;
pub mod gcloud;
pub mod git_branch;
pub mod git_commit;
@ -155,6 +156,8 @@ pub struct FullConfig<'a> {
#[serde(borrow)]
fill: fill::FillConfig<'a>,
#[serde(borrow)]
fossil_branch: fossil_branch::FossilBranchConfig<'a>,
#[serde(borrow)]
gcloud: gcloud::GcloudConfig<'a>,
#[serde(borrow)]
git_branch: git_branch::GitBranchConfig<'a>,

View File

@ -38,6 +38,7 @@ pub const PROMPT_ORDER: &[&str] = &[
"kubernetes",
"directory",
"vcsh",
"fossil_branch",
"git_branch",
"git_commit",
"git_state",

View File

@ -34,6 +34,7 @@ pub const ALL_MODULES: &[&str] = &[
"erlang",
"fennel",
"fill",
"fossil_branch",
"gcloud",
"git_branch",
"git_commit",

View File

@ -0,0 +1,215 @@
use super::{Context, Module, ModuleConfig};
use crate::configs::fossil_branch::FossilBranchConfig;
use crate::formatter::StringFormatter;
use crate::modules::utils::truncate::truncate_text;
/// Creates a module with the Fossil branch of the check-out in the current directory
///
/// Will display the branch name if the current directory is a Fossil check-out
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("fossil_branch");
let config = FossilBranchConfig::try_load(module.config);
// As we default to disabled=true, we have to check here after loading our config module,
// before it was only checking against whatever is in the config starship.toml
if config.disabled {
return None;
};
let is_checkout = context
.try_begin_scan()?
.set_files(&[".fslckout"])
.is_match();
if !is_checkout {
return None;
}
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 truncated_branch_name = {
let output = context.exec_cmd("fossil", &["branch", "current"])?.stdout;
truncate_text(output.trim(), len, config.truncation_symbol)
};
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
formatter
.map_meta(|variable, _| match variable {
"symbol" => Some(config.symbol),
_ => None,
})
.map_style(|variable| match variable {
"style" => Some(Ok(config.style)),
_ => None,
})
.map(|variable| match variable {
"branch" => Some(Ok(truncated_branch_name.as_str())),
_ => None,
})
.parse(None, Some(context))
});
module.set_segments(match parsed {
Ok(segments) => segments,
Err(error) => {
log::warn!("Error in module `fossil_branch`:\n{}", error);
return None;
}
});
Some(module)
}
#[cfg(test)]
mod tests {
use std::io;
use std::path::Path;
use nu_ansi_term::{Color, Style};
use crate::test::{fixture_repo, FixtureProvider, ModuleRenderer};
enum Expect<'a> {
BranchName(&'a str),
Empty,
NoTruncation,
Symbol(&'a str),
Style(Style),
TruncationSymbol(&'a str),
}
#[test]
fn show_nothing_on_empty_dir() -> io::Result<()> {
let checkout_dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("fossil_branch")
.path(checkout_dir.path())
.collect();
let expected = None;
assert_eq!(expected, actual);
checkout_dir.close()
}
#[test]
fn test_fossil_branch_disabled_per_default() -> io::Result<()> {
let tempdir = fixture_repo(FixtureProvider::Fossil)?;
let checkout_dir = tempdir.path();
expect_fossil_branch_with_config(
checkout_dir,
Some(toml::toml! {
// no "disabled=false" in config!
[fossil_branch]
truncation_length = 14
}),
&[Expect::Empty],
);
tempdir.close()
}
#[test]
fn test_fossil_branch_autodisabled() -> io::Result<()> {
let tempdir = tempfile::tempdir()?;
expect_fossil_branch_with_config(tempdir.path(), None, &[Expect::Empty]);
tempdir.close()
}
#[test]
fn test_fossil_branch() -> io::Result<()> {
let tempdir = fixture_repo(FixtureProvider::Fossil)?;
let checkout_dir = tempdir.path();
run_fossil(&["branch", "new", "topic-branch", "trunk"], checkout_dir)?;
run_fossil(&["update", "topic-branch"], checkout_dir)?;
expect_fossil_branch_with_config(
checkout_dir,
None,
&[Expect::BranchName("topic-branch"), Expect::NoTruncation],
);
tempdir.close()
}
#[test]
fn test_fossil_branch_configured() -> io::Result<()> {
let tempdir = fixture_repo(FixtureProvider::Fossil)?;
let checkout_dir = tempdir.path();
run_fossil(&["branch", "new", "topic-branch", "trunk"], checkout_dir)?;
run_fossil(&["update", "topic-branch"], checkout_dir)?;
expect_fossil_branch_with_config(
checkout_dir,
Some(toml::toml! {
[fossil_branch]
style = "underline blue"
symbol = "F "
truncation_length = 10
truncation_symbol = "%"
disabled = false
}),
&[
Expect::BranchName("topic-bran"),
Expect::Style(Color::Blue.underline()),
Expect::Symbol("F"),
Expect::TruncationSymbol("%"),
],
);
tempdir.close()
}
fn expect_fossil_branch_with_config(
checkout_dir: &Path,
config: Option<toml::Table>,
expectations: &[Expect],
) {
let actual = ModuleRenderer::new("fossil_branch")
.path(checkout_dir.to_str().unwrap())
.config(config.unwrap_or_else(|| {
toml::toml! {
[fossil_branch]
disabled = false
}
}))
.collect();
let mut expect_branch_name = "trunk";
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!(None, actual);
return;
}
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 = Some(format!(
"on {} ",
expect_style.paint(format!(
"{expect_symbol} {expect_branch_name}{expect_truncation_symbol}"
))
));
assert_eq!(expected, actual);
}
fn run_fossil(args: &[&str], _checkout_dir: &Path) -> io::Result<()> {
crate::utils::mock_cmd("fossil", args).ok_or(io::ErrorKind::Unsupported)?;
Ok(())
}
}

View File

@ -24,6 +24,7 @@ mod env_var;
mod erlang;
mod fennel;
mod fill;
mod fossil_branch;
mod gcloud;
mod git_branch;
mod git_commit;
@ -126,6 +127,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
"env_var" => env_var::module(None, context),
"fennel" => fennel::module(context),
"fill" => fill::module(context),
"fossil_branch" => fossil_branch::module(context),
"gcloud" => gcloud::module(context),
"git_branch" => git_branch::module(context),
"git_commit" => git_commit::module(context),
@ -239,6 +241,7 @@ pub fn description(module: &str) -> &'static str {
"erlang" => "Current OTP version",
"fennel" => "The currently installed version of Fennel",
"fill" => "Fills the remaining space on the line with a pad string",
"fossil_branch" => "The active branch of the check-out in your current directory",
"gcloud" => "The current GCP client configuration",
"git_branch" => "The active branch of the repo in your current directory",
"git_commit" => "The active commit (and tag if any) of the repo in your current directory",

View File

@ -167,6 +167,7 @@ impl<'a> ModuleRenderer<'a> {
#[derive(Clone, Copy)]
pub enum FixtureProvider {
Fossil,
Git,
Hg,
Pijul,
@ -174,6 +175,15 @@ pub enum FixtureProvider {
pub fn fixture_repo(provider: FixtureProvider) -> io::Result<TempDir> {
match provider {
FixtureProvider::Fossil => {
let path = tempfile::tempdir()?;
fs::OpenOptions::new()
.create(true)
.write(true)
.open(path.path().join(".fslckout"))?
.sync_all()?;
Ok(path)
}
FixtureProvider::Git => {
let path = tempfile::tempdir()?;

View File

@ -249,6 +249,18 @@ Elixir 1.10 (compiled with Erlang/OTP 22)\n",
stdout: String::from("Fennel 1.2.1 on PUC Lua 5.4\n"),
stderr: String::default(),
}),
"fossil branch current" => Some(CommandOutput{
stdout: String::from("topic-branch"),
stderr: String::default(),
}),
"fossil branch new topic-branch trunk" => Some(CommandOutput{
stdout: String::default(),
stderr: String::default(),
}),
"fossil update topic-branch" => Some(CommandOutput{
stdout: String::default(),
stderr: String::default(),
}),
"go version" => Some(CommandOutput {
stdout: String::from("go version go1.12.1 linux/amd64\n"),
stderr: String::default(),