summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJamie <hello@itsjamie.dev>2022-10-11 09:02:46 -0700
committerGitHub <noreply@github.com>2022-10-11 18:02:46 +0200
commit355800f8147b1755a5289dc679e2147abd662daf (patch)
tree6968e9518c983c84296e94af74e4089bedb45642
parent41070313311318f02d25b3b85fd3476b61ab8375 (diff)
feat(module): Add a meson devenv indicator (#4389)
* feat(module): Add a meson devenv indicator Adds a Meson Developer Environment indicator, if the MESON_DEVENV variable is set. Inside a `meson devenv`, the prompt will include the current Meson project name This also contains a new Truncate utility function, which may be adapted for other modules in the future * docs: Add Meson to presets
-rw-r--r--.github/config-schema.json47
-rw-r--r--docs/.vuepress/public/presets/toml/bracketed-segments.toml3
-rw-r--r--docs/.vuepress/public/presets/toml/nerd-font-symbols.toml3
-rw-r--r--docs/.vuepress/public/presets/toml/no-runtime-versions.toml3
-rw-r--r--docs/.vuepress/public/presets/toml/plain-text-symbols.toml3
-rw-r--r--docs/config/README.md40
-rw-r--r--src/configs/meson.rs30
-rw-r--r--src/configs/mod.rs3
-rw-r--r--src/configs/starship_root.rs1
-rw-r--r--src/module.rs1
-rw-r--r--src/modules/meson.rs98
-rw-r--r--src/modules/mod.rs5
-rw-r--r--src/modules/utils/mod.rs2
-rw-r--r--src/modules/utils/truncate.rs107
14 files changed, 346 insertions, 0 deletions
diff --git a/.github/config-schema.json b/.github/config-schema.json
index f92051a57..f1d97cfb7 100644
--- a/.github/config-schema.json
+++ b/.github/config-schema.json
@@ -864,6 +864,21 @@
}
]
},
+ "meson": {
+ "default": {
+ "disabled": false,
+ "format": "via [$symbol$project]($style) ",
+ "style": "blue bold",
+ "symbol": "⬢ ",
+ "truncation_length": 4294967295,
+ "truncation_symbol": "…"
+ },
+ "allOf": [
+ {
+ "$ref": "#/definitions/MesonConfig"
+ }
+ ]
+ },
"nim": {
"default": {
"detect_extensions": [
@@ -3600,6 +3615,38 @@
},
"additionalProperties": false
},
+ "MesonConfig": {
+ "type": "object",
+ "properties": {
+ "truncation_length": {
+ "default": 4294967295,
+ "type": "integer",
+ "format": "uint32",
+ "minimum": 0.0
+ },
+ "truncation_symbol": {
+ "default": "…",
+ "type": "string"
+ },
+ "format": {
+ "default": "via [$symbol$project]($style) ",
+ "type": "string"
+ },
+ "symbol": {
+ "default": "⬢ ",
+ "type": "string"
+ },
+ "style": {
+ "default": "blue bold",
+ "type": "string"
+ },
+ "disabled": {
+ "default": false,
+ "type": "boolean"
+ }
+ },
+ "additionalProperties": false
+ },
"NimConfig": {
"type": "object",
"properties": {
diff --git a/docs/.vuepress/public/presets/toml/bracketed-segments.toml b/docs/.vuepress/public/presets/toml/bracketed-segments.toml
index 76a7f382e..51ea664a9 100644
--- a/docs/.vuepress/public/presets/toml/bracketed-segments.toml
+++ b/docs/.vuepress/public/presets/toml/bracketed-segments.toml
@@ -85,6 +85,9 @@ format = '\[[$symbol($version)]($style)\]'
[memory_usage]
format = '\[$symbol[$ram( | $swap)]($style)\]'
+[meson]
+format = '\[[$symbol$project]($style)\]'
+
[nim]
format = '\[[$symbol($version)]($style)\]'
diff --git a/docs/.vuepress/public/presets/toml/nerd-font-symbols.toml b/docs/.vuepress/public/presets/toml/nerd-font-symbols.toml
index 1c62e0a14..b31685dc0 100644
--- a/docs/.vuepress/public/presets/toml/nerd-font-symbols.toml
+++ b/docs/.vuepress/public/presets/toml/nerd-font-symbols.toml
@@ -49,6 +49,9 @@ symbol = " "
[memory_usage]
symbol = " "
+[meson]
+symbol = "喝 "
+
[nim]
symbol = " "
diff --git a/docs/.vuepress/public/presets/toml/no-runtime-versions.toml b/docs/.vuepress/public/presets/toml/no-runtime-versions.toml
index 156c4e762..0cea589c8 100644
--- a/docs/.vuepress/public/presets/toml/no-runtime-versions.toml
+++ b/docs/.vuepress/public/presets/toml/no-runtime-versions.toml
@@ -49,6 +49,9 @@ format = 'via [$symbol]($style)'
[lua]
format = 'via [$symbol]($style)'
+[meson]
+format = 'via [$symbol]($style)'
+
[nim]
format = 'via [$symbol]($style)'
diff --git a/docs/.vuepress/public/presets/toml/plain-text-symbols.toml b/docs/.vuepress/public/presets/toml/plain-text-symbols.toml
index 05b23720e..d152423ca 100644
--- a/docs/.vuepress/public/presets/toml/plain-text-symbols.toml
+++ b/docs/.vuepress/public/presets/toml/plain-text-symbols.toml
@@ -85,6 +85,9 @@ symbol = "nodejs "
[memory_usage]
symbol = "memory "
+[meson]
+symbol = "meson "
+
[nim]
symbol = "nim "
diff --git a/docs/config/README.md b/docs/config/README.md
index 2f58ee355..36a08d38a 100644
--- a/docs/config/README.md
+++ b/docs/config/README.md
@@ -278,6 +278,7 @@ $zig\
$buf\
$nix_shell\
$conda\
+$meson\
$spack\
$memory_usage\
$aws\
@@ -2384,6 +2385,45 @@ symbol = " "
style = "bold dimmed green"
```
+## Meson
+
+The `meson` module shows the current Meson developer environment status.
+
+By default the Meson project name is displayed, if `$MESON_DEVENV` is set.
+
+### Options
+
+| Option | Default | Description |
+| ------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------- |
+| `truncation_length` | `2^32 - 1` | Truncates a project name to `N` graphemes. |
+| `truncation_symbol` | `"…"` | The symbol used to indicate a project name was truncated. You can use `""` for no symbol. |
+| `format` | `"via [$symbol$project]($style) "` | The format for the module. |
+| `symbol` | `"⬢ "` | The symbol used before displaying the project name. |
+| `style` | `"blue bold"` | The style for the module. |
+| `disabled` | `false` | Disables the `meson` module. |
+
+### Variables
+
+| Variable | Example | Description |
+| -------- | ---------- | ------------------------------------ |
+| project | `starship` | The current Meson project name |
+| 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
+
+[meson]
+disabled = false
+truncation_symbol = "--"
+symbol = " "
+style = "bold dimmed green"
+```
+
## Mercurial Branch
The `hg_branch` module shows the active branch of the repo in your current directory.
diff --git a/src/configs/meson.rs b/src/configs/meson.rs
new file mode 100644
index 000000000..0325c3f56
--- /dev/null
+++ b/src/configs/meson.rs
@@ -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 MesonConfig<'a> {
+ pub truncation_length: u32,
+ pub truncation_symbol: &'a str,
+ pub format: &'a str,
+ pub symbol: &'a str,
+ pub style: &'a str,
+ pub disabled: bool,
+}
+
+impl<'a> Default for MesonConfig<'a> {
+ fn default() -> Self {
+ MesonConfig {
+ truncation_length: std::u32::MAX,
+ truncation_symbol: "…",
+ format: "via [$symbol$project]($style) ",
+ symbol: "⬢ ",
+ style: "blue bold",
+ disabled: false,
+ }
+ }
+}
diff --git a/src/configs/mod.rs b/src/configs/mod.rs
index ec47d5012..e767d0d81 100644
--- a/src/configs/mod.rs
+++ b/src/configs/mod.rs
@@ -46,6 +46,7 @@ pub mod line_break;
pub mod localip;
pub mod lua;
pub mod memory_usage;
+pub mod meson;
pub mod nim;
pub mod nix_shell;
pub mod nodejs;
@@ -184,6 +185,8 @@ pub struct FullConfig<'a> {
#[serde(borrow)]
memory_usage: memory_usage::MemoryConfig<'a>,
#[serde(borrow)]
+ meson: meson::MesonConfig<'a>,
+ #[serde(borrow)]
nim: nim::NimConfig<'a>,
#[serde(borrow)]
nix_shell: nix_shell::NixShellConfig<'a>,
diff --git a/src/configs/starship_root.rs b/src/configs/starship_root.rs
index aeb03358a..6cc4da77b 100644
--- a/src/configs/starship_root.rs
+++ b/src/configs/starship_root.rs
@@ -87,6 +87,7 @@ pub const PROMPT_ORDER: &[&str] = &[
"buf",
"nix_shell",
"conda",
+ "meson",
"spack",
"memory_usage",
"aws",
diff --git a/src/module.rs b/src/module.rs
index c83947e24..c23e9435d 100644
--- a/src/module.rs
+++ b/src/module.rs
@@ -54,6 +54,7 @@ pub const ALL_MODULES: &[&str] = &[
"localip",
"lua",
"memory_usage",
+ "meson",
"nim",
"nix_shell",
"nodejs",
diff --git a/src/modules/meson.rs b/src/modules/meson.rs
new file mode 100644
index 000000000..afbedcb5a
--- /dev/null
+++ b/src/modules/meson.rs
@@ -0,0 +1,98 @@
+use super::{Context, Module, ModuleConfig};
+
+use super::utils::truncate::truncate_text;
+use crate::configs::meson::MesonConfig;
+use crate::formatter::StringFormatter;
+
+/// Creates a module with the current Meson dev environment
+///
+/// Will display the Meson environment if `$MESON_DEVENV` and `MESON_PROJECT_NAME` are set.
+pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
+ let meson_env = context.get_env("MESON_DEVENV")?;
+ let project_env = context.get_env("MESON_PROJECT_NAME")?;
+ if meson_env != "1" || project_env.trim().is_empty() {
+ return None;
+ }
+
+ let mut module = context.new_module("meson");
+ let config: MesonConfig = MesonConfig::try_load(module.config);
+
+ let truncated_text = truncate_text(
+ &project_env,
+ config.truncation_length as usize,
+ 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 {
+ "project" => Some(Ok(&truncated_text)),
+ _ => None,
+ })
+ .parse(None, Some(context))
+ });
+
+ module.set_segments(match parsed {
+ Ok(segments) => segments,
+ Err(error) => {
+ log::warn!("Error in module `meson`:\n{}", error);
+ return None;
+ }
+ });
+
+ Some(module)
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::test::ModuleRenderer;
+ use nu_ansi_term::Color;
+
+ #[test]
+ fn not_in_env() {
+ let actual = ModuleRenderer::new("meson").collect();
+
+ let expected = None;
+
+ assert_eq!(expected, actual);
+ }
+
+ #[test]
+ fn env_set() {
+ let actual = ModuleRenderer::new("meson")
+ .env("MESON_DEVENV", "1")
+ .env("MESON_PROJECT_NAME", "starship")
+ .collect();
+
+ let expected = Some(format!("via {} ", Color::Blue.bold().paint("⬢ starship")));
+
+ assert_eq!(expected, actual);
+ }
+
+ #[test]
+ fn env_invalid_devenv() {
+ let actual = ModuleRenderer::new("meson")
+ .env("MESON_DEVENV", "0")
+ .env("MESON_PROJECT_NAME", "starship")
+ .collect();
+ let expected = None;
+ assert_eq!(expected, actual);
+ }
+ #[test]
+ fn env_invalid_project_name() {
+ let actual = ModuleRenderer::new("meson")
+ .env("MESON_DEVENV", "1")
+ .env("MESON_PROJECT_NAME", " ")
+ .collect();
+ let expected = None;
+ assert_eq!(expected, actual);
+ }
+}
diff --git a/src/modules/mod.rs b/src/modules/mod.rs
index b6575281b..6626e1c77 100644
--- a/src/modules/mod.rs
+++ b/src/modules/mod.rs
@@ -43,6 +43,7 @@ mod line_break;
mod localip;
mod lua;
mod memory_usage;
+mod meson;
mod nim;
mod nix_shell;
mod nodejs;
@@ -137,6 +138,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
"localip" => localip::module(context),
"lua" => lua::module(context),
"memory_usage" => memory_usage::module(context),
+ "meson" => meson::module(context),
"nim" => nim::module(context),
"nix_shell" => nix_shell::module(context),
"nodejs" => nodejs::module(context),
@@ -242,6 +244,9 @@ pub fn description(module: &str) -> &'static str {
"localip" => "The currently assigned ipv4 address",
"lua" => "The currently installed version of Lua",
"memory_usage" => "Current system memory and swap usage",
+ "meson" => {
+ "The current Meson environment, if $MESON_DEVENV and $MESON_PROJECT_NAME are set"
+ }
"nim" => "The currently installed version of Nim",
"nix_shell" => "The nix-shell environment",
"nodejs" => "The currently installed version of NodeJS",
diff --git a/src/modules/utils/mod.rs b/src/modules/utils/mod.rs
index 1d509c14b..209da8ed6 100644
--- a/src/modules/utils/mod.rs
+++ b/src/modules/utils/mod.rs
@@ -7,3 +7,5 @@ pub mod directory_win;
pub mod directory_nix;
pub mod path;
+
+pub mod truncate;
diff --git a/src/modules/utils/truncate.rs b/src/modules/utils/truncate.rs
new file mode 100644
index 000000000..9ec920cb6
--- /dev/null
+++ b/src/modules/utils/truncate.rs
@@ -0,0 +1,107 @@
+use unicode_segmentation::UnicodeSegmentation;
+
+/// Truncate a string to only have a set number of characters
+///
+/// Will truncate a string to only show the last `length` character in the string.
+/// If a length of `0` is provided, the string will not be truncated and the original
+/// will be returned.
+pub fn truncate_text(text: &str, length: usize, truncation_symbol: &str) -> String {
+ if length == 0 {
+ return String::from(text);
+ }
+
+ let truncated_graphemes = get_graphemes(text, length);
+ // The truncation symbol should only be added if we truncated
+ let truncated_and_symbol = if length < graphemes_len(text) {
+ let truncation_symbol = get_graphemes(truncation_symbol, 1);
+ truncated_graphemes + truncation_symbol.as_str()
+ } else {
+ truncated_graphemes
+ };
+
+ truncated_and_symbol
+}
+
+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()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_multi_char_truncation_symbol() {
+ let actual = truncate_text("1337_hello_world", 15, "apple");
+
+ assert_eq!("1337_hello_worla", actual);
+ }
+
+ #[test]
+ fn test_changed_truncation_symbol() {
+ test_truncate_length("1337_hello_world", 15, "1337_hello_worl", "%")
+ }
+
+ #[test]
+ fn test_no_truncation_symbol() {
+ test_truncate_length("1337_hello_world", 15, "1337_hello_worl", "")
+ }
+
+ #[test]
+ fn test_ascii_boundary_below() {
+ test_truncate_length("1337_hello_world", 15, "1337_hello_worl", "…")
+ }
+
+ #[test]
+ fn test_ascii_boundary_on() {
+ test_truncate_length("1337_hello_world", 16, "1337_hello_world", "")
+ }
+
+ #[test]
+ fn test_ascii_boundary_above() {
+ test_truncate_length("1337_hello_world", 17, "1337_hello_world", "")
+ }
+
+ #[test]
+ fn test_one() {
+ test_truncate_length("1337_hello_world", 1, "1", "…")
+ }
+
+ #[test]
+ fn test_negative() {
+ test_truncate_length("1337_hello_world", -1, "1337_hello_world", "")
+ }
+
+ #[test]
+ fn test_hindi_truncation() {
+ test_truncate_length("नमस्ते", 3, "नमस्", "…")
+ }
+
+ #[test]
+ fn test_hindi_truncation2() {
+ test_truncate_length("नमस्त", 3, "नमस्", "…")
+ }
+
+ #[test]
+ fn test_japanese_truncation() {
+ test_truncate_length("がんばってね", 4, "がんばっ", "…")
+ }
+
+ fn test_truncate_length(
+ text: &str,
+ truncate_length: i64,
+ expected: &str,
+ truncation_symbol: &str,
+ ) {
+ let actual = truncate_text(text, truncate_length as usize, truncation_symbol);
+
+ assert_eq!(format!("{}{}", expected, truncation_symbol), actual);
+ }
+}