summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Knaack <davidkna@users.noreply.github.com>2022-03-07 04:18:23 +0100
committerGitHub <noreply@github.com>2022-03-06 21:18:23 -0600
commitd2366ddb9cf6d3ec288fc6aafd64edf2cef4d06d (patch)
treeab481d61af365ff81ae5b7fd61d4aee80f4d1746
parent6f1d19470e4e56ae46b38b8775a6c89c346c42b8 (diff)
perf(git_status): add option to use windows starship to render in wsl (#2146)
-rw-r--r--docs/config/README.md51
-rw-r--r--src/config.rs28
-rw-r--r--src/configs/git_status.rs3
-rw-r--r--src/modules/git_status.rs108
4 files changed, 161 insertions, 29 deletions
diff --git a/docs/config/README.md b/docs/config/README.md
index b56a01ead..7ceb20c74 100644
--- a/docs/config/README.md
+++ b/docs/config/README.md
@@ -1480,25 +1480,33 @@ format = '[+$added]($added_style)/[-$deleted]($deleted_style) '
The `git_status` module shows symbols representing the state of the repo in your
current directory.
+::: tip
+
+The Git Status module is very slow in Windows directories (for example under `/mnt/c/`) when in a WSL environment.
+You can disable the module or use the `windows_starship` option to use a Windows-native Starship executable to compute `git_status` for those paths.
+
+:::
+
### Options
-| Option | Default | Description |
-| ------------------- | --------------------------------------------- | ----------------------------------- |
-| `format` | `'([\[$all_status$ahead_behind\]]($style) )'` | The default format for `git_status` |
-| `conflicted` | `"="` | This branch has merge conflicts. |
-| `ahead` | `"⇡"` | The format of `ahead` |
-| `behind` | `"⇣"` | The format of `behind` |
-| `diverged` | `"⇕"` | The format of `diverged` |
-| `up_to_date` | `""` | The format of `up_to_date` |
-| `untracked` | `"?"` | The format of `untracked` |
-| `stashed` | `"$"` | The format of `stashed` |
-| `modified` | `"!"` | The format of `modified` |
-| `staged` | `"+"` | The format of `staged` |
-| `renamed` | `"»"` | The format of `renamed` |
-| `deleted` | `"✘"` | The format of `deleted` |
-| `style` | `"bold red"` | The style for the module. |
-| `ignore_submodules` | `false` | Ignore changes to submodules. |
-| `disabled` | `false` | Disables the `git_status` module. |
+| Option | Default | Description |
+| ------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
+| `format` | `'([\[$all_status$ahead_behind\]]($style) )'` | The default format for `git_status` |
+| `conflicted` | `"="` | This branch has merge conflicts. |
+| `ahead` | `"⇡"` | The format of `ahead` |
+| `behind` | `"⇣"` | The format of `behind` |
+| `diverged` | `"⇕"` | The format of `diverged` |
+| `up_to_date` | `""` | The format of `up_to_date` |
+| `untracked` | `"?"` | The format of `untracked` |
+| `stashed` | `"$"` | The format of `stashed` |
+| `modified` | `"!"` | The format of `modified` |
+| `staged` | `"+"` | The format of `staged` |
+| `renamed` | `"»"` | The format of `renamed` |
+| `deleted` | `"✘"` | The format of `deleted` |
+| `style` | `"bold red"` | The style for the module. |
+| `ignore_submodules` | `false` | Ignore changes to submodules. |
+| `disabled` | `false` | Disables the `git_status` module. |
+| `windows_starship` | | Use this (Linux) path to a Windows Starship executable to render `git_status` when on Windows paths in WSL. |
### Variables
@@ -1562,6 +1570,15 @@ diverged = "⇕⇡${ahead_count}⇣${behind_count}"
behind = "⇣${count}"
```
+Use Windows Starship executable on Windows paths in WSL
+
+```toml
+# ~/.config/starship.toml
+
+[git_status]
+windows_starship = '/mnt/c/Users/username/scoop/apps/starship/current/starship.exe'
+```
+
## Go
The `golang` module shows the currently installed version of [Go](https://golang.org/).
diff --git a/src/config.rs b/src/config.rs
index 83bccb7e8..bd2eae4ff 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -225,6 +225,21 @@ pub struct StarshipConfig {
pub config: Option<Value>,
}
+pub fn get_config_path() -> Option<String> {
+ if let Ok(path) = env::var("STARSHIP_CONFIG") {
+ // Use $STARSHIP_CONFIG as the config path if available
+ log::debug!("STARSHIP_CONFIG is set: {}", &path);
+ Some(path)
+ } else {
+ // Default to using ~/.config/starship.toml
+ log::debug!("STARSHIP_CONFIG is not set");
+ let config_path = utils::home_dir()?.join(".config/starship.toml");
+ let config_path_str = config_path.to_str()?.to_owned();
+ log::debug!("Using default config path: {}", config_path_str);
+ Some(config_path_str)
+ }
+}
+
impl StarshipConfig {
/// Initialize the Config struct
pub fn initialize() -> Self {
@@ -241,18 +256,7 @@ impl StarshipConfig {
/// Create a config from a starship configuration file
fn config_from_file() -> Option<Value> {
- let file_path = if let Ok(path) = env::var("STARSHIP_CONFIG") {
- // Use $STARSHIP_CONFIG as the config path if available
- log::debug!("STARSHIP_CONFIG is set: {}", &path);
- path
- } else {
- // Default to using ~/.config/starship.toml
- log::debug!("STARSHIP_CONFIG is not set");
- let config_path = utils::home_dir()?.join(".config/starship.toml");
- let config_path_str = config_path.to_str()?.to_owned();
- log::debug!("Using default config path: {}", config_path_str);
- config_path_str
- };
+ let file_path = get_config_path()?;
let toml_content = match utils::read_file(&file_path) {
Ok(content) => {
diff --git a/src/configs/git_status.rs b/src/configs/git_status.rs
index 8a0140445..78186db3d 100644
--- a/src/configs/git_status.rs
+++ b/src/configs/git_status.rs
@@ -20,6 +20,8 @@ pub struct GitStatusConfig<'a> {
pub untracked: &'a str,
pub ignore_submodules: bool,
pub disabled: bool,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub windows_starship: Option<&'a str>,
}
impl<'a> Default for GitStatusConfig<'a> {
@@ -40,6 +42,7 @@ impl<'a> Default for GitStatusConfig<'a> {
untracked: "?",
ignore_submodules: false,
disabled: false,
+ windows_starship: None,
}
}
}
diff --git a/src/modules/git_status.rs b/src/modules/git_status.rs
index db02083f5..c7291e101 100644
--- a/src/modules/git_status.rs
+++ b/src/modules/git_status.rs
@@ -34,6 +34,13 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
//Return None if not in git repository
context.get_repo().ok()?;
+ if let Some(git_status) = git_status_wsl(context, &config) {
+ if git_status.is_empty() {
+ return None;
+ }
+ module.set_segments(Segment::from_text(None, git_status));
+ return Some(module);
+ }
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
formatter
@@ -354,6 +361,107 @@ fn format_symbol(format_str: &str, config_path: &str, context: &Context) -> Opti
format_text(format_str, config_path, context, |_variable| None)
}
+#[cfg(target_os = "linux")]
+fn git_status_wsl(context: &Context, conf: &GitStatusConfig) -> Option<String> {
+ use crate::utils::create_command;
+ use nix::sys::utsname::uname;
+ use std::env;
+ use std::io::ErrorKind;
+
+ let starship_exe = conf.windows_starship?;
+
+ // Ensure this is WSL
+ // This is lowercase in WSL1 and uppercase in WSL2, just skip the first letter
+ if !uname().release().contains("icrosoft") {
+ return None;
+ }
+
+ log::trace!("Using WSL mode");
+
+ // Get Windows path
+ let winpath = match create_command("wslpath")
+ .map(|mut c| {
+ c.arg("-w").arg(&context.current_dir);
+ c
+ })
+ .and_then(|mut c| c.output())
+ {
+ Ok(r) => r,
+ Err(e) => {
+ // Not found might means this might not be WSL after all
+ let level = if e.kind() == ErrorKind::NotFound {
+ log::Level::Debug
+ } else {
+ log::Level::Error
+ };
+
+ log::log!(level, "Failed to get Windows path:\n{:?}", e);
+
+ return None;
+ }
+ };
+
+ let winpath = match std::str::from_utf8(&winpath.stdout) {
+ Ok(r) => r.trim_end(),
+ Err(e) => {
+ log::error!("Failed to parse Windows path:\n{:?}", e);
+
+ return None;
+ }
+ };
+
+ log::trace!("Windows path: {}", winpath);
+
+ // In Windows or Linux dir?
+ if winpath.starts_with(r"\\wsl") {
+ log::trace!("Not a Windows path");
+ return None;
+ }
+
+ // Get foreign starship to use WSL config
+ // https://devblogs.microsoft.com/commandline/share-environment-vars-between-wsl-and-windows/
+ let wslenv = env::var("WSLENV")
+ .map(|e| e + ":STARSHIP_CONFIG/wp")
+ .unwrap_or_else(|_| "STARSHIP_CONFIG/wp".to_string());
+
+ let out = match create_command(starship_exe)
+ .map(|mut c| {
+ c.env(
+ "STARSHIP_CONFIG",
+ crate::config::get_config_path().unwrap_or_else(|| "/dev/null".to_string()),
+ )
+ .env("WSLENV", wslenv)
+ .args(&["module", "git_status", "--path", winpath]);
+ c
+ })
+ .and_then(|mut c| c.output())
+ {
+ Ok(r) => r,
+ Err(e) => {
+ log::error!("Failed to run Git Status module on Windows:\n{}", e);
+
+ return None;
+ }
+ };
+
+ match String::from_utf8(out.stdout) {
+ Ok(r) => Some(r),
+ Err(e) => {
+ log::error!(
+ "Failed to parse Windows Git Status module status output:\n{}",
+ e
+ );
+
+ None
+ }
+ }
+}
+
+#[cfg(not(target_os = "linux"))]
+fn git_status_wsl(_context: &Context, _conf: &GitStatusConfig) -> Option<String> {
+ None
+}
+
#[cfg(test)]
mod tests {
use ansi_term::{ANSIStrings, Color};