From 97b9c1202ab4a9362dabf6f3c06563a2c4cf6a49 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Mon, 5 Sep 2022 18:08:40 -0400 Subject: [PATCH] Async git prompt (#288) --- prompt/async_git_prompt/README.md | 28 ++++++++ prompt/async_git_prompt/async-git-prompt.nu | 77 +++++++++++++++++++++ prompt/async_git_prompt/prompt.nu | 30 ++++++++ 3 files changed, 135 insertions(+) create mode 100644 prompt/async_git_prompt/README.md create mode 100644 prompt/async_git_prompt/async-git-prompt.nu create mode 100644 prompt/async_git_prompt/prompt.nu diff --git a/prompt/async_git_prompt/README.md b/prompt/async_git_prompt/README.md new file mode 100644 index 0000000..11f707b --- /dev/null +++ b/prompt/async_git_prompt/README.md @@ -0,0 +1,28 @@ +This module exports commands for creating a nushell prompt that computes git status (staged and +unstaged changes) asynchronously. This can be useful in large git repos when it is slow to obtain +this information synchronously. + +To use this module: + +0. Place the file `async-git-prompt.nu` in the `$nu.config-path` directory (this is the directory + containing `init.nu` and `env.nu`). + +1. Use the command `async-git-prompt-string` in your own `PROMPT_COMMAND` (the file prompt.nu + contains an example of doing this.) + At this point, your prompt will be computing the information synchronously, because the cache + file does not yet exist. + +2. In a repo where git is slow, run the command `async-git-prompt-refresh-cache`. + Now, your prompt will be fast, but it also won't update automatically. You could investigate a good + way to invalidate the cache automatically, but the manual alternative is: + +3. Whenever you think your prompt might be stale, re-run the command `async-git-prompt-refresh-cache`. + Your prompt will update on one of the next times that you hit . + +4. It will probably be convenient to alias this, e.g. + + ```nu + alias r = async-git-prompt-refresh-cache + ``` + +5. To go back to synchronous mode, run `async-git-prompt-delete-cache`. diff --git a/prompt/async_git_prompt/async-git-prompt.nu b/prompt/async_git_prompt/async-git-prompt.nu new file mode 100644 index 0000000..1447c3e --- /dev/null +++ b/prompt/async_git_prompt/async-git-prompt.nu @@ -0,0 +1,77 @@ +# This module exports commands for creating a nushell prompt that computes git status (staged and +# unstaged changes) asynchronously. This can be useful in large git repos when it is slow to obtain +# this information synchronously. + +# See README.md for usage. + +def unstaged-symbol [] { 'અ' } +def staged-symbol [] { 'જ' } +def in-progress-symbol [] { '…' } +def cached-result-symbol [] { $"·" } #〈 +def cache-file [] { '.nu-async-git-prompt-cache'} + +def do-async [commands: string] { + bash -c $"nu -c '($commands)' &" +} + +export def async-git-prompt-string [] { + let cache_path = (cache-path) + if ($cache_path | empty?) { + "" + } else if ($cache_path | path exists) { + $"(cached-result-symbol)(open $cache_path | str trim)" + } else { + async-git-prompt-compute-sync + } +} + +export def async-git-prompt-compute-sync [] { + let unstaged = { + let symbol = if ((git diff --quiet | complete).exit_code == 1) { + (unstaged-symbol) + } else { + '' + } + { unstaged: $symbol} + } + let staged = { + let symbol = if ((git diff --cached --quiet | complete).exit_code == 1) { + (staged-symbol) + } else { + '' + } + { staged: $symbol} + } + # Execute the two slow git commands in parallel and merge the results into a single record + let symbols = ([ $unstaged $staged ] | par-each { |it| do $it } | reduce {|a b| $a | merge {$b}}) + + $"($symbols | get 'unstaged') ($symbols | get 'staged')" | str trim +} + +export def async-git-prompt-refresh-cache [] { + let cache_path = (cache-path) + if ($cache_path != null) { + echo (in-progress-symbol) | save $cache_path + do-async $"use ($nu.config-path | path expand | path dirname)/async-git-prompt.nu *; async-git-prompt-compute-sync | save ($cache_path)" + } +} + +export def async-git-prompt-delete-cache [] { + let cache_path = (cache-path) + if ($cache_path != null) { + rm -f $cache_path + } +} + +def cache-path [] { + let dir = if ('.git' | path exists) { + '.' + } else { + do -i { git rev-parse --show-toplevel | str trim -r } + } + if ($dir | empty?) { + null + } else { + $dir | path join (cache-file) + } +} diff --git a/prompt/async_git_prompt/prompt.nu b/prompt/async_git_prompt/prompt.nu new file mode 100644 index 0000000..a8b3eda --- /dev/null +++ b/prompt/async_git_prompt/prompt.nu @@ -0,0 +1,30 @@ +# This file contains an example nushell prompt, making use of the async-git-prompt module. +use async-git-prompt.nu * + +def prompt-concat [parts: table] { + $parts + | where (not ($it.text | empty?)) + | each { |it| $"($it.color)($it.text)" } + | str collect ' ' +} + +def prompt-git-branch [] { + do -i { git rev-parse --abbrev-ref HEAD | str trim -r} +} + +def prompt-create-left-prompt [] { + let pwd = ($env.PWD | str replace $env.HOME '~') + prompt-concat [ + {text: $pwd, color: (ansi green_bold)} + {text: (prompt-git-branch), color: (ansi blue_bold)} + {text: (async-git-prompt-string), color: (ansi green_bold)} + ] +} + +def prompt-create-right-prompt [] { + $nothing +} + +let-env PROMPT_COMMAND = { prompt-create-left-prompt } +let-env PROMPT_COMMAND_RIGHT = { prompt-create-right-prompt } +let-env PROMPT_INDICATOR = { $" (ansi green_bold)〉" }