starcraftman / zsh-git-prompt

[Active Fork] Informative git prompt for zsh
MIT License
58 stars 15 forks source link

WIP: Git super status as template string #40

Open NAR8789 opened 5 years ago

NAR8789 commented 5 years ago

NOTE: this builds on top of some unit testing brought in in #39, and that PR is generally less weird than this one, so you should probably read #39 first.

This PR is an attempt (possibly misguided) at allowing "template-string"-like customization of the git prompt.

For example, here is the original theme in template-string form:

'$ZSH_THEME_GIT_PROMPT_PREFIX$branch$merge_or_rebase$upstream${${:-$behind$ahead}:+ }$behind$ahead$ZSH_THEME_GIT_PROMPT_SEPARATOR$staged$conflicts$changed$untracked$stashed$clean$ZSH_THEME_GIT_PROMPT_SUFFIX'

Here is an example retheming involving custom template string (template string is the last line)

ZSH_GIT_PROMPT_SHOW_UPSTREAM=2
ZSH_THEME_GIT_PROMPT_BEHIND="↓"
ZSH_THEME_GIT_PROMPT_AHEAD="↑"
ZSH_THEME_GIT_PROMPT_LOCAL='L'
ZSH_THEME_GIT_PROMPT_UPSTREAM_FRONT="%{$fg_bold[blue]%}"
ZSH_THEME_GIT_PROMPT_UPSTREAM_END="%{${reset_color}%}"
ZSH_THEME_GIT_PROMPT_MERGING="%{$fg_bold[yellow]%}MERGE"
ZSH_THEME_GIT_PROMPT_REBASE="%{$fg_bold[yellow]%}REBASE "

ZSH_THEME_GIT_PROMPT='(${(j:|:)${(s:|:)${:-$branch|$upstream|$merge_or_rebase|$behind$ahead$staged$conflicts$changed$untracked$stashed$clean}}})'

And here are some example prompts under that custom template

clean:                          (master|origin|✔)
basic metrics:                  (master|origin|↓2↑1●3✚5…6⚑7)
local only:                     (master|L|✔)
local only, with metrics:       (master|L|●3✚5…6⚑7)
merging:                        (master|origin|MERGE|✖1)
merging, with metrics:          (master|L|MERGE|●3✖4✚5…6⚑7)
rebasing:                       (:abc1234|origin/master|REBASE 3/10|✔)
rebasing, with metrics:         (:abc1234|origin/master|REBASE 2/7|●3✖4✚5…6⚑7)

Why?

I think template strings potentially provide an easier way to make structural modifications to the git-prompt theme, e.g. omitting or reordering pieces. Reordering by moving variables around in a template string feels a lot lighter weight than overriding git_super_status and reordering the blocks therein.

At the end of the day it's about having varying levels of granularity in configuration. The preexisting theme variables already nicely support fine-grained changes (i.e. tweaking look-and-feel in-place), so I've written this template string implementation to avoid that level of granular detail, and to leave that existing layer of customizability intact.

Template strings fill in a missing level of granularity--providing the ability to work at the level of prebuilt status blocks, e.g. ✖4, or ●3. or REBASE 2/7

In theory this might also be a nicer place to configure separators and margins, rather than using ZSH_THEME_GIT_PROMPT_PREFIX, ZSH_THEME_GIT_PROMPT_SUFFIX, and ZSH_THEME_GIT_PROMPT_SEPARATOR

Why Not?

Template strings sound nice, but in practice I find them headachey in particular for optional separators or spacers (e.g. "omit this spacer if the next section is empty"). Optional Spacers require a tiny bit of logic, and I found zsh expansions an awkward tool for this purpose.

For example, my custom template above contains optional separators logic, but it reads like a code golf solution. Here is my example custom template string again: ``` '(${(j:|:)${(s:|:)${:-$branch|$upstream|$merge_or_rebase|$behind$ahead$staged$conflicts$changed$untracked$stashed$clean}}})' ``` At a high level, the idea I'm implementing above is simple: - Take a bunch of segments, in order: `$branch`, `$upstream`, `$merge_or_rebase`, and `$behind$ahead$staged$conflicts$changed$untracked$stashed$clean` - omit the empty segments - join the remaining segments with pipes `|` as separators Zsh has built-in support for joining arrays: `${(j:|:)array_var}` will join the elements of `$array_var` with pipes `|`. You can see this at the outermost layer of my expression, just inside the literally-printed parens. The rest of the expression, `${(s:|:)${:-...}}`, is a hack to declare an anonymous array inside of a string. - `${(s:|:)string_var}` splits a string on pipes - `${:-string_expression}` is a hack, to shoehorn an arbitrary string expression into the above splitter. Otherwise, the `string_var` argument above would only take variables, _not_ arbitrary expressions. [credit to stackoverflow for this one](https://superuser.com/questions/1098551/append-string-to-array-elements-and-join-them-in-one-expression/1098608#1098608). - omitting empty segments happens automatically, because splitting behaves like argument-splitting. - Put everything together, and the result is this weird anonymous array syntax based on string operations. Note that, though this works for the current purpose, this is not fully general: - Since omitting of blanks is built-in, I'm not sure how I'd create an anonymous array that _does_ contain blanks - The current solution is also susceptible to oversplitting (e.g. if any of the nonempty segments themselves contain pipes)

Given the weirdness of zsh expansions, it feels like template strings might be hard for most people to write from scratch.

Extra Bits

As part of this, I wrote a previewer tool, to see the git status under various conditions all at once, without needing repositories to produce the correct underlying states. It's invoked as preview_git_super_status, and lives in a separate file so as not to clutter the main logic. It avoids polluting the environment by doing all its work in a subshell.