Changed . token to _dot

This change allows the dotfiles to work with chezmoi (e.g: on windows)
and improves grepability with neovim/telescope
This commit is contained in:
2024-11-07 13:52:17 +00:00
parent 83b02bd753
commit 896af887ca
2351 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
# Prompt Scripts
### Definition
These scripts should be used to draw a custom command prompt in nushell. They can include anything that we think is appropriate for prompts such as `git` commands, `starship`, `oh-my-posh`, etc.
#### starship.nu
File is in [starship](./starship.nu)
This describe how to use starship to make a leftprompt, the repo of starship is [here](https://github.com/starship/starship).
This script set the output of starship as leftprompt
![starshipshow](./images/starship.png)
#### shell_space.nu
File is in [shell_space](./shell_space.nu)
Use the function of shells in nu, you can view the function with the command following
```
help shells
```
![shell_spaceshow](./images/shell_space.png)
#### jalon-git.nu
From https://github.com/JalonWong/nushell-prompt
![jalon-git](./images/jalon-git.png)

View File

@@ -0,0 +1,33 @@
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 <enter>.
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`.
### TODO
- Automatic cache invalidation
- Show untracked files (this can be very expensive in large repos)

View File

@@ -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 | is-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 | is-empty) {
null
} else {
$dir | path join (cache-file)
}
}

View File

@@ -0,0 +1,31 @@
# 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 | is-empty))
| each { |it| $"($it.color)($it.text)" }
| str join ' '
}
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 color];
[pwd (ansi green_bold)]
[(prompt-git-branch) (ansi blue_bold)]
[(async-git-prompt-string) (ansi green_bold)]
]
}
def prompt-create-right-prompt [] {
null
}
$env.PROMPT_COMMAND = { prompt-create-left-prompt }
$env.PROMPT_COMMAND_RIGHT = { prompt-create-right-prompt }
$env.PROMPT_INDICATOR = { $" (ansi green_bold)" }

View File

@@ -0,0 +1,70 @@
# Max Brown
# a very basic git prompt
# sort of like panache-git, but fewer than 60 lines of code.
# use as below without the comments and in your
# env.nu file
# use path/to/basic-git.nu basic-git-left-prompt
# $env.PROMPT_COMMAND = {||
# let left = create_left_prompt
# basic-git-left-prompt $left
# }
def in_git_repo [] {
(do --ignore-errors { git rev-parse --abbrev-ref HEAD } | is-empty) == false
}
export def basic-git-left-prompt [in_left_prompt] {
# if we're in a repo, let's go!
let currently_in_git_repo = in_git_repo
if $currently_in_git_repo {
# get the branch info first
let branch_info = git branch -l
| lines
| filter {|e| $e | str contains "*" }
| each {|e| $e | str replace "* " "="}
| get 0
let git_status = git status -s
# get the status in short
let git_status = $git_status
| lines
| str replace -r '(.* ).*' '$1'
| sort
| uniq -c
| insert type {
|e| if ($e.value | str contains "M") {
"blue_bold"
} else if ($e.value | str contains "??") {
"yellow_bold"
} else if ($e.value | str contains "D") {
"red_bold"
} else if ($e.value | str contains "A") {
"cyan_bold"
} else ""
}
| each {
|e| $"(ansi $e.type)($e.count)($e.value | str trim)(ansi reset)"
}
| reduce --fold '' {|str all| $"($all),($str)" }
| str substring 1..
let final_git_status = if $git_status == "" {
""
} else {
$" ($git_status)"
}
# construct the prompt
$"($in_left_prompt)(ansi reset) [($branch_info)($final_git_status)]"
} else {
# otherwise just return the normal prompt
$in_left_prompt
}
}

View File

@@ -0,0 +1,512 @@
# Build a full-line prompt with widgets for:
# activated python virtual environment (from `overlay use <ve>/bin/activate.nu`)
# current working directory
# git status (branch, branch ahead/behind remote, files changed)
# current position in remembered working directories (`std dirs`, a.k.a. `shells`)
# also, as a nu dev special, widget for active nu executable (flags `cargo run` vs "installed" and which branch built from).
#
# to use:
# 1. copy this file to `($nu.default-config-dir | path add 'scripts')` (Or someplace on your $env.NU_LIB_DIRS path, defined in env.nu)
# 2. cut `$env.PROMPT_COMMAND` and `PROMPT_OMMAND_RIGHT' from your env.nu.
# These will depend on `use full-line`, which can not be done in env.nu.
# You can leave the `PROMPT-*INDICATOR*` statements in env.nu or
# consolidate all prompt stuff in config.nu.
# 3. Add new prompt setup stuff somewhere in config.nu:
# ```
# use full-line.nu
# $env.PROMPT_COMMAND = {|| full-line }
# $env.PROMPT_COMMAND_RIGHT = ""
# ```
#
# credit panache-git for the git status widget.
use std dirs
use std assert
# build the prompt from segments, looks like:
#
# ^(<py ve>) ------------ <workingDirectory> ------ <gitRepoStatus> --- <dirs>$
#
# or, if no git repo current directory
#
# ^------------------------------------ <workingDirectory> ------------ <dirs>$
export def main [
--pad_char (-p) = '-' # character to fill with
] {
let left_content = ($"(
if 'VIRTUAL_ENV_PROMPT' in $env {'(' + $env.VIRTUAL_ENV_PROMPT + ') '}
)($pad_char + $pad_char + $pad_char)(current_exe)")
let left_content_len = ($left_content | ansi strip | str length -g)
let mid_content = ($" (dir_string) ")
let mid_content_len = ($mid_content | ansi strip | str length -g)
let dirs_segment = $" |(dirs show | each {|it| if $it.active {'V'} else {'.'}} | str join '')|"
let right_content = ($"(repo-styled)($pad_char + $pad_char + $pad_char)($dirs_segment)")
let right_content_len = ($right_content | ansi strip | str length -g)
let term_width = ((term size) | get columns)
let mid_padding = ($term_width - $left_content_len - $right_content_len)
[(ansi reset),
$left_content,
($mid_content | fill --character $pad_char --width $mid_padding --alignment center),
$right_content,
"\n"
] | str join ''
}
# build current exe widget
def current_exe [] {
let content = ([
($nu.current-exe | path dirname | path basename | str replace "bin" ""),
(version | get branch | str replace "main" ""),
] | str join " " | str trim)
(if (($content | str length) > 0) {
$"(' <' | bright-yellow)($content)('> ' | bright-yellow)"
} else {
""
})
}
# build current working directory segment
## don't get sucked into the path syntax wars: simply color portions of path to flag priviliged vs normal user mode.
def dir_string [] {
let path_color = (if (is-admin) { ansi red_bold } else { ansi green_bold })
let separator_color = (if (is-admin) { ansi light_red_bold } else { ansi light_green_bold })
$"($path_color)($env.PWD)(ansi reset)" | str replace --all (char path_sep) $"($separator_color)(char path_sep)($path_color)"
}
# Following code cheerfully ~~stolen~~ adapted from:
# https://github.com/nushell/nu_scripts/blob/ab0d3aaad015ca8ac2c2004d728cc8bac32cda1b/modules/prompt/panache-git.nu
# Get repository status as structured data
def repo-structured [] {
let in_git_repo = ((git rev-parse HEAD err> /dev/null | complete | get exit_code) == 0)
let status = (if $in_git_repo {
git --no-optional-locks status --porcelain=2 --branch | lines
} else {
[]
})
let on_named_branch = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.head')
| first
| str contains '(detached)'
| nope
} else {
false
})
let branch_name = (if $on_named_branch {
$status
| where ($it | str starts-with '# branch.head')
| split column ' ' col1 col2 branch
| get branch
| first
} else {
''
})
let commit_hash = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.oid')
| split column ' ' col1 col2 full_hash
| get full_hash
| first
| str substring 0..7
} else {
''
})
let tracking_upstream_branch = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.upstream')
| str join
| is-empty
| nope
} else {
false
})
let upstream_exists_on_remote = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.ab')
| str join
| is-empty
| nope
} else {
false
})
let ahead_behind_table = (if $upstream_exists_on_remote {
$status
| where ($it | str starts-with '# branch.ab')
| split column ' ' col1 col2 ahead behind
} else {
[[]]
})
let commits_ahead = (if $upstream_exists_on_remote {
$ahead_behind_table
| get ahead
| first
| into int
} else {
0
})
let commits_behind = (if $upstream_exists_on_remote {
$ahead_behind_table
| get behind
| first
| into int
| math abs
} else {
0
})
let has_staging_or_worktree_changes = (if $in_git_repo {
$status
| where ($it | str starts-with '1') or ($it | str starts-with '2')
| str join
| is-empty
| nope
} else {
false
})
let has_untracked_files = (if $in_git_repo {
$status
| where ($it | str starts-with '?')
| str join
| is-empty
| nope
} else {
false
})
let has_unresolved_merge_conflicts = (if $in_git_repo {
$status
| where ($it | str starts-with 'u')
| str join
| is-empty
| nope
} else {
false
})
let staging_worktree_table = (if $has_staging_or_worktree_changes {
$status
| where ($it | str starts-with '1') or ($it | str starts-with '2')
| split column ' '
| get column2
| split column '' staging worktree --collapse-empty
} else {
[[]]
})
let staging_added_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where staging == 'A'
| length
} else {
0
})
let staging_modified_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where staging in ['M', 'R']
| length
} else {
0
})
let staging_deleted_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where staging == 'D'
| length
} else {
0
})
let untracked_count = (if $has_untracked_files {
$status
| where ($it | str starts-with '?')
| length
} else {
0
})
let worktree_modified_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where worktree in ['M', 'R']
| length
} else {
0
})
let worktree_deleted_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where worktree == 'D'
| length
} else {
0
})
let merge_conflict_count = (if $has_unresolved_merge_conflicts {
$status
| where ($it | str starts-with 'u')
| length
} else {
0
})
{
in_git_repo: $in_git_repo,
on_named_branch: $on_named_branch,
branch_name: $branch_name,
commit_hash: $commit_hash,
tracking_upstream_branch: $tracking_upstream_branch,
upstream_exists_on_remote: $upstream_exists_on_remote,
commits_ahead: $commits_ahead,
commits_behind: $commits_behind,
staging_added_count: $staging_added_count,
staging_modified_count: $staging_modified_count,
staging_deleted_count: $staging_deleted_count,
untracked_count: $untracked_count,
worktree_modified_count: $worktree_modified_count,
worktree_deleted_count: $worktree_deleted_count,
merge_conflict_count: $merge_conflict_count
}
}
# Get repository status as a styled string
def repo-styled [] {
let status = (repo-structured)
let is_local_only = ($status.tracking_upstream_branch != true)
let upstream_deleted = (
$status.tracking_upstream_branch and
$status.upstream_exists_on_remote != true
)
let is_up_to_date = (
$status.upstream_exists_on_remote and
$status.commits_ahead == 0 and
$status.commits_behind == 0
)
let is_ahead = (
$status.upstream_exists_on_remote and
$status.commits_ahead > 0 and
$status.commits_behind == 0
)
let is_behind = (
$status.upstream_exists_on_remote and
$status.commits_ahead == 0 and
$status.commits_behind > 0
)
let is_ahead_and_behind = (
$status.upstream_exists_on_remote and
$status.commits_ahead > 0 and
$status.commits_behind > 0
)
let branch_name = (if $status.in_git_repo {
(if $status.on_named_branch {
$status.branch_name
} else {
['(' $status.commit_hash '...)'] | str join
})
} else {
''
})
let branch_styled = (if $status.in_git_repo {
(if $is_local_only {
(branch-local-only $branch_name)
} else if $is_up_to_date {
(branch-up-to-date $branch_name)
} else if $is_ahead {
(branch-ahead $branch_name $status.commits_ahead)
} else if $is_behind {
(branch-behind $branch_name $status.commits_behind)
} else if $is_ahead_and_behind {
(branch-ahead-and-behind $branch_name $status.commits_ahead $status.commits_behind)
} else if $upstream_deleted {
(branch-upstream-deleted $branch_name)
} else {
$branch_name
})
} else {
''
})
let has_staging_changes = (
$status.staging_added_count > 0 or
$status.staging_modified_count > 0 or
$status.staging_deleted_count > 0
)
let has_worktree_changes = (
$status.untracked_count > 0 or
$status.worktree_modified_count > 0 or
$status.worktree_deleted_count > 0 or
$status.merge_conflict_count > 0
)
let has_merge_conflicts = $status.merge_conflict_count > 0
let staging_summary = (if $has_staging_changes {
(staging-changes $status.staging_added_count $status.staging_modified_count $status.staging_deleted_count)
} else {
''
})
let worktree_summary = (if $has_worktree_changes {
(worktree-changes $status.untracked_count $status.worktree_modified_count $status.worktree_deleted_count)
} else {
''
})
let merge_conflict_summary = (if $has_merge_conflicts {
(unresolved-conflicts $status.merge_conflict_count)
} else {
''
})
let delimiter = (if ($has_staging_changes and $has_worktree_changes) {
('|' | bright-yellow)
} else {
''
})
let local_summary = (
$'($staging_summary) ($delimiter) ($worktree_summary) ($merge_conflict_summary)' | str trim
)
let local_indicator = (if $status.in_git_repo {
(if $has_worktree_changes {
('!' | red)
} else if $has_staging_changes {
('~' | bright-cyan)
} else {
''
})
} else {
''
})
let repo_summary = (
$'($branch_styled) ($local_summary) ($local_indicator)' | str trim
)
let left_bracket = ('[' | bright-yellow)
let right_bracket = (']' | bright-yellow)
(if $status.in_git_repo {
$' ($left_bracket)($repo_summary)($right_bracket) '
} else {
''
})
}
# Helper commands to encapsulate style and make everything else more readable
def nope [] {
each { |it| $it == false }
}
def bright-cyan [] {
each { |it| $"(ansi -e '96m')($it)(ansi reset)" }
}
def bright-green [] {
each { |it| $"(ansi -e '92m')($it)(ansi reset)" }
}
def bright-red [] {
each { |it| $"(ansi -e '91m')($it)(ansi reset)" }
}
def bright-yellow [] {
each { |it| $"(ansi -e '93m')($it)(ansi reset)" }
}
def green [] {
each { |it| $"(ansi green)($it)(ansi reset)" }
}
def red [] {
each { |it| $"(ansi red)($it)(ansi reset)" }
}
def branch-local-only [
branch: string
] {
$branch | bright-cyan
}
def branch-upstream-deleted [
branch: string
] {
$'($branch) (char failed)' | bright-cyan
}
def branch-up-to-date [
branch: string
] {
$'($branch) (char identical_to)' | bright-cyan
}
def branch-ahead [
branch: string
ahead: int
] {
$'($branch) (char branch_ahead)($ahead)' | bright-green
}
def branch-behind [
branch: string
behind: int
] {
$'($branch) (char branch_behind)($behind)' | bright-red
}
def branch-ahead-and-behind [
branch: string
ahead: int
behind: int
] {
$'($branch) (char branch_behind)($behind) (char branch_ahead)($ahead)' | bright-yellow
}
def staging-changes [
added: int
modified: int
deleted: int
] {
$'+($added) ~($modified) -($deleted)' | green
}
def worktree-changes [
added: int
modified: int
deleted: int
] {
$'+($added) ~($modified) -($deleted)' | red
}
def unresolved-conflicts [
conflicts: int
] {
$'!($conflicts)' | red
}

View File

@@ -0,0 +1,97 @@
# Displays a prompt
export def git-status-prompt [] {
let not_windows = ($nu.os-info.name !~ "windows")
$"(ansi reset)(ansi green)(if $not_windows {
$env.USER
} else {
$env.USERNAME
}
)(ansi reset)@(hostname | str trim):(ansi green_dimmed)(prompt-pwd)(ansi reset)(git-branch-icon)(ansi reset)(char newline)(char prompt) "
}
# Returns a shortened pwd for use in prompt
def prompt-pwd [] {
let not_windows = ($nu.os-info.name !~ "windows")
let path = (pwd | if $not_windows { split row "/" } else { split row '\' })
let home = (if $not_windows {
($env.HOME | split row "/")
} else {
(echo [$env.HOMEDRIVE $env.HOMEPATH] | path join | split row '\')
}
)
if ($path | length) > 1 {
if ($home | all {|it| $it in $path }) {
let path_without_home = ($path | skip ($home | length))
if ($path_without_home | wrap path | compact | length) > 0 {
let parent = ($path | skip ($home | length) | drop)
if ($parent | wrap parent | compact | length) > 0 {
let short_part = ($parent | each { |part|
if ($part | str starts-with ".") {
$"($part | str substring [0 2])/"
} else {
$"($part | str substring [0 1])/"
}
})
$"~/( | str join)($path | last)"
} else {
$"~/($path | last)"
}
} else {
"~"
}
} else {
let parent = (echo $path | drop | str substring [0 1] | each {|it| echo $it "/" })
$"/($parent)($path | last)"
}
} else {
pwd
}
}
# Map of git status codes to ANSI colour codes
def git-prompt-map [] {
echo a m r c d "??" u |
rotate --ccw |
reject column0 | append (
echo (ansi green) (ansi yellow_bold) (ansi cyan) (ansi blue) (ansi red) (ansi red_dimmed) (ansi red) |
rotate --ccw |
reject column0
) | headers
}
# Gets an icon and a colour for a given git status code
def git-prompt-icons [k] {
let icns = ["✚ " "* " "➜ " "⇒ " "✖ " "? " "! "];
git-prompt-map |
transpose status colour | enumerate | each { |icon|
let idx = $icon.index;
if $icon.item.status == $k {
$"($icon.item.colour)($icns | get $idx)"
}
} | compact
}
# Checks git status of current working directory and displays an icon
def git-branch-icon [] {
do -i {
let branch = (do -i { git rev-parse --abbrev-ref HEAD } | str trim)
if ($branch | str length) > 0 {
let modified = (do -i { git status --porcelain } | split row "\n" | str trim | split column " " status file);
if ($modified | get 0 | first | is-empty) {
$"|(ansi green)($branch)(ansi reset):(ansi green)(ansi reset)"
} else {
let modified2 = (do -i { git status --porcelain } | split row "\n" | str substring [0 1])
let branch_colour = (if (echo $modified2 | each {|it| $it in [A M R C D] } | reduce {|it acc| $it or $acc }) {
"yellow"
} else {
"red"
}
)
$"|(ansi $branch_colour)($branch)(ansi reset):($modified | get status | uniq | str downcase | each {|it| git-prompt-icons $it })" | str join
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,377 @@
# $env.PROMPT_INDICATOR = {|| "" }
#
# $env.PROMPT_COMMAND = {|| full-left-prompt }
# or
# $env.PROMPT_COMMAND = {|| left-prompt [
# 'user',
# 'dir',
# 'fast-git'
# 'duration',
# ]}
#
# Optional:
# $env.PROMPT_COMMAND_RIGHT = {|| "" }
export def full-left-prompt [] {
(par-left-prompt [
'user-host',
'dir',
'full-git'
'duration',
'wsl',
])
}
# Filter ----------------------------------------------------------------------
export def left-prompt [modules: list] {
let ret = ($modules | each { |name| (exec-module $name)} | append $'(prompt-indicator)' | str join)
$'(ansi reset)($ret)'
}
# Parallel run
export def par-left-prompt [modules: list] {
let str_table = ($modules | par-each { |name|
{ name: $name, content: (exec-module $name) }
})
let ret = ($modules | each { |name| ($str_table | where name == $'($name)' | get content.0)} | append $'(prompt-indicator)' | str join)
$'(ansi reset)($ret)'
}
def exec-module [name: string] {
if $name == 'user-host' {
(username-style true)
} else if $name == 'user' {
(username-style false)
} else if $name == 'dir' {
$'(current-dir-style) (read-only-style)'
} else if $name == 'fast-git' {
(fast-git-style)
} else if $name == 'full-git' {
(full-git-style)
} else if $name == 'duration' {
(duration-style)
} else if $name == 'wsl' {
(wsl-style)
} else {
''
}
}
# Styles ----------------------------------------------------------------------
def get-styles [] {
{
USER_STYLE: (ansi green),
PATH_STYLE: (ansi light_blue),
BRANCH_STYLE: (ansi dark_gray_bold),
AHEAD_STYLE: $'(ansi green)(char branch_ahead)',
BEHIND_STYLE: $'(ansi yellow_bold)(char branch_behind)',
STAGE_STYLE: $'(ansi blue)S(ansi reset)',
UNSTAGE_STYLE: $'(ansi dark_gray)U(ansi reset)',
NEW_FILE_STYLE: $'(ansi green)N',
ADD_FILE_STYLE: $'(ansi green)A',
MODIFY_FILE_STYLE: $'(ansi yellow)M',
DELETE_FILE_STYLE: $'(ansi red)D',
CONFLICT_FILE_STYLE: $'(ansi light_purple_bold)C',
DURATION_STYLE: (ansi yellow),
}
}
def prompt-indicator [] {
if ($env.LAST_EXIT_CODE | into int) == 0 {
$"\r\n(ansi cyan)> "
} else {
$"\r\n(ansi red)x "
}
}
def username-style [show_host: bool] {
let s = get-styles
let name = (get-username)
if $show_host and (is-ssh-session) {
$'($s.USER_STYLE)($name)(ansi dark_gray)@($s.USER_STYLE)(get-hostname)(ansi dark_gray):(ansi reset)'
} else if (is-self-user $name) == false {
$'($s.USER_STYLE)($name)(ansi dark_gray):(ansi reset)'
} else {
''
}
}
def wsl-style [] {
if 'WSLENV' in $env {
$'(ansi dark_gray) WSL(ansi reset)'
} else {
''
}
}
# Get the current directory with home abbreviated
def current-dir-style [] {
let s = get-styles
let current_dir = ($env.PWD)
let current_dir_abbreviated = if $current_dir == $nu.home-path {
'~'
} else {
let current_dir_relative_to_home = (
do --ignore-errors { $current_dir | path relative-to $nu.home-path } | str join
)
if ($current_dir_relative_to_home | is-empty) == false {
$'~(char separator)($current_dir_relative_to_home)'
} else {
$current_dir
}
}
if (is-admin) {
$'(ansi red_bold)($current_dir_abbreviated)(ansi reset)'
} else {
$'($s.PATH_STYLE)($current_dir_abbreviated)(ansi reset)'
}
}
def read-only-style [] {
if (ls -Dl $env.PWD | get readonly.0) {
$'[(ansi red_bold)ro(ansi reset)]'
} else {
''
}
}
def duration-style [] {
let s = get-styles
mut secs = ($env.CMD_DURATION_MS | into int) / 1000
if $secs > 1 {
mut ret = [$'[took ($s.DURATION_STYLE)']
if $secs >= 3600 {
$ret = ($ret | append $'($secs // 3600)h ($secs mod 3600 // 60)m ')
$secs = $secs mod 60
} else if $secs >= 60 {
$ret = ($ret | append $'($secs // 60)m ')
$secs = $secs mod 60
}
($ret | append $'($secs | math round -p 1)s(ansi reset)]' | str join)
} else {
''
}
}
def fast-git-style [] {
let s = get-styles
let b_info = (do -p { git --no-optional-locks branch -v } | str trim)
if ($b_info | is-empty) {
''
} else {
let info = ($b_info | parse -r '\* (?<name>(\([\S ]+\))|([\w\/\-\.]+)) +\w+ (\[((?<state>[^\]]+))+\])?')
let state_list = ($info.state.0 | split row ', ' | each { |it|
let p = ($it | parse "{s} {n}")
if ($p | is-empty) {
if ($it | str starts-with "gone") {
$' (ansi light_red)(char failed)'
} else {
''
}
} else if $p.s.0 == 'ahead' {
$' ($s.AHEAD_STYLE)($p.n.0)(ansi reset)'
} else if $p.s.0 == 'behind' {
$' ($s.BEHIND_STYLE)($p.n.0)'
} else {
$' (ansi red)($p.s.0) ($p.n.0)'
}
})
let state_str = ($state_list | str join)
$'[($s.BRANCH_STYLE)($info.name.0)(ansi reset)($state_str)(ansi reset)]'
}
}
def full-git-style [] {
let s = get-styles
let info_lines = (do -p { git --no-optional-locks status --porcelain=2 --branch } | str trim | lines)
if ($info_lines | is-empty) {
''
} else {
# Scan lines
let info = ($info_lines | reduce -f {
out: [],
staged: {a: 0, m: 0, d: 0},
unstaged: {n: 0, m: 0, d: 0, c: 0},
track: false,
remote: false,
} { |str, ctx|
mut track = $ctx.track
mut remote = $ctx.remote
mut out = $ctx.out
mut staged = $ctx.staged
mut unstaged = $ctx.unstaged
let l = ($str | split row -n 3 ' ')
if $track == false {
if $l.0 == '#' and $l.1 == 'branch.upstream' {
$track = true
}
}
# Branch
if $l.0 == '#' {
if $l.1 == 'branch.oid' {
let id = ($l.2 | str substring 0..7)
$out = [$"($s.BRANCH_STYLE)\(HEAD detached at ($id)\)(ansi reset)"]
} else if $l.1 == 'branch.head' {
if $l.2 != "\(detached\)" {
$out = ($out | update 0 $'($s.BRANCH_STYLE)($l.2)(ansi reset)')
}
} else if $track {
if $l.1 == 'branch.ab' {
$remote = true
let state = ($l.2 | parse "+{an} -{bn}")
if $state.an.0 != '0' {
$out = ($out | append $' ($s.AHEAD_STYLE)($state.an.0)(ansi reset)')
}
if $state.bn.0 != '0' {
$out = ($out | append $' ($s.BEHIND_STYLE)($state.bn.0)')
}
}
}
}
# Status
if $l.0 == '?' {
$unstaged = ($unstaged | update n ($unstaged.n + 1))
} else if $l.0 == 'u' {
$unstaged = ($unstaged | update c ($unstaged.c + 1))
} else if $l.0 == '1' or $l.0 == '2' {
let state_l = ($l.1 | split chars)
$staged = (update-git-status $staged $state_l.0)
$unstaged = (update-git-status $unstaged $state_l.1)
}
{
out: $out,
staged: $staged,
unstaged: $unstaged,
track: $track,
remote: $remote,
}
})
# Branch string
mut out_list = if $info.track {
if $info.remote == false {
($info.out | append $' (ansi light_red)(char failed)')
} else if ($info.out | length) < 2 {
($info.out | append $' (ansi cyan)(char identical_to)')
} else {
$info.out
}
} else {
$info.out
}
$out_list = ($out_list | append $'(ansi reset)')
# Stage string
mut stage_list = []
if $info.staged.a > 0 {
$stage_list = ($stage_list | append $' ($s.ADD_FILE_STYLE)($info.staged.a)(ansi reset)')
}
if $info.staged.m > 0 {
$stage_list = ($stage_list | append $' ($s.MODIFY_FILE_STYLE)($info.staged.m)(ansi reset)')
}
if $info.staged.d > 0 {
$stage_list = ($stage_list | append $' ($s.DELETE_FILE_STYLE)($info.staged.d)(ansi reset)')
}
# Unstage string
mut unstage_list = []
if $info.unstaged.c > 0 {
$unstage_list = ($unstage_list | append $' ($s.CONFLICT_FILE_STYLE)($info.unstaged.c)(ansi reset)')
}
if $info.unstaged.n > 0 {
$unstage_list = ($unstage_list | append $' ($s.NEW_FILE_STYLE)($info.unstaged.n)(ansi reset)')
}
if $info.unstaged.m > 0 {
$unstage_list = ($unstage_list | append $' ($s.MODIFY_FILE_STYLE)($info.unstaged.m)(ansi reset)')
}
if $info.unstaged.d > 0 {
$unstage_list = ($unstage_list | append $' ($s.DELETE_FILE_STYLE)($info.unstaged.d)(ansi reset)')
}
# Append list
if ($stage_list | length) > 0 {
$out_list = ($out_list | append $' | ($s.STAGE_STYLE):' | append $stage_list)
}
if ($unstage_list | length) > 0 {
$out_list = ($out_list | append $' | ($s.UNSTAGE_STYLE):' | append $unstage_list)
}
$'[($out_list | str join)(ansi reset)]'
}
}
def update-git-status [
status: record
m: string
] {
if $m == 'A' {
($status | update a (($status.a | into int) + 1))
} else if $m == 'M' {
($status | update m (($status.m | into int) + 1))
} else if $m == 'D' {
($status | update d (($status.d | into int) + 1))
} else {
$status
}
}
# Helper ----------------------------------------------------------------------
def get-username [] {
if 'USERNAME' in $env {
$env.USERNAME
} else if 'USER' in $env {
$env.USER
} else {
''
}
}
def is-self-user [name: string] {
if 'LOGNAME' in $env {
($env.LOGNAME == $name)
} else {
true
}
}
def get-hostname [] {
if 'COMPUTERNAME' in $env {
$env.COMPUTERNAME
} else if 'HOSTNAME' in $env {
$env.HOSTNAME
} else {
''
}
}
def is-ssh-session [] {
if 'SSH_CONNECTION' in $env {
true
} else if 'SSH_CLIENT' in $env {
true
} else if 'SSH_TTY' in $env {
true
} else {
false
}
}

View File

@@ -0,0 +1,86 @@
# This is a work in progress. Not working yet but you can see where I'm going.
export def construct_prompt [] {
# let decorator = (char prompt)
let decorator = (create_second_line)
# not using machine name
# let machine_name = (sys host | get hostname)
# the current working directory
# let current_dir = (pwd)
let current_dir = (home_abbrev)
# the current bit branch
# let git_status = (git -c core.quotepath=false -c color.status=false status -uall --short --branch)
let git_info = (do -i { git rev-parse --abbrev-ref HEAD } | str trim -c (char nl) | str join )
# what to put in the title
let title_bar = (set_title)
# get the terminal width
let term_width = (term size).columns
# get the curren time
let current_time = (date now | format date '%I:%M:%S%.3f %p')
# let's construct the left and right prompt
# the left side of the prompt with ansi colors
let left_colored = $"(ansi gb)($current_dir)(ansi cb)(char lparen)($git_info)(char rparen)(ansi reset)"
# the left prompt length without the ansi escapes
let left_len = ($left_colored | ansi strip | str length)
# the right side of the prompt with ansi colors
let right_colored = $"(ansi blue)($env.CMD_DURATION_MS)|(ansi dark_gray)($current_time)(ansi reset)"
# let's calcuate the length of the right prompt so we know how much to pad the left prompt
let calculated_right_len = ($term_width - $left_len)
# finally, let's make the prompt
let the_prompt = $"($left_colored)($right_colored | fill -a r -c ' ' -w $calculated_right_len)(char newline)($decorator) "
# let's update the title bar now
print -n $title_bar
# and last, but not least, let's print the prompt
echo $the_prompt
## put this in your config.toml
# prompt = "construct_prompt"
## also you need to source the file in your startup like
# "source C:\\Users\\username\\source\\some\\folder\\nu_scripts\\prompt\\left_and_right_prompt.nu",
}
# Abbreviate home path
def home_abbrev [] {
let is_home_in_path = (pwd | into string | str starts-with $nu.home-path)
if $is_home_in_path {
let lin_home = ($nu.home-path | into string | str replace -a '\\' '/' | str downcase)
let lin_pwd = (pwd | into string | str replace -a '\\' '/' | str downcase)
$lin_pwd | str replace $lin_home '~'
} else {
pwd
}
}
# Get Git Info custom commands
def git_br [] {
$"(ansi gb)(pwd)(ansi reset)(char lparen)(ansi cb)(do -i { git rev-parse --abbrev-ref HEAD } | str trim -c (char nl) | str join)(ansi reset)(char rparen)(char newline)(ansi yb)(date now | format date '%m/%d/%Y %I:%M:%S%.3f %p')(ansi reset)¯\\_(char lparen)ツ)_/¯(char prompt) "
}
# Set Title String custom commands
def set_title_str [str_arg] {
$"(ansi title) ($str_arg) (char bel)"
}
def get_abbrev_pwd_lin [] {
home_abbrev | split row '/' | first (home_abbrev | split row '/' | length | each { $in - 1} ) | each { str substring 0..1 | $'($in)/' } | append (home_abbrev | split row '/' | last ) | str join
}
def set_title [] {
set_title_str ([(get_abbrev_pwd_lin) ' ' (term size).columns 'x' (term size).rows ] | str join)
}
def create_second_line [] {
[(ansi gb) (char -u "2514") (char -u "2500") ' $ ' (ansi cb) (char prompt) (ansi reset)] | str join
}

View File

@@ -0,0 +1,284 @@
# NOTE: This is meant to run with engine-q and not nushell yet
# It's still being tested. There will be bugs. :)
# REQUIREMENTS #
# you definitely need nerd fonts https://www.nerdfonts.com
# nerd fonts repo https://github.com/ryanoasis/nerd-fonts
# i use "FiraCode Nerd Font Mono" on mac
#
# you also must have the engine-q gstat plugin installed and registered
# ATTRIBUTION #
# A little fancier prompt with git information
# inspired by https://github.com/xcambar/purs
# inspired by https://github.com/IlanCosman/tide
# inspired by https://github.com/JanDeDobbeleer/oh-my-posh
# Abbreviate home path
def home_abbrev [os] {
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
if ($is_home_in_path == true) {
if ($os == "windows") {
let home = ($nu.home-path | str replace -ar '\\' '/')
let pwd = ($env.PWD | str replace -ar '\\' '/')
$pwd | str replace $home '~'
} else {
$env.PWD | str replace $nu.home-path '~'
}
} else {
$env.PWD | str replace -ar '\\' '/'
}
}
export def path_abbrev_if_needed [apath term_width] {
# probably shouldn't do coloring here but since we're coloring
# only certain parts, it's kind of tricky to do it in another place
# if needed, use `ansi strip` to remove coloring
let T = (ansi { fg: "#BCBCBC" bg: "#3465A4"}) # truncated
let P = (ansi { fg: "#E4E4E4" bg: "#3465A4"}) # path
let PB = (ansi { fg: "#E4E4E4" bg: "#3465A4" attr: b}) # path bold
let R = (ansi reset)
let red = (ansi red)
# replace the home path first
let apath = ($apath | str replace $nu.home-path ~)
# split out by path separator into tokens
# don't use psep here because in home_abbrev we're making them all '/'
let splits = ($apath | split row '/')
let splits_len = ($splits | length)
if (($apath | str length) > ($term_width / 2)) {
# get all the tokens except the last
let tokens = ($splits | take ($splits_len - 1) | each {|x|
$"($T)($x | str substring 0..1)($R)"
})
# append the last part of the path
let tokens = ($tokens | append $"($PB)($splits | last)($R)")
# collect
$tokens | str join $"($T)/"
} else {
if ($splits_len == 0) {
# We're at / on the file system
$"/($T)"
} else if ($splits_len == 1) {
let top_part = ($splits | first)
let tokens = $"($PB)($top_part)($R)"
$tokens | str join $"($T)"
} else {
let top_part = ($splits | first ($splits_len - 1))
let end_part = ($splits | last)
let tokens = ($top_part | each {|x|
$"/($T)($x | str substring 0..1)($R)"
})
let tokens = ($tokens | append $"/($PB)($end_part)($R)")
$tokens | skip 1 | str join $"($T)"
}
}
}
def get_os_icon [os use_nerd_fonts] {
# f17c = tux, f179 = apple, f17a = windows
if $use_nerd_fonts {
if ($os =~ macos) {
(char -u f179)
} else if ($os =~ linux) {
(char -u f17c)
} else if ($os =~ windows) {
(char -u f17a)
} else {
''
}
} else {
if ($os =~ macos) {
"M"
} else if ($os =~ linux) {
"L"
} else if ($os =~ windows) {
"W"
} else {
''
}
}
}
export def get_left_prompt [os use_nerd_fonts] {
# replace this 30 with whatever the width of the terminal is
let display_path = (path_abbrev_if_needed (home_abbrev $os) 30)
let R = (ansi reset)
# some icons and the unicode char
# e0b0 ▷ 25b7 ⏵ 23f5 ▶ 25b6 ⯈ 2bc8 🞂 1f782
# e0b1
# e0b2 ◁ 25c1 ⏴ 23f4 ◀ 25c0 ⯇ 2bc7 🞀 1f780
# e0b3
# f1d3
# f07c or  f115 📁 1f4c1 🗀 1f5c0
# f015 or  f7db 🏠 1f3e0 ⌂ 2302
let TERM_BG = "#0C0C0C"
let right_transition_nf = (char -u e0b0)
# let right_transition = (char -u 1f782)
let right_transition = ""
let home_nf = (char -u f015)
# let home = (char -u 1f3e0)
let home = ""
let folder_nf = (char -u f07c)
# let folder = (char -u 1f5c0)
let folder = ""
# build segments and then put together the segments for the prompt
let os_segment = ([
(ansi { fg: "#080808" bg: "#CED7CF"}) # os bg color
(char space) # space
(get_os_icon $os $use_nerd_fonts) # os icon
(char space) # space
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color transition
(if $use_nerd_fonts {
$right_transition_nf # 
} else {
$right_transition
})
(char space) # space
] | str join)
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
let path_segment = (if $is_home_in_path {
[
(if $use_nerd_fonts {
$home_nf #  home icon
} else {
$home
})
(char space) # space
$display_path # ~/src/forks/nushell
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color just to color the next space
(char space) # space
] | str join
} else {
[
(if $use_nerd_fonts {
$folder_nf #  folder icon
} else {
$folder
})
(char space) # space
$display_path # ~/src/forks/nushell
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color just to color the next space
(char space) # space
] | str join
})
let indicator_segment = (
[
(ansi { fg: "#3465A4" bg: $TERM_BG}) # color
(if $use_nerd_fonts {
$right_transition_nf # 
} else {
# $right_transition
" >"
})
($R) # reset color
] | str join
)
# assemble all segments for final prompt printing
[
$os_segment
$path_segment
$indicator_segment
] | str join
}
export def get_right_prompt [os use_nerd_fonts] {
# right prompt ideas
# 1. just the time on the right
# 2. date and time on the right
# 3. git information on the right
# 4. maybe git and time
# 5. would like to get CMD_DURATION_MS going there too when it's implemented
# 6. all of the above, chosen by def parameters
let R = (ansi reset)
let TIME_BG = "#D3D7CF"
let TERM_FG = "#0C0C0C"
let left_transition_nf = (char -u e0b2)
# let left_transition = (char -u 1f780)
let left_transition = ""
let datetime_segment = ([
(ansi { fg: $TIME_BG bg: $TERM_FG})
(if $use_nerd_fonts {
$left_transition_nf # 
} else {
$left_transition
})
(ansi { fg: $TERM_FG bg: $TIME_BG})
(char space)
(date now | format date '%m/%d/%Y %I:%M:%S%.3f')
(char space)
($R)
] | str join)
let time_segment = ([
(ansi { fg: $TIME_BG bg: $TERM_FG})
(if $use_nerd_fonts {
$left_transition_nf # 
} else {
$left_transition
})
(ansi { fg: $TERM_FG bg: $TIME_BG})
(char space)
(date now | format date '%I:%M:%S %p')
(char space)
($R)
] | str join)
# 1. datetime - working
# $datetime_segment
# 2. time only - working
$time_segment
# 3. git only - working
# $git_segment
# 4. git + time -> need to fix the transition
# [
# $git_segment
# $time_segment
# ] | str join
# 5. fernando wants this on the left prompt
# [
# $os_segment
# $time_segment
# $path_segment
# ]
}
export def get_prompt [nerd?] {
let use_nerd_fonts = ($nerd != null)
let os = $nu.os-info.name
let left_prompt = (get_left_prompt $os $use_nerd_fonts)
let right_prompt = (get_right_prompt $os $use_nerd_fonts)
# return in record literal syntax to be used kind of like a tuple
# so we don't have to run this script more than once per prompt
{
left_prompt: $left_prompt
right_prompt: $right_prompt
}
#
# in the config.nu you would do something like
# use "c:\some\path\to\nu_scripts\engine-q\prompt\oh-my-minimal.nu" get_prompt
# $env.PROMPT_COMMAND = { (get_prompt).left_prompt }
# $env.PROMPT_COMMAND_RIGHT = { (get_prompt).right_prompt }
# $env.PROMPT_INDICATOR = " "
# or with nerdfonts
# $env.PROMPT_COMMAND = { (get_prompt 1).left_prompt }
# $env.PROMPT_COMMAND_RIGHT = { (get_prompt 1).right_prompt }
}

View File

@@ -0,0 +1,192 @@
# oh-my.nu v2
This is less a version 2 and more of a different way of thinking. The intent of this script is to start to make oh-my.nu more configurable. We start with that by defining a lot of variables. Then we create a runtime_colors list of records. Those records will have 8bit colors, 24bit colors, or a value. 8bit for terminals like MacOS's Terminal.app and 24bit color for the rest of the world.
The thought would be that at some point this file source read another file for configured settings. That part is not done yet.
In order to use this script you need to source it and then set these.
```
$env.PROMPT_COMMAND = { (get_prompt 8bit).left_prompt }
$env.PROMPT_COMMAND_RIGHT = { (get_prompt 8bit).right_prompt }
$env.PROMPT_INDICATOR = { "" }
```
I'd love for someone to take up the torch and work on this script in order to make it better, configurable, awesome.
Below is some rough documentation on what the configuration points are and could be. Not all of these configuration points are implemented. BTW, this is a total rip-off of the fish [tide](https://github.com/IlanCosman/tide) prompt, but not as nice.
### color mode
* 24bit
* 8bit
### prompt
| Variable | Description | Type |
| -------------------------- | --------------------------------------------------------------------------------------------- | ------- |
| add_newline_before | print an empty line before the prompt | boolean |
| color_frame_and_connection | color of frame and prompt connection | color |
| color_separator_same_color | color of the separator between items with the same background color | color |
| icon_connection | repeated symbol that spans gap between left and right sides of prompt | string |
| min_cols | if using one-line prompt, Tide attempts to have at least this many columns for you to type in | integer |
| pad_items | if true, add a space before and after each item | boolean |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_prompt_color_frame_and_connection|#6C6C6C|242|
|tide_prompt_color_separator_same_color|#949494|246|
### left_prompt
| Variable | Description | Type |
| -------------------- | --------------------------------------------------------- | ------- |
| frame_enabled | display the left prompt frame | boolean |
| items | order of items to print in the left prompt | list |
| prefix | string to put at the beginning the left prompt | string |
| separator_diff_color | string to separate items with different background colors | string |
| separator_same_color | string to separate items with the same background color | string |
| suffix | string to put at the end of the left prompt | string |
| variable_name | char |
| - | - |
|tide_left_prompt_separator_diff_color|e0b0|
|tide_left_prompt_separator_same_color|e0b1|
### right_prompt
| Variable | Description | Type |
| -------------------- | --------------------------------------------------------- | ------- |
| frame_enabled | display the right prompt frame | boolean |
| items | order of items to print in the right prompt | list |
| prefix | string to put at the beginning the right prompt | string |
| separator_diff_color | string to separate items with different background colors | string |
| separator_same_color | string to separate items with the same background color | string |
| suffix | string to put at the end of the right prompt | string |
| variable_name | char |
| - | - |
|tide_right_prompt_separator_diff_color|e0b2|
|tide_right_prompt_separator_same_color|e0b3|
### cmd_duration
| Variable | Description | Type |
| --------- | ------------------------------------------------------------------ | ------- |
| bg_color | background color of the cmd_duration item | color |
| color | color of the cmd_duration item | color |
| decimals | number of decimals to display after the seconds place | integer |
| icon | icon for the cmd_duration item | string |
| threshold | number of milliseconds that duration must exceed to produce output | integer |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_cmd_duration_bg_color|#C4A000|178|
|tide_cmd_duration_color|#000000|16|
### git
| Variable | Description | Type |
| ----------------- | ---------------------------------------------------------------------- | ------ |
| bg_color | default background color of the git_item | color |
| bg_color_unstable | background color when repository has dirty, staged, or untracked files | color |
| bg_color_urgent | background color when repository has conflicts or ongoing operations | color |
| color_branch | color of branch/SHA | color |
| color_conflicted | color of conflicted files number | color |
| color_dirty | color of dirty files number | color |
| color_operation | color of the current operation | color |
| color_staged | color of staged files number | color |
| color_stash | color of stashes number | color |
| color_untracked | color of untracked files number | color |
| color_upstream | color of upstream behind/ahead numbers | color |
| icon | icon of the git item, colored same as branch | string |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_git_bg_color|#4E9A06|70|
|tide_git_bg_color_unstable|#C4A000|178|
|tide_git_bg_color_urgent|#CC0000|160|
|tide_git_color_branch|#000000|16|
|tide_git_color_conflicted|#000000|16|
|tide_git_color_dirty|#000000|16|
|tide_git_color_operation|#000000|16|
|tide_git_color_staged|#000000|16|
|tide_git_color_stash|#000000|16|
|tide_git_color_untracked|#000000|16|
|tide_git_color_upstream|#000000|16|
### os
| Variable | Description | Type |
| -------- | --------------------------- | ----- |
| bg_color | background color of os item | color |
| color | color of os item | color |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_os_bg_color|#CED7CF|188|
|tide_os_color|#080808|232|
### pwd
| Variable | Description | Type |
| -------------------- | ---------------------------------------------------------------------------------------------- | ------ |
| bg_color | background color of pwd item | color |
| color_anchors | color of anchor directories. These directories are displayed in bold and immune to truncation. | color |
| color_dirs | color of normal directories | color |
| color_truncated_dirs | color of truncated directories | color |
| icon | default icon for pwd item | string |
| icon_home | icon when the the current directory is the user's HOME | string |
| icon_unwritable | icon when the directory is not writable by the user | string |
| markers | if a directory contains any of these files/directories, it will be anchored | list |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_pwd_bg_color|#3465A4|61|
|tide_pwd_color_anchors|#E4E4E4|254|
|tide_pwd_color_dirs|#E4E4E4|254|
|tide_pwd_color_truncated_dirs|#BCBCBC|250|
### rustc
| Variable | Description | Type |
| -------- | ---------------------------------------- | ------ |
| bg_color | background color of rust item | color |
| color | color of rust item | color |
| icon | icon to display next to the rust version | string |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_rustc_bg_color|#F74C00|202|
|tide_rustc_color|#000000|16|
### status
| Variable | Description | Type |
| ---------------- | ----------------------------------- | ------ |
| bg_color | background color when `$status` = 0 | color |
| bg_color_failure | background color when `$status` > 0 | color |
| color | color when `$status` = 0 | string |
| color_failure | color when `$status` > 0 | color |
| icon | icon when `$status` = 0 | string |
| icon_failure | icon when `$status` > 0 | string |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_status_bg_color|#2E3436|236|
|tide_status_bg_color_failure|#CC0000|160|
|tide_status_color|#4E9A06|70|
|tide_status_color_failure|#FFFF00|226|
### time
| Variable | Description | Type |
| -------- | ------------------------------------------- | ------ |
| bg_color | background color of time item | color |
| color | color of time item | color |
| format | format of time item. Uses `date` formatting | string |
| variable_name | 24bit_color | 8bit_color |
| - | - | - |
|tide_time_bg_color|#D3D7CF|188|
|tide_time_color|#000000|16|

View File

@@ -0,0 +1,435 @@
# See the readme for how to use this script
# modes
# * 8bit
# * 24bit
let color_mode = "8bit"
# setup separate characters
let left_prompt_separator_diff_color = (char -u 'e0b0')
let left_prompt_separator_same_color = (char -u 'e0b1')
let right_prompt_separator_diff_color = (char -u 'e0b2')
let right_prompt_separator_same_color = (char -u 'e0b3')
# setup color variables for 24bit and 8bit
# prompt
let prompt_color_frame_and_connection_24 = (ansi -e { fg: "#6C6C6C" })
let prompt_color_separator_same_color_24 = (ansi -e { fg: "#949494" })
let prompt_color_frame_and_connection_8 = $"(ansi idx_fg)242m"
let prompt_color_separator_same_color_8 = $"(ansi idx_fg)246m"
let prompt_add_new_line_before = false
let prompt_color_frame_and_connection = ""
let prompt_color_separator_same_color = ""
let left_separator_diff_color = ""
let left_separator_same_color = ""
let left_items = []
let left_prefix = ""
let left_suffix = ""
let right_separator_diff_color = ""
let right_separator_same_color = ""
let right_items = []
let right_prefix = ""
let right_suffix = ""
# cmd
let cmd_duration_bg_color_24 = (ansi -e { bg: "#C4A000" })
let cmd_duration_color_24 = (ansi -e { fg: "#000000" })
let cmd_duration_bg_color_8 = $"(ansi idx_bg)178m"
let cmd_duration_color_8 = $"(ansi idx_fg)16m"
let cmd_bg_color = ""
let cmd_color = ""
let cmd_decimals = 2
let cmd_icon = ""
# git
let git_bg_color_24 = (ansi -e { bg: "#4E9A06" })
let git_bg_color_unstable_24 = (ansi -e { bg: "#C4A000" })
let git_bg_color_urgent_24 = (ansi -e { bg: "#CC0000" })
let git_color_branch_24 = (ansi -e { fg: "#000000" })
let git_color_conflicted_24 = (ansi -e { fg: "#000000" })
let git_color_dirty_24 = (ansi -e { fg: "#000000" })
let git_color_operation_24 = (ansi -e { fg: "#000000" })
let git_color_staged_24 = (ansi -e { fg: "#000000" })
let git_color_stash_24 = (ansi -e { fg: "#000000" })
let git_color_untracked_24 = (ansi -e { fg: "#000000" })
let git_color_upstream_24 = (ansi -e { fg: "#000000" })
let git_bg_color_8 = $"(ansi idx_bg)70m"
let git_bg_color_unstable_8 = $"(ansi idx_bg)178m"
let git_bg_color_urgent_8 = $"(ansi idx_bg)160m"
let git_color_branch_8 = $"(ansi idx_fg)16m"
let git_color_conflicted_8 = $"(ansi idx_fg)16m"
let git_color_dirty_8 = $"(ansi idx_fg)16m"
let git_color_operation_8 = $"(ansi idx_fg)16m"
let git_color_staged_8 = $"(ansi idx_fg)16m"
let git_color_stash_8 = $"(ansi idx_fg)16m"
let git_color_untracked_8 = $"(ansi idx_fg)16m"
let git_color_upstream_8 = $"(ansi idx_fg)16m"
let git_bg_color = ""
let git_bg_color_unstable = ""
let git_bg_color_urgent = ""
let git_color_branch = ""
let git_color_conflicted = ""
let git_color_dirty = ""
let git_color_operation = ""
let git_color_staged = ""
let git_color_stash = ""
let git_color_untracked = ""
let git_color_upstream = ""
# os
let os_bg_color_24 = (ansi -e { bg: "#CED7CF" })
let os_color_24 = (ansi -e { fg: "#080808" })
let os_bg_color_8 = $"(ansi idx_bg)188m"
let os_color_8 = $"(ansi idx_fg)232m"
let os_bg_color = ""
let os_color = ""
# pwd
let pwd_bg_color_24 = (ansi -e { bg: "#3465A4" })
let pwd_color_anchors_24 = (ansi -e { fg: "#E4E4E4" })
let pwd_color_dirs_24 = (ansi -e { fg: "#E4E4E4" })
let pwd_color_truncated_dirs_24 = (ansi -e { fg: "#BCBCBC" })
let pwd_bg_color_8 = $"(ansi idx_bg)61m"
let pwd_color_anchors_8 = $"(ansi idx_fg)254m"
let pwd_color_dirs_8 = $"(ansi idx_fg)254m"
let pwd_color_truncated_dirs_8 = $"(ansi idx_fg)250m"
let pwd_bg_color = ""
let pwd_color_anchors = ""
let pwd_color_dirs = ""
let pwd_color_truncated_dirs = ""
let pwd_icon = ""
let pwd_icon_home = ""
let pwd_icon_unwritable = ""
let pwd_markers = []
# rustc
let rustc_bg_color_24 = (ansi -e { bg: "#F74C00" })
let rustc_color_24 = (ansi -e { fg: "#000000" })
let rustc_bg_color_8 = $"(ansi idx_bg)202m"
let rustc_color_8 = $"(ansi idx_fg)16m"
let rustc_bg_color = ""
let rustc_color = ""
let rustc_icon = ""
# status
let status_bg_color_24 = (ansi -e { bg: "#2E3436" })
let status_bg_color_failure_24 = (ansi -e { bg: "#CC0000" })
let status_color_24 = (ansi -e { fg: "#4E9A06" })
let status_color_failure_24 = (ansi -e { fg: "#FFFF00" })
let status_bg_color_8 = $"(ansi idx_bg)236m"
let status_bg_color_failure_8 = $"(ansi idx_bg)160m"
let status_color_8 = $"(ansi idx_fg)70m"
let status_color_failure_8 = $"(ansi idx_fg)226m"
let status_bg_color = ""
let status_bg_color_faiure = ""
let status_color = ""
let status_color_failure = ""
let status_icon = ""
let status_icon_failure = ""
# time
let time_bg_color_24 = (ansi -e { bg: "#D3D7CF" })
let time_color_24 = (ansi -e { fg: "#000000" })
let time_bg_color_8 = $"(ansi idx_bg)188m"
let time_color_8 = $"(ansi idx_fg)16m"
let time_bg_color = ""
let time_color = ""
let time_format = ""
# indicator
let indicator_color_24 = (ansi -e { fg: "#3465a4" })
let indicator_bg_color_24 = (ansi -e { bg: "#000000" })
let indicator_color_8 = $"(ansi idx_fg)61m"
let indicator_bg_color_8 = $"(ansi idx_bg)16m"
# terminal background color
let terminal_color_24 = (ansi -e { fg: "#c7c7c7" })
let terminal_color_8 = (ansi white)
let terminal_bg_color_24 = (ansi -e { bg: "#000000" })
let terminal_bg_color_8 = (ansi black)
# cmd_duration_ms
let cmd_duration_ms_color_24 = (ansi -e { fg: "#606060" })
let cmd_duration_ms_color_8 = $"(ansi idx_fg)244m"
let cmd_duration_ms_bg_color_24 = (ansi -e { fg: "#000000" })
let cmd_duration_ms_bg_color_8 = (ansi black)
let runtime_colors = [
{ name: prompt_color_frame_and_connection, '8bit': $prompt_color_frame_and_connection_8, '24bit': $prompt_color_frame_and_connection_24 },
{ name: prompt_color_separator_same_color, '8bit': $prompt_color_separator_same_color_8, '24bit': $prompt_color_separator_same_color_24 },
{ name: left_separator_diff_color, '8bit': $left_prompt_separator_diff_color, '24bit': $left_prompt_separator_diff_color },
{ name: left_separator_same_color, '8bit': $left_prompt_separator_same_color, '24bit': $left_prompt_separator_same_color },
{ name: left_prefix, value: null },
{ name: left_suffix, value: null },
{ name: right_separator_diff_color, '8bit': $right_prompt_separator_diff_color, '24bit': $right_prompt_separator_diff_color },
{ name: right_separator_same_color, '8bit': $right_prompt_separator_same_color, '24bit': $right_prompt_separator_same_color },
{ name: right_prefix, value: null },
{ name: right_suffix, value: null },
{ name: cmd_bg_color, '8bit': $cmd_duration_bg_color_8, '24bit': $cmd_duration_bg_color_24 },
{ name: cmd_color, '8bit': $cmd_duration_color_8, '24bit': $cmd_duration_color_24 },
{ name: cmd_decimals, value: 2 },
{ name: cmd_icon, value: },
{ name: git_bg_color, '8bit': $git_bg_color_8, '24bit': $git_bg_color_24 },
{ name: git_bg_color_unstable, '8bit': $git_bg_color_unstable_8, '24bit': $git_bg_color_unstable_24 },
{ name: git_bg_color_urgent, '8bit': $git_bg_color_urgent_8 , '24bit': $git_bg_color_urgent_24 },
{ name: git_color_branch, '8bit': $git_color_branch_8, '24bit': $git_color_branch_24 },
{ name: git_color_conflicted, '8bit': $git_color_conflicted_8, '24bit': $git_color_conflicted_24 },
{ name: git_color_dirty, '8bit': $git_color_dirty_8, '24bit': $git_color_dirty_24 },
{ name: git_color_operation, '8bit': $git_color_operation_8, '24bit': $git_color_operation_24 },
{ name: git_color_staged, '8bit': $git_color_staged_8, '24bit': $git_color_staged_24 },
{ name: git_color_stash, '8bit': $git_color_stash_8, '24bit': $git_color_stash_24 },
{ name: git_color_untracked, '8bit': $git_color_untracked_8, '24bit': $git_color_untracked_24 },
{ name: git_color_upstream, '8bit': $git_color_upstream_8, '24bit': $git_color_upstream_24 },
{ name: os_bg_color, '8bit': $os_bg_color_8, '24bit': $os_bg_color_24 },
{ name: os_color, '8bit': $os_color_8, '24bit': $os_color_24 },
{ name: pwd_bg_color, '8bit': $pwd_bg_color_8, '24bit': $pwd_bg_color_24 },
{ name: pwd_color_anchors, '8bit': $pwd_color_anchors_8, '24bit': $pwd_color_anchors_24 },
{ name: pwd_color_dirs, '8bit': $pwd_color_dirs_8, '24bit': $pwd_color_dirs_24 },
{ name: pwd_color_truncated_dirs, '8bit': $pwd_color_truncated_dirs_8, '24bit': $pwd_color_truncated_dirs_24 },
{ name: pwd_icon, value: null },
{ name: pwd_icon_home, value: null },
{ name: pwd_icon_unwritable, value: null },
{ name: rustc_bg_color, '8bit': $rustc_bg_color_8, '24bit': $rustc_bg_color_24 },
{ name: rustc_color, '8bit': $rustc_color_8, '24bit': $rustc_color_24 },
{ name: rustc_icon, value: },
{ name: status_bg_color, '8bit': $status_bg_color_8, '24bit': $status_bg_color_24 },
{ name: status_bg_color_failure, '8bit': $status_bg_color_failure_8, '24bit': $status_bg_color_failure_24 },
{ name: status_color, '8bit': $status_color_8, '24bit': $status_color_24 },
{ name: status_color_failure, '8bit': $status_color_failure_8 , '24bit': $status_color_failure_24 },
{ name: status_icon, value: null },
{ name: status_icon_failure, value: null },
{ name: time_bg_color, '8bit': $time_bg_color_8, '24bit': $time_bg_color_24 },
{ name: time_color, '8bit': $time_color_8, '24bit': $time_color_24 },
{ name: time_format, value: null },
{ name: indicator_bg_color, '8bit': $indicator_bg_color_8, '24bit': $indicator_bg_color_24 },
{ name: indicator_color, '8bit': $indicator_color_8, '24bit': $indicator_color_24 },
{ name: terminal_color, '8bit': $terminal_color_8, '24bit': $terminal_color_24 },
{ name: terminal_bg_color, '8bit': $terminal_bg_color_8, '24bit': $terminal_bg_color_24 },
{name: cmd_duration_ms_color, '8bit': $cmd_duration_ms_color_8, '24bit': $cmd_duration_ms_color_24 },
{name: cmd_duration_ms_bg_color, '8bit': $cmd_duration_ms_bg_color_8, '24bit': $cmd_duration_ms_bg_color_24 },
]
# get the color from the $runtime_colors array
def get_color [name, mode] {
$runtime_colors | where name == $name | get $mode | get 0
}
######################################################
# Abbreviate home path for the prompt
def home_abbrev [os_name] {
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
if $is_home_in_path {
if ($os_name == "windows") {
let home = ($nu.home-path | str replace -ar '\\' '/')
let pwd = ($env.PWD | str replace -ar '\\' '/')
$pwd | str replace $home '~'
} else {
$env.PWD | str replace $nu.home-path '~'
}
} else {
$env.PWD | str replace -ar '\\' '/'
}
}
# get the operating system icon for the prompt
def get_os_icon [os] {
# f17c = tux, f179 = apple, f17a = windows
if ($os.name =~ macos) {
(char -u f179)
} else if ($os.name =~ windows) {
(char -u f17a)
} else if ($os.kernel_version =~ WSL) {
$'(char -u f17a)(char -u f17c)'
} else if ($os.family =~ unix) {
(char -u f17c)
} else {
''
}
}
# get the os segment for the prompt
def get_os_segment [os color_mode] {
let os_bg_color = (get_color os_bg_color $color_mode)
let os_color = (get_color os_color $color_mode)
let os_icon = (get_os_icon $os)
let transition_icon = $left_prompt_separator_diff_color
let transition_bg_color = (get_color pwd_bg_color $color_mode)
let transition_color = (get_color pwd_color_anchors $color_mode)
let os_segment = (
[
($os_color)
($os_bg_color)
(char space)
($os_icon)
(char space)
($transition_color)
($transition_bg_color)
($transition_icon)
(char space)
] | str join
)
$os_segment
}
# get the path segment for the prompt
def get_path_segment [os color_mode] {
let display_path = (home_abbrev $os.name)
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
let pwd_bg_color = (get_color pwd_bg_color $color_mode)
let pwd_color = (get_color pwd_color_dirs $color_mode)
let home_or_folder = (if $is_home_in_path { (char nf_house1) } else { (char nf_folder1) })
let path_segment = (
[
$home_or_folder
(char space) # space
$display_path # ~/src/forks/nushell
($pwd_color)
($pwd_bg_color)
(char space) # space
] | str join
)
$path_segment
}
# get the indicator segment for the prompt
def get_indicator_segment [os color_mode] {
let R = (ansi reset)
let indicator_color = (get_color indicator_color $color_mode)
let indicator_bg_color = (get_color indicator_bg_color $color_mode)
let indicator_segment = (
[
($indicator_color)
($indicator_bg_color)
(char nf_segment) # 
($R) # reset color
] | str join
)
$indicator_segment
}
# construct the left prompt
def get_left_prompt [os color_mode] {
let os_segment = (get_os_segment $os $color_mode)
let path_segment = (get_path_segment $os $color_mode)
let indicator_segment = (get_indicator_segment $os $color_mode)
$os_segment + $path_segment + $indicator_segment
}
# get the time segment for the prompt
def get_time_segment [os color_mode] {
let R = (ansi reset)
let time_bg_color = (get_color time_bg_color $color_mode)
let time_color = (get_color time_color $color_mode)
let time_segment = ([
(ansi { fg: $time_bg_color bg: $time_color})
(char nf_right_segment) #(char -u e0b2) # 
($time_color)
($time_bg_color)
(char space)
(date now | format date '%I:%M:%S %p')
(char space)
($R)
] | str join)
$time_segment
}
# get the status segment for the prompt
def get_status_segment [os color_mode] {
let R = (ansi reset)
# set status bg color to foreground since bg is dark
let fg_failure = (get_color status_bg_color_failure $color_mode)
let term_bg_color = (get_color terminal_bg_color $color_mode)
let cmd_dur_fg = (get_color cmd_duration_ms_color $color_mode)
let cmd_dur_bg = (get_color cmd_duration_ms_bg_color $color_mode)
let status_segment = (
[
(if $env.LAST_EXIT_CODE != 0 {
(ansi { fg: $fg_failure bg: $term_bg_color })
} else {
(ansi { fg: $cmd_dur_fg bg: $cmd_dur_bg })
})
(char nf_right_segment_thin)
(char space)
$env.LAST_EXIT_CODE
(char space)
($R)
] | str join
)
$status_segment
}
# get the execution segment for the prompt
def get_execution_time_segment [os color_mode] {
let R = (ansi reset)
let cmd_dur_fg = (get_color cmd_duration_ms_color $color_mode)
let cmd_dur_bg = (get_color cmd_duration_ms_bg_color $color_mode)
let execution_time_segment = (
[
($cmd_dur_fg)
($cmd_dur_bg)
# (ansi { fg: $cmd_dur_fg bg: $cmd_dur_bg })
(char nf_right_segment_thin)
(char space)
$env.CMD_DURATION_MS
(char space)
($R)
] | str join
)
$execution_time_segment
}
# construct the right prompt
def get_right_prompt [os color_mode] {
let status_segment = (get_status_segment $os $color_mode)
let execution_time_segment = (get_execution_time_segment $os $color_mode)
let time_segment = (get_time_segment $os $color_mode)
let exit_if = (if $env.LAST_EXIT_CODE != 0 { $status_segment })
[$exit_if $execution_time_segment $time_segment] | str join
}
# constructe the left and right prompt by color_mode (8bit or 24bit)
def get_prompt [color_mode] {
# modes = 8bit or 24bit
# let os = ((sys).host)
let os = $nu.os-info
let left_prompt = (get_left_prompt $os $color_mode)
let right_prompt = (get_right_prompt $os $color_mode)
# return in record literal syntax to be used kind of like a tuple
# so we don't have to run this script more than once per prompt
{
left_prompt: $left_prompt
right_prompt: $right_prompt
}
}

View File

@@ -0,0 +1,545 @@
# REQUIREMENTS #
# you definitely need nerd fonts https://www.nerdfonts.com
# nerd fonts repo https://github.com/ryanoasis/nerd-fonts
# i use "FiraCode Nerd Font Mono" on mac
#
# you also must have the engine-q gstat plugin installed and registered
# ATTRIBUTION #
# A little fancier prompt with git information
# inspired by https://github.com/xcambar/purs
# inspired by https://github.com/IlanCosman/tide
# inspired by https://github.com/JanDeDobbeleer/oh-my-posh
# Abbreviate home path
def home_abbrev [os_name] {
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
if $is_home_in_path {
if ($os_name =~ "windows") {
let home = ($nu.home-path | str replace -ar '\\' '/')
let pwd = ($env.PWD | str replace -ar '\\' '/')
$pwd | str replace $home '~'
} else {
$env.PWD | str replace $nu.home-path '~'
}
} else {
if ($os_name =~ "windows") {
# remove the C: from the path
$env.PWD | str replace -ar '\\' '/' | str substring 2..
} else {
$env.PWD
}
}
}
def path_abbrev_if_needed [apath term_width] {
# probably shouldn't do coloring here but since we're coloring
# only certain parts, it's kind of tricky to do it in another place
let T = (ansi { fg: "#BCBCBC" bg: "#3465A4"}) # truncated
let P = (ansi { fg: "#E4E4E4" bg: "#3465A4"}) # path
let PB = (ansi { fg: "#E4E4E4" bg: "#3465A4" attr: b}) # path bold
let R = (ansi reset)
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
if (($apath | str length) > ($term_width / 2)) {
# split out by path separator into tokens
# don't use psep here because in home_abbrev we're making them all '/'
let splits = ($apath | split row '/')
let splits_len = ($splits | length)
# get all the tokens except the last
let tokens = (1..<($splits_len - 1) | each {|x|
$"($T)((($splits) | get $x | split chars) | get 0)($R)"
})
# need an insert command
let tokens = ($tokens | prepend $"($T)~")
# append the last part of the path
let tokens = ($tokens | append $"($PB)($splits | last)($R)")
# collect
$tokens | str join $"($T)/"
} else {
let splits = ($apath | split row '/')
let splits_len = ($splits | length)
let apath_len = ($apath | str length)
if ($splits_len == 2 and $apath_len == 1) {
$"/($T)($R)"
} else if ($splits_len == 2) {
let top_part = ($splits | last)
let tokens = $"($PB)($top_part)($R)"
$tokens | str join $"($T)"
} else if ($splits.0 | is-empty) {
let top_part = ($splits | skip | first ($splits_len - 2))
let end_part = ($splits | last)
let tokens = ($top_part | each {|x|
$"($T)/(($x | split chars).0)($R)"
})
let tokens = ($tokens | append $"/($PB)($end_part)($R)")
$tokens | str join $"($T)"
} else {
let top_part = ($splits | first ($splits_len - 1))
let end_part = ($splits | last)
let tokens = ($top_part | each {|x|
if $x == '~' {
$"($T)(($x | split chars).0)($R)"
} else {
$"/($T)(($x | split chars).0)($R)"
}
})
let tokens = ($tokens | append $"/($PB)($end_part)($R)")
$tokens | str join $"($T)"
}
}
}
def get_index_change_count [gs] {
let index_new = ($gs | get idx_added_staged)
let index_modified = ($gs | get idx_modified_staged)
let index_deleted = ($gs | get idx_deleted_staged)
let index_renamed = ($gs | get idx_renamed)
let index_typechanged = ($gs | get idx_type_changed)
$index_new + $index_modified + $index_deleted + $index_renamed + $index_typechanged
}
def get_working_tree_count [gs] {
let wt_modified = ($gs | get wt_modified)
let wt_deleted = ($gs | get wt_deleted)
let wt_typechanged = ($gs | get wt_type_changed)
let wt_renamed = ($gs | get wt_renamed)
$wt_modified + $wt_deleted + $wt_typechanged + $wt_renamed
}
def get_conflicted_count [gs] {
($gs | get conflicts)
}
def get_untracked_count [gs] {
($gs | get wt_untracked)
}
def get_branch_name [gs] {
let br = ($gs | get branch)
if $br == "no_branch" {
""
} else {
$br
}
}
def get_ahead_count [gs] {
($gs | get ahead)
}
def get_behind_count [gs] {
($gs | get behind)
}
def get_icons_list [] {
{
AHEAD_ICON: (char branch_ahead), # "↑" 2191
BEHIND_ICON: (char branch_behind), # "↓" 2193
NO_CHANGE_ICON: (char branch_identical) # ≣ 2263
HAS_CHANGE_ICON: "*",
INDEX_CHANGE_ICON: "♦",
WT_CHANGE_ICON: "✚",
CONFLICTED_CHANGE_ICON: "✖",
UNTRACKED_CHANGE_ICON: (char branch_untracked) # ≢ 2262
INSERT_SYMBOL_ICON: "",
HAMBURGER_ICON: (char hamburger) # "≡" 2261
GITHUB_ICON: "", # f408
BRANCH_ICON: (char nf_branch) # "" e0a0
REBASE_ICON: "", # e728
TAG_ICON: "" # f412
}
}
def get_icon_by_name [name] {
get_icons_list | get $name
}
def get_os_icon [os] {
# f17c = tux, f179 = apple, f17a = windows
if ($os.name =~ macos) {
(char -u f179)
} else if ($os.name =~ windows) {
(char -u f17a)
} else if ($os.kernel_version =~ WSL) {
$'(char -u f17a)(char -u f17c)'
} else if ($os.family =~ unix) {
(char -u f17c)
} else {
''
}
}
# ╭─────────────────────┬───────────────╮
# │ idx_added_staged │ 0 │ #INDEX_NEW
# │ idx_modified_staged │ 0 │ #INDEX_MODIFIED
# │ idx_deleted_staged │ 0 │ #INDEX_DELETED
# │ idx_renamed │ 0 │ #INDEX_RENAMED
# │ idx_type_changed │ 0 │ #INDEX_TYPECHANGE
# │ wt_untracked │ 0 │ #WT_NEW
# │ wt_modified │ 0 │ #WT_MODIFIED
# │ wt_deleted │ 0 │ #WT_DELETED
# │ wt_type_changed │ 0 │ #WT_TYPECHANGE
# │ wt_renamed │ 0 │ #WT_RENAMED
# │ ignored │ 0 │
# │ conflicts │ 0 │ #CONFLICTED
# │ ahead │ 0 │
# │ behind │ 0 │
# │ stashes │ 0 │
# │ repo_name │ nushell │
# │ tag │ no_tag │
# │ branch │ main │
# │ remote │ upstream/main │
# ╰─────────────────────┴───────────────╯
def get_repo_status [gs os] {
let display_path = (path_abbrev_if_needed (home_abbrev $os.name) (term size).columns)
let branch_name = (get_branch_name $gs)
let ahead_cnt = (get_ahead_count $gs)
let behind_cnt = (get_behind_count $gs)
let index_change_cnt = (get_index_change_count $gs)
let wt_change_cnt = (get_working_tree_count $gs)
let conflicted_cnt = (get_conflicted_count $gs)
let untracked_cnt = (get_untracked_count $gs)
let has_no_changes = (
if ($index_change_cnt <= 0) and
($wt_change_cnt <= 0) and
($conflicted_cnt <= 0) and
($untracked_cnt <= 0) {
true
} else {
false
}
)
let GIT_BG = "#C4A000"
let GIT_FG = "#000000"
# let TERM_BG = "#0C0C0C"
# The multi-color fg colors are good if you just have a black background
let AHEAD_ICON = (get_icon_by_name AHEAD_ICON)
# let A_COLOR = (ansi { fg:"#00ffff" bg: ($GIT_BG) })
let A_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let BEHIND_ICON = (get_icon_by_name BEHIND_ICON)
# let B_COLOR = (ansi { fg:"#00ffff" bg: ($GIT_BG) })
let B_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let INDEX_CHANGE_ICON = (get_icon_by_name INDEX_CHANGE_ICON)
# let I_COLOR = (ansi { fg:"#00ff00" bg: ($GIT_BG) })
let I_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let CONFLICTED_CHANGE_ICON = (get_icon_by_name CONFLICTED_CHANGE_ICON)
# let C_COLOR = (ansi { fg:"#ff0000" bg: ($GIT_BG) })
let C_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let WT_CHANGE_ICON = (get_icon_by_name WT_CHANGE_ICON)
# let W_COLOR = (ansi { fg:"#ff00ff" bg: ($GIT_BG) })
let W_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let UNTRACKED_CHANGE_ICON = (get_icon_by_name UNTRACKED_CHANGE_ICON)
# let U_COLOR = (ansi { fg:"#ffff00" bg: ($GIT_BG) })
let U_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let NO_CHANGE_ICON = (get_icon_by_name NO_CHANGE_ICON)
# let N_COLOR = (ansi { fg:"#00ff00" bg: ($GIT_BG) })
let N_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let HAS_CHANGE_ICON = (get_icon_by_name HAS_CHANGE_ICON)
# let H_COLOR = (ansi { fg:"#ff0000" bg: ($GIT_BG) attr: b })
let H_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) attr: b })
let INSERT_SYMBOL_ICON = (get_icon_by_name INSERT_SYMBOL_ICON)
# let S_COLOR = (ansi { fg:"#00ffff" bg: ($GIT_BG) })
let S_COLOR = (ansi { fg: ($GIT_FG) bg: ($GIT_BG) })
let R = (ansi reset)
let repo_status = (
$"(
if ($ahead_cnt > 0) { $'($A_COLOR)($AHEAD_ICON)($ahead_cnt)($R)' }
)(
if ($behind_cnt > 0) { $'($B_COLOR)($BEHIND_ICON)($behind_cnt)($R)' }
)(
if ($index_change_cnt > 0) { $'($I_COLOR)($INDEX_CHANGE_ICON)($index_change_cnt)($R)' }
)(
if ($conflicted_cnt > 0) { $'($C_COLOR)($CONFLICTED_CHANGE_ICON)($conflicted_cnt)($R)' }
)(
if ($wt_change_cnt > 0) { $'($W_COLOR)($WT_CHANGE_ICON)($wt_change_cnt)($R)' }
)(
if ($untracked_cnt > 0) { $'($U_COLOR)($UNTRACKED_CHANGE_ICON)($untracked_cnt)($R)' }
)(
if $has_no_changes { $'($N_COLOR)($NO_CHANGE_ICON)($R)' } else { $'($H_COLOR)($HAS_CHANGE_ICON)($R)' }
)"
)
$repo_status
}
def git_left_prompt [gs os] {
# replace this 30 with whatever the width of the terminal is
let display_path = (path_abbrev_if_needed (home_abbrev $os.name) (term size).columns)
let branch_name = (get_branch_name $gs)
let R = (ansi reset)
# when reduce is available
# echo "one" "two" "three" | reduce { if ($acc | str starts-with 't') { $acc + $it } { $it }}
# some icons and the unicode char
# e0b0
# e0b1
# e0b2
# e0b3
# f1d3
# f07c or  f115
# f015 or  f7db
let GIT_BG = "#C4A000"
let GIT_FG = "#000000"
let TERM_BG = "#0C0C0C"
let repo_status = (get_repo_status $gs $os)
# build segments and then put together the segments for the prompt
let os_segment = ([
(ansi { fg: "#080808" bg: "#CED7CF"}) # os bg color
(char space) # space
(get_os_icon $os) # os icon
(char space) # space
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color transition
(char -u e0b0) # 
(char space) # space
] | str join)
let is_home_in_path = ($env.PWD | str starts-with $nu.home-path)
let path_segment = (if (($is_home_in_path) and ($branch_name == "")) {
[
(char -u f015) #  home icon
(char space) # space
$display_path # ~/src/forks/nushell
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color just to color the next space
(char space) # space
] | str join
} else {
[
(char -u f07c) #  folder icon
(char space) # space
$display_path # ~/src/forks/nushell
(ansi { fg: "#CED7CF" bg: "#3465A4"}) # color just to color the next space
(char space) # space
] | str join
})
let git_segment = (if ($branch_name != "") {
[
(ansi { fg: "#3465A4" bg: "#4E9A06"}) # color
(char -u e0b0) # 
(char space) # space
(ansi { fg: $TERM_BG bg: "#4E9A06"}) # color
# (char -u f1d3) # 
(char -u e0a0) # 
(char space) # space
($branch_name) # main
(char space) # space
(ansi { fg: "#4E9A06" bg: $GIT_BG}) # color
(char -u e0b0) # 
(char space) # space
($R) # reset color
$repo_status # repo status
] | str join
})
let git_right = false
let indicator_segment = (if ($branch_name == "" or $git_right) {
[
(ansi { fg: "#3465A4" bg: $TERM_BG}) # color
(char -u e0b0) # 
($R) # reset color
] | str join
} else {
[
(ansi { fg: $GIT_BG bg: $TERM_BG}) # color
(char -u e0b0) # 
($R) # reset color
] | str join
})
# assemble all segments for final prompt printing
[
$os_segment
$path_segment
(if ($git_right == false) {
$git_segment
})
$indicator_segment
] | str join
}
def git_right_prompt [gs os] {
# right prompt ideas
# 1. just the time on the right
# 2. date and time on the right
# 3. git information on the right
# 4. maybe git and time
# 5. would like to get CMD_DURATION_MS going there too when it's implemented
# 6. all of the above, chosen by def parameters
let branch_name = (get_branch_name $gs)
let repo_status = (get_repo_status $gs $os)
let R = (ansi reset)
let TIME_BG = "#D3D7CF"
let TERM_FG = "#0C0C0C"
let GIT_BG = "#C4A000"
let GIT_FG = "#000000"
let TERM_BG = "#0C0C0C"
let TERM_FG_DEFAULT = "\e[39m"
let TERM_BG_DEFAULT = "\e[49m"
let datetime_segment = ([
(ansi { fg: $TIME_BG bg: $TERM_FG})
(char -u e0b2) # 
(ansi { fg: $TERM_FG bg: $TIME_BG})
(char space)
(date now | format date '%m/%d/%Y %I:%M:%S%.3f')
(char space)
($R)
] | str join)
let time_segment = ([
(ansi { fg: $TIME_BG bg: $TERM_FG})
(char -u e0b2) # 
(ansi { fg: $TERM_FG bg: $TIME_BG})
(char space)
(date now | format date '%I:%M:%S %p')
(char space)
($R)
] | str join)
let git_segment = (if ($branch_name != "") {
[
(ansi { fg: $GIT_BG bg: $TERM_BG}) # color
(char -u e0b2) # 
(ansi { fg: $TERM_FG bg: $GIT_BG}) # color
(char space) # space
$repo_status # repo status
(ansi { fg: $TERM_FG bg: $GIT_BG}) # color
(char space)
(ansi { fg: "#4E9A06" bg: $GIT_BG }) # color
(char -u e0b2) # 
(ansi { fg: $TERM_BG bg: "#4E9A06"}) # color
(char space) # space
# (char -u f1d3) # 
# (char -u e0a0) # 
(char nf_git_branch) # 
(char space) # space
$branch_name # main
(char space) # space
($R) # reset color
] | str join
})
let execution_time_segment = (
[
# (ansi { fg: "#606060" bg: "#191323"})
(ansi { fg: "#606060"})
$TERM_BG_DEFAULT
(char -u e0b3)
(char space)
$env.CMD_DURATION_MS
(char space)
($R)
] | str join
)
let status_segment = (
[
(if $env.LAST_EXIT_CODE != 0 {
(ansi { fg: "#CC0000" })
} else {
(ansi { fg: "#606060" })
})
(char -u e0b3)
(char space)
$env.LAST_EXIT_CODE
(char space)
($R)
] | str join
)
# 1. datetime - working
# $datetime_segment
# 2. time only - working
[
(if $env.LAST_EXIT_CODE != 0 {
$status_segment
})
$execution_time_segment
$time_segment
] | str join
# 3. git only - working
# $git_segment
# 4. git + time -> need to fix the transition
# [
# $git_segment
# $time_segment
# ] | str join
# 5. fernando wants this on the left prompt
# [
# $os_segment
# $time_segment
# $path_segment
# ]
}
export def git_prompt [] {
let gs = (gstat)
let os = $nu.os-info
let left_prompt = (git_left_prompt $gs $os)
let right_prompt = (git_right_prompt $gs $os)
# set the title of the window/tab
# Wezterm accepts:
# osc0 \x1b]0;
# osc1 \x1b]1;
# osc2 \x1b]2;
# the typical way to set the terminal title is:
# osc2 some_string bel aka (char osc)2;($some_string)(char bel) or "\u001b]2;($some_string)\a"
# bel is escape \a or \x7 or \u0007
# but i've also seen it as
# osc2 some_string string_terminator aka (char osc)2;($some_string)(ansi st) or "\u001b];($some_string)\\"
# where string_terminator is \
# so you might want to play around with these settings a bit
#let abbrev = ((path_abbrev_if_needed (home_abbrev $os.name) 30) | ansi strip)
# $"\u001b]0;($abbrev)"
# note that this isn't ending properly with a bel or a st, that's
# because it makes the string echo to the screen as an empty line
# turning off now since a similar thing is built into nushell + it breaks kitty
#$"(ansi osc)2;($abbrev)"
# return in record literal syntax to be used kind of like a tuple
# so we don't have to run this script more than once per prompt
{
left_prompt: $left_prompt
right_prompt: $right_prompt
}
#
# in the config.nu you would do something like
# use "c:\some\path\to\nu_scripts\prompt\oh-my.nu" git_prompt
# $env.PROMPT_COMMAND = { (git_prompt).left_prompt }
# $env.PROMPT_COMMAND_RIGHT = { (git_prompt).right_prompt }
# $env.PROMPT_INDICATOR = " "
}

View File

@@ -0,0 +1,462 @@
# panache-git
# An opinionated Git prompt for Nushell, styled after posh-git
#
# Quick Start:
# - Download this file (panache-git.nu)
# - In your Nushell config:
# - Import the main command from the panache-git.nu module file
# - Set panache-git as your prompt command
# - Disable the separate prompt indicator by setting it to an empty string
# - For example, with this file in your home directory:
# use ~/panache-git.nu main
# $env.PROMPT_COMMAND = {|| panache-git }
# $env.PROMPT_INDICATOR = {|| "" }
# - Restart Nushell
#
# For more documentation or to file an issue, see https://github.com/ehdevries/panache-git
# An opinionated Git prompt for Nushell, styled after posh-git
export def main [] {
let prompt = ($'(current-dir) (repo-styled)' | str trim)
$'($prompt)> '
}
# Get the current directory with home abbreviated
export def current-dir [] {
let current_dir = ($env.PWD)
let current_dir_relative_to_home = (
do --ignore-errors { $current_dir | path relative-to $nu.home-path } | str join
)
let in_sub_dir_of_home = ($current_dir_relative_to_home | is-not-empty)
let current_dir_abbreviated = (if $in_sub_dir_of_home {
$'~(char separator)($current_dir_relative_to_home)' | str replace -ar '\\' '/'
} else {
$current_dir | str replace -ar '\\' '/'
})
$'(ansi reset)($current_dir_abbreviated)'
}
# Get repository status as structured data
export def repo-structured [] {
let in_git_repo = (do { git rev-parse --abbrev-ref HEAD } | complete | get stdout | is-not-empty)
let status = (if $in_git_repo {
git --no-optional-locks status --porcelain=2 --branch | lines
} else {
[]
})
let on_named_branch = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.head')
| first
| str contains '(detached)'
| not $in
} else {
false
})
let branch_name = (if $on_named_branch {
$status
| where ($it | str starts-with '# branch.head')
| split column ' ' col1 col2 branch
| get branch
| first
} else {
''
})
let commit_hash = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.oid')
| split column ' ' col1 col2 full_hash
| get full_hash
| first
| str substring 0..7
} else {
''
})
let tracking_upstream_branch = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.upstream')
| str join
| is-not-empty
} else {
false
})
let upstream_exists_on_remote = (if $in_git_repo {
$status
| where ($it | str starts-with '# branch.ab')
| str join
| is-not-empty
} else {
false
})
let ahead_behind_table = (if $upstream_exists_on_remote {
$status
| where ($it | str starts-with '# branch.ab')
| split column ' ' col1 col2 ahead behind
} else {
[[]]
})
let commits_ahead = (if $upstream_exists_on_remote {
$ahead_behind_table
| get ahead
| first
| into int
} else {
0
})
let commits_behind = (if $upstream_exists_on_remote {
$ahead_behind_table
| get behind
| first
| into int
| math abs
} else {
0
})
let has_staging_or_worktree_changes = (if $in_git_repo {
$status
| where ($it | str starts-with '1') or ($it | str starts-with '2')
| str join
| is-not-empty
} else {
false
})
let has_untracked_files = (if $in_git_repo {
$status
| where ($it | str starts-with '?')
| str join
| is-not-empty
} else {
false
})
let has_unresolved_merge_conflicts = (if $in_git_repo {
$status
| where ($it | str starts-with 'u')
| str join
| is-not-empty
} else {
false
})
let staging_worktree_table = (if $has_staging_or_worktree_changes {
$status
| where ($it | str starts-with '1') or ($it | str starts-with '2')
| split column ' '
| get column2
| split column '' staging worktree --collapse-empty
} else {
[[]]
})
let staging_added_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where staging == 'A'
| length
} else {
0
})
let staging_modified_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where staging in ['M', 'R']
| length
} else {
0
})
let staging_deleted_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where staging == 'D'
| length
} else {
0
})
let untracked_count = (if $has_untracked_files {
$status
| where ($it | str starts-with '?')
| length
} else {
0
})
let worktree_modified_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where worktree in ['M', 'R']
| length
} else {
0
})
let worktree_deleted_count = (if $has_staging_or_worktree_changes {
$staging_worktree_table
| where worktree == 'D'
| length
} else {
0
})
let merge_conflict_count = (if $has_unresolved_merge_conflicts {
$status
| where ($it | str starts-with 'u')
| length
} else {
0
})
{
in_git_repo: $in_git_repo,
on_named_branch: $on_named_branch,
branch_name: $branch_name,
commit_hash: $commit_hash,
tracking_upstream_branch: $tracking_upstream_branch,
upstream_exists_on_remote: $upstream_exists_on_remote,
commits_ahead: $commits_ahead,
commits_behind: $commits_behind,
staging_added_count: $staging_added_count,
staging_modified_count: $staging_modified_count,
staging_deleted_count: $staging_deleted_count,
untracked_count: $untracked_count,
worktree_modified_count: $worktree_modified_count,
worktree_deleted_count: $worktree_deleted_count,
merge_conflict_count: $merge_conflict_count
}
}
# Get repository status as a styled string
export def repo-styled [] {
let status = (repo-structured)
let is_local_only = ($status.tracking_upstream_branch != true)
let upstream_deleted = (
$status.tracking_upstream_branch and
$status.upstream_exists_on_remote != true
)
let is_up_to_date = (
$status.upstream_exists_on_remote and
$status.commits_ahead == 0 and
$status.commits_behind == 0
)
let is_ahead = (
$status.upstream_exists_on_remote and
$status.commits_ahead > 0 and
$status.commits_behind == 0
)
let is_behind = (
$status.upstream_exists_on_remote and
$status.commits_ahead == 0 and
$status.commits_behind > 0
)
let is_ahead_and_behind = (
$status.upstream_exists_on_remote and
$status.commits_ahead > 0 and
$status.commits_behind > 0
)
let branch_name = (if $status.in_git_repo {
(if $status.on_named_branch {
$status.branch_name
} else {
['(' $status.commit_hash '...)'] | str join
})
} else {
''
})
let branch_styled = (if $status.in_git_repo {
(if $is_local_only {
(branch-local-only $branch_name)
} else if $is_up_to_date {
(branch-up-to-date $branch_name)
} else if $is_ahead {
(branch-ahead $branch_name $status.commits_ahead)
} else if $is_behind {
(branch-behind $branch_name $status.commits_behind)
} else if $is_ahead_and_behind {
(branch-ahead-and-behind $branch_name $status.commits_ahead $status.commits_behind)
} else if $upstream_deleted {
(branch-upstream-deleted $branch_name)
} else {
$branch_name
})
} else {
''
})
let has_staging_changes = (
$status.staging_added_count > 0 or
$status.staging_modified_count > 0 or
$status.staging_deleted_count > 0
)
let has_worktree_changes = (
$status.untracked_count > 0 or
$status.worktree_modified_count > 0 or
$status.worktree_deleted_count > 0 or
$status.merge_conflict_count > 0
)
let has_merge_conflicts = $status.merge_conflict_count > 0
let staging_summary = (if $has_staging_changes {
(staging-changes $status.staging_added_count $status.staging_modified_count $status.staging_deleted_count)
} else {
''
})
let worktree_summary = (if $has_worktree_changes {
(worktree-changes $status.untracked_count $status.worktree_modified_count $status.worktree_deleted_count)
} else {
''
})
let merge_conflict_summary = (if $has_merge_conflicts {
(unresolved-conflicts $status.merge_conflict_count)
} else {
''
})
let delimiter = (if ($has_staging_changes and $has_worktree_changes) {
('|' | bright-yellow)
} else {
''
})
let local_summary = (
$'($staging_summary) ($delimiter) ($worktree_summary) ($merge_conflict_summary)' | str trim
)
let local_indicator = (if $status.in_git_repo {
(if $has_worktree_changes {
('!' | red)
} else if $has_staging_changes {
('~' | bright-cyan)
} else {
''
})
} else {
''
})
let repo_summary = (
$'($branch_styled) ($local_summary) ($local_indicator)' | str trim
)
let left_bracket = ('[' | bright-yellow)
let right_bracket = (']' | bright-yellow)
(if $status.in_git_repo {
$'($left_bracket)($repo_summary)($right_bracket)'
} else {
''
})
}
# Helper commands to encapsulate style and make everything else more readable
def bright-cyan [] {
each { |it| $"(ansi -e '96m')($it)(ansi reset)" }
}
def bright-green [] {
each { |it| $"(ansi -e '92m')($it)(ansi reset)" }
}
def bright-red [] {
each { |it| $"(ansi -e '91m')($it)(ansi reset)" }
}
def bright-yellow [] {
each { |it| $"(ansi -e '93m')($it)(ansi reset)" }
}
def green [] {
each { |it| $"(ansi green)($it)(ansi reset)" }
}
def red [] {
each { |it| $"(ansi red)($it)(ansi reset)" }
}
def branch-local-only [
branch: string
] {
$branch | bright-cyan
}
def branch-upstream-deleted [
branch: string
] {
$'($branch) (char failed)' | bright-cyan
}
def branch-up-to-date [
branch: string
] {
$'($branch) (char identical_to)' | bright-cyan
}
def branch-ahead [
branch: string
ahead: int
] {
$'($branch) (char branch_ahead)($ahead)' | bright-green
}
def branch-behind [
branch: string
behind: int
] {
$'($branch) (char branch_behind)($behind)' | bright-red
}
def branch-ahead-and-behind [
branch: string
ahead: int
behind: int
] {
$'($branch) (char branch_behind)($behind) (char branch_ahead)($ahead)' | bright-yellow
}
def staging-changes [
added: int
modified: int
deleted: int
] {
$'+($added) ~($modified) -($deleted)' | green
}
def worktree-changes [
added: int
modified: int
deleted: int
] {
$'+($added) ~($modified) -($deleted)' | red
}
def unresolved-conflicts [
conflicts: int
] {
$'!($conflicts)' | red
}

View File

@@ -0,0 +1,71 @@
### configuration
```
use power.nu
use power_git.nu
power inject 0 1 {source: git, color: '#504945'}
use power_kube.nu
power inject 1 2 {source: kube, color: '#504945'} {
context: cyan
} {
reverse: true
separator: '@'
}
use power_utils.nu
power inject 0 1 {source: atuin, color: '#4C4B4A'}
power set time null { style: compact }
power init
```
or
```
$env.NU_POWER_SCHEMA = [
[
{source: pwd, color: '#353230'}
{source: git, color: '#504945'}
]
[
{source: proxy, color: 'dark_gray'}
{source: host, color: '#353230'}
{source: kube, color: '#504945'}
{source: time, color: '#666560'}
]
]
use power.nu
use power_git.nu
use power_kube.nu
power init
```
`$env.NU_POWER_SCHEMA` support configuring dynamically
## mode
- `$env.NU_POWER_MODE = '<power|fast>'` fast mode and default mode (experimental)
- `$env.NU_POWER_DECORATOR = '<power|plain>'` power mode and plain mode
- `$env.NU_POWER_FRAME = '<default|fill>'` two line prompt (experimental)
### benchmark
```
$env.NU_POWER_BENCHMARK = true
```
Then execute a few commands casually, such as pressing the Enter key continuously.
then execute
```
$env.NU_POWER_MODE = 'fast' # or 'power'
```
Go ahead and press enter,
Execute power timelog to analyze the results.
```
power analyze
```
## todo
- [x] source return `null` for hiding
- [ ] in fast mode, there is still a problem with hideable components on the left
- [x] proxy stat invalid in plain mode
- '<<' not longer hide separator in `fast` mode
- [ ] implement `power eject`
- [ ] `$env.config.menus[].maker` can be restored
- [x] support color theme
- [x] refactor: theme/decorator/frame/schema

View File

@@ -0,0 +1,618 @@
### pwd
def related [sub dir] {
if $sub == $dir {
return { related: '=', path: '' }
}
let suffix = (do --ignore-errors { $sub | path relative-to $dir })
if ($suffix | is-empty) {
{ related: '>', path: '' }
} else {
{ related: '<', path: $suffix}
}
}
export def "pwd_abbr" [] {
{|bg|
let pwd = ($env.PWD)
let to_home = (related $pwd $nu.home-path)
let cwd = if $to_home.related == '=' {
"~"
} else if $to_home.related == '>' {
$pwd
} else {
$'~(char separator)($to_home.path)'
}
mut dir_comp = ($cwd | split row (char separator))
if ($dir_comp | length) > 5 {
let first = ($dir_comp | first)
let last = ($dir_comp | last)
let body = (
$dir_comp
|range 1..-2
|each {|x| $x | str substring ..2 }
)
$dir_comp = ([$first $body $last] | flatten)
}
let theme = $env.NU_POWER_THEME.pwd
let style = if $to_home.related == '>' {
$theme.out_home
} else {
$theme.default
}
[$bg $"($style)($dir_comp | str join (char separator))"]
}
}
### proxy
export def proxy_stat [] {
{|bg|
let theme = $env.NU_POWER_THEME.proxy
if not (($env.https_proxy? | is-empty) and ($env.http_proxy? | is-empty)) {
[$bg '']
} else {
[$bg null]
}
}
}
### host
def host_abbr [] {
{|bg|
let theme = $env.NU_POWER_THEME.host
let n = (hostname | str trim)
let ucl = if (is-admin) {
$theme.is_admin
} else {
$theme.default
}
[$bg $"($ucl)($n)"]
}
}
### time
def time_segment [] {
{|bg|
let config = $env.NU_POWER_CONFIG.time
let theme = $env.NU_POWER_THEME.time
let format = match $config.style {
"compact" => { $'($theme.fst)%y%m%d($theme.snd)%w($theme.fst)%H%M%S' }
"rainbow" => {
let fmt = [w y m d H M S]
let color = ['1;93m' '1;35m' '1;34m' '1;36m' '1;32m' '1;33m' '1;91m']
$fmt
| enumerate
| each { |x| $"(ansi -e ($color | get $x.index))%($x.item)" }
| str join
}
_ => { $'($theme.fst)%y-%m-%d[%w]%H:%M:%S' }
}
[$bg $"(date now | format date $format)"]
}
}
### utils
def logtime [msg act] {
let start = (date now)
let result = (do $act)
# HACK: serialization
let period = ((date now) - $start | format duration ns | str replace ' ' '')
echo $'($start | format date '%Y-%m-%d_%H:%M:%S%z')(char tab)($period)(char tab)($msg)(char newline)'
| save -a ~/.cache/nushell/power_time.log
$result
}
export def wraptime [message action] {
if $env.NU_POWER_BENCHMARK? == true {
{|| logtime $message $action }
} else {
$action
}
}
def get_component [schema] {
let component = ($env.NU_PROMPT_COMPONENTS | get $schema.source)
if $env.NU_POWER_BENCHMARK? == true {
{|bg| logtime $'component ($schema.source)' {|| do $component $bg } }
} else {
$component
}
}
export def timelog [] {
open ~/.cache/nushell/power_time.log
| from tsv -n
| rename start duration message
| each {|x|
$x
| update start ($x.start | into datetime -f '%Y-%m-%d_%H:%M:%S%z')
| update duration ($x.duration | into duration)
}
}
export def analyze [] {
timelog
| group-by message
| transpose component metrics
| each {|x| $x | upsert metrics ($x.metrics | get duration | math avg)}
}
### prompt
def decorator [ ] {
match $env.NU_POWER_DECORATOR {
'plain' => {
{|s, direction?: string, color?: string = 'light_yellow', next_color?: string|
match $direction {
'|>'|'>' => {
let r = $'(ansi light_yellow)|'
$"($s)($r)"
}
'>>'|'<<' => {
$s
}
'<' => {
let l = $'(ansi light_yellow)|'
$"($l)($s)"
}
}
}
}
'power' => {
{|s, direction?: string, color?: string = 'light_yellow', next_color?: string|
match $direction {
'|>' => {
let l = (ansi -e {bg: $color})
let r = $'(ansi -e {fg: $color, bg: $next_color})(char nf_left_segment)'
$'($l)($s)($r)'
}
'>' => {
let r = $'(ansi -e {fg: $color, bg: $next_color})(char nf_left_segment)'
$'($s)($r)'
}
'>>' => {
let r = $'(ansi reset)(ansi -e {fg: $color})(char nf_left_segment)'
$'($s)($r)'
}
'<'|'<<' => {
let l = $'(ansi -e {fg: $color})(char nf_right_segment)(ansi -e {bg: $color})'
$'($l)($s)'
}
}
}
}
}
}
def left_prompt [segment] {
let decorator = (decorator)
let segment = ($segment
| each {|x|
[$x.color (get_component $x)]
})
{||
let segment = ($segment
| reduce -f [] {|x, acc|
let y = (do $x.1 $x.0)
if $y.1 == null {
$acc
} else {
$acc | append [$y]
}
})
let stop = ($segment | length) - 1
let cs = ($segment | each {|x| $x.0 } | append $segment.0.0 | range 1..)
$segment
| zip $cs
| enumerate
| each {|x|
if $x.index == $stop {
do $decorator $x.item.0.1 '>>' $x.item.0.0 $x.item.1
} else if $x.index == 0 {
do $decorator $x.item.0.1 '|>' $x.item.0.0 $x.item.1
} else {
do $decorator $x.item.0.1 '>' $x.item.0.0 $x.item.1
}
}
| str join
}
}
def right_prompt [segment] {
let decorator = (decorator)
let segment = ($segment
| each {|x|
[$x.color (get_component $x)]
})
{||
$segment
| reduce -f [] {|x,acc|
let y = (do $x.1 $x.0)
if $y.1 == null {
$acc
} else {
$acc | append [$y]
}
}
| enumerate
| each {|x|
if $x.index == 0 {
do $decorator $x.item.1 '<<' $x.item.0
} else {
do $decorator $x.item.1 '<' $x.item.0
}
}
| str join
}
}
def decorator_gen [
direction?: string
color?: string = 'light_yellow'
next_color?: string
] {
match $env.NU_POWER_DECORATOR {
'plain' => {
match $direction {
'|>'|'>' => {
let r = $'(ansi light_yellow)|'
{|s| $"($s)($r)" }
}
'>>' => {
{|s| $s }
}
'<'|'<<' => {
let l = $'(ansi light_yellow)|'
{|s| $"($l)($s)" }
}
}
}
'power' => {
match $direction {
'|>' => {
let l = $'(ansi -e {bg: $color})'
let r = $'(ansi -e {fg: $color, bg: $next_color})(char nf_left_segment)'
{|s| $'($l)($s)($r)' }
}
'>' => {
let r = $'(ansi -e {fg: $color, bg: $next_color})(char nf_left_segment)'
{|s| $'($s)($r)' }
}
'>>' => {
let r = $'(ansi reset)(ansi -e {fg: $color})(char nf_left_segment)'
{|s| $'($s)($r)' }
}
'<'|'<<' => {
let l = $'(ansi -e {fg: $color})(char nf_right_segment)(ansi -e {bg: $color})'
{|s| $'($l)($s)' }
}
}
}
}
}
def squash [thunk] {
mut r = ""
for t in $thunk {
let v = (do $t.0 null)
if ($v.1 != null) {
$r += (do $t.1 $v.1)
}
}
$r
}
def left_prompt_gen [segment] {
let stop = ($segment | length) - 1
let vs = ($segment | each {|x| [$x.color (get_component $x)]})
let cs = ($segment | each {|x| $x.color } | append $segment.0.color | range 1..)
let thunk = ($vs
| zip $cs
| enumerate
| each {|x|
if $x.index == $stop {
[$x.item.0.1 (decorator_gen '>>' $x.item.0.0 $x.item.1)]
} else if $x.index == 0 {
[$x.item.0.1 (decorator_gen '|>' $x.item.0.0 $x.item.1)]
} else {
[$x.item.0.1 (decorator_gen '>' $x.item.0.0 $x.item.1)]
}
})
{|| squash $thunk }
}
def right_prompt_gen [segment] {
let thunk = ( $segment
| each {|x| [$x.color (get_component $x)]}
| enumerate
| each {|x|
if $x.index == 0 {
[$x.item.1 (decorator_gen '<<' $x.item.0)]
} else {
[$x.item.1 (decorator_gen '<' $x.item.0)]
}
})
{|| squash $thunk }
}
def up_prompt [segment] {
let thunk = ($segment
| each {|y| $y | each {|x| get_component $x }
})
{ ||
let ss = ($thunk
| each {|y|
$y
| reduce -f [] {|x, acc|
let y = (do $x null)
if $y.1 == null {
$acc
} else {
$acc | append $y.1
}
}
| str join $'(ansi light_yellow)|'
})
# TODO: length of unicode char is 3
let fl = (((term size).columns - ($ss | str join ''| ansi strip | str length)) | math abs)
$ss | str join $"(ansi xterm_grey)('' | fill -c '-' -w $fl)(ansi reset)"
}
}
export def default_env [name value] {
if ($name in $env) {
$env | get $name
} else {
$value
}
}
export def --env init [] {
match $env.NU_POWER_FRAME {
'default' => {
match $env.NU_POWER_MODE {
'power' => {
$env.PROMPT_COMMAND = (wraptime
'dynamic left'
(left_prompt $env.NU_POWER_SCHEMA.0)
)
$env.PROMPT_COMMAND_RIGHT = (wraptime
'dynamic right'
(right_prompt $env.NU_POWER_SCHEMA.1)
)
}
'fast' => {
$env.PROMPT_COMMAND = (wraptime
'static left'
(left_prompt_gen $env.NU_POWER_SCHEMA.0)
)
$env.PROMPT_COMMAND_RIGHT = (wraptime
'static right'
(right_prompt_gen $env.NU_POWER_SCHEMA.1)
)
}
}
}
'fill' => {
$env.PROMPT_COMMAND = (up_prompt $env.NU_POWER_SCHEMA)
}
}
$env.PROMPT_INDICATOR = {||
match $env.NU_POWER_DECORATOR {
'plain' => { "> " }
_ => { " " }
}
}
$env.PROMPT_INDICATOR_VI_INSERT = {|| ": " }
$env.PROMPT_INDICATOR_VI_NORMAL = {|| "> " }
$env.PROMPT_MULTILINE_INDICATOR = {||
match $env.NU_POWER_DECORATOR {
'plain' => { "::: " }
_ => { $"(char haze) " }
}
}
$env.config = ( $env.config | update menus ($env.config.menus
| each {|x|
if ($x.marker in $env.NU_POWER_MENU_MARKER) {
let c = ($env.NU_POWER_MENU_MARKER | get $x.marker)
$x | upsert marker $'(ansi -e {fg: $c})(char nf_left_segment_thin) '
} else {
$x
}
}
))
hook
}
export def --env set [name theme config?] {
$env.NU_POWER_THEME = (if ($theme | is-empty) {
$env.NU_POWER_THEME
} else {
$env.NU_POWER_THEME
| upsert $name ($theme
| transpose k v
| reduce -f {} {|it, acc|
$acc | insert $it.k (ansi -e {fg: $it.v})
})
})
$env.NU_POWER_CONFIG = (if ($config | is-empty) {
$env.NU_POWER_CONFIG
} else {
$env.NU_POWER_CONFIG
| upsert $name ($config
| transpose k v
| reduce -f {} {|it, acc|
$acc | insert $it.k $it.v
})
})
}
export def --env register [name source theme config?] {
set $name $theme $config
$env.NU_PROMPT_COMPONENTS = (
$env.NU_PROMPT_COMPONENTS | upsert $name {|| $source }
)
}
export def --env inject [pos idx define theme? config?] {
let prev = ($env.NU_POWER_SCHEMA | get $pos)
let next = if $idx == 0 {
$prev | prepend $define
} else {
[
($prev | range 0..($idx - 1))
$define
($prev | range $idx..)
] | flatten
}
$env.NU_POWER_SCHEMA = (
$env.NU_POWER_SCHEMA
| update $pos $next
)
let kind = $define.source
if not ($theme | is-empty) {
let prev_theme = ($env.NU_POWER_THEME | get $kind)
let prev_cols = ($prev_theme | columns)
let next_theme = ($theme | transpose k v)
for n in $next_theme {
if $n.k in $prev_cols {
$env.NU_POWER_THEME = (
$env.NU_POWER_THEME | update $kind {|conf|
$conf | get $kind | update $n.k (ansi -e {fg: $n.v})
}
)
}
}
}
if not ($config | is-empty) {
let prev_cols = ($env.NU_POWER_CONFIG | get $kind | columns)
for n in ($config | transpose k v) {
if $n.k in $prev_cols {
$env.NU_POWER_CONFIG = (
$env.NU_POWER_CONFIG | update $kind {|conf|
$conf | get $kind | update $n.k $n.v
}
)
}
}
}
}
export def --env eject [] {
"power eject not implement"
}
export def --env hook [] {
$env.config = ( $env.config | upsert hooks.env_change { |config|
let init = [{|before, after| if not ($before | is-empty) { init } }]
$config.hooks.env_change
| upsert NU_POWER_MODE $init
| upsert NU_POWER_SCHEMA $init
| upsert NU_POWER_FRAME $init
| upsert NU_POWER_DECORATOR $init
| upsert NU_POWER_MENU_MARKER $init
| upsert NU_POWER_BENCHMARK [{ |before, after|
if not ($before | is-empty) {
init
rm -f ~/.cache/nushell/power_time.log
}
}]
# NU_POWER_THEME
})
}
export-env {
$env.NU_POWER_BENCHMARK = false
$env.NU_POWER_MODE = (default_env
NU_POWER_MODE
'power' # power | fast
)
$env.NU_POWER_SCHEMA = (default_env
NU_POWER_SCHEMA
[
[
{source: pwd, color: '#353230'}
]
[
{source: proxy, color: 'dark_gray'}
{source: host, color: '#353230'}
{source: time, color: '#666560'}
]
]
)
$env.NU_POWER_FRAME = (default_env
NU_POWER_FRAME
'default' # default | fill
)
$env.NU_POWER_DECORATOR = (default_env
NU_POWER_DECORATOR
'power' # power | plain
)
$env.NU_POWER_MENU_MARKER = (default_env
NU_POWER_MENU_MARKER
{
"| " : 'green'
": " : 'yellow'
"# " : 'blue'
"? " : 'red'
}
)
$env.NU_POWER_THEME = (default_env
NU_POWER_THEME
{
pwd: {
default: (ansi light_green_bold)
out_home: (ansi xterm_gold3b)
}
proxy: {
on: (ansi yellow)
}
host: {
is_admin: (ansi yellow)
default: (ansi blue)
}
time: {
fst: (ansi xterm_tan)
snd: (ansi xterm_aqua)
}
}
)
$env.NU_POWER_CONFIG = (default_env
NU_POWER_CONFIG
{
time: {
style: null
}
}
)
$env.NU_PROMPT_COMPONENTS = {
pwd: (pwd_abbr)
proxy: (proxy_stat)
host: (host_abbr)
time: (time_segment)
}
}

View File

@@ -0,0 +1,151 @@
### git
def _git_status [] {
# TODO: show-stash
let raw_status = (do -i { git --no-optional-locks status --porcelain=2 --branch | lines })
let stashes = (do -i { git stash list | lines | length })
mut status = {
idx_added_staged : 0
idx_modified_staged : 0
idx_deleted_staged : 0
idx_renamed : 0
idx_type_changed : 0
wt_untracked : 0
wt_modified : 0
wt_deleted : 0
wt_type_changed : 0
wt_renamed : 0
ignored : 0
conflicts : 0
ahead : 0
behind : 0
stashes : $stashes
repo_name : no_repository
tag : no_tag
branch : no_branch
remote : ''
}
if ($raw_status | is-empty) { return $status }
for s in $raw_status {
let r = ($s | split row ' ')
match $r.0 {
'#' => {
match ($r.1 | str substring 7..) {
'oid' => {
$status.commit_hash = ($r.2 | str substring 0..8)
}
'head' => {
$status.branch = $r.2
}
'upstream' => {
$status.remote = $r.2
}
'ab' => {
$status.ahead = ($r.2 | into int)
$status.behind = ($r.3 | into int | math abs)
}
}
}
'1'|'2' => {
match ($r.1 | str substring 0..1) {
'A' => {
$status.idx_added_staged += 1
}
'M' => {
$status.idx_modified_staged += 1
}
'R' => {
$status.idx_renamed += 1
}
'D' => {
$status.idx_deleted_staged += 1
}
'T' => {
$status.idx_type_changed += 1
}
}
match ($r.1 | str substring 1..2) {
'M' => {
$status.wt_modified += 1
}
'R' => {
$status.wt_renamed += 1
}
'D' => {
$status.wt_deleted += 1
}
'T' => {
$status.wt_type_changed += 1
}
}
}
'?' => {
$status.wt_untracked += 1
}
'u' => {
$status.conflicts += 1
}
}
}
$status
}
export def git_stat [] {
{|bg|
let status = (_git_status)
if $status.branch == 'no_branch' { return [$bg ''] }
let theme = $env.NU_POWER_THEME.git
let branch = if ($status.remote | is-empty) {
$'($theme.no_upstream)($status.branch)'
} else {
$'($theme.default)($status.branch)'
}
let summary = (
$env.NU_PROMPT_GIT_FORMATTER
| reduce -f "" {|x, acc|
let y = ($status | get $x.0)
if $y > 0 {
$acc + $"(ansi $'light_($x.2)_dimmed')($x.1)($y)"
} else {
$acc
}
})
[$bg $'($branch)($summary)']
}
}
export-env {
$env.NU_PROMPT_GIT_FORMATTER = (power default_env
NU_PROMPT_GIT_FORMATTER
[
[behind (char branch_behind) yellow]
[ahead (char branch_ahead) yellow]
[conflicts ! red]
[ignored _ purple]
[idx_added_staged + green]
[idx_modified_staged ~ green]
[idx_deleted_staged - green]
[idx_renamed % green]
[idx_type_changed * green]
[wt_untracked + red]
[wt_modified ~ red]
[wt_deleted - red]
[wt_renamed % red]
[wt_type_changed * red]
[stashes = blue]
]
)
power register git (git_stat) {
default : blue
no_upstream: red
}
}

View File

@@ -0,0 +1,61 @@
### kubernetes
export def ensure-cache [cache path action] {
let ts = (do -i { ls $path | sort-by modified | reverse | get 0.modified })
if ($ts | is-empty) { return false }
let tc = (do -i { ls $cache | get 0.modified })
if not (($cache | path exists) and ($ts < $tc)) {
mkdir ($cache | path dirname)
do $action | save -f $cache
}
open $cache
}
def "kube ctx" [] {
mut cache = ''
mut file = ''
if ($env.KUBECONFIG? | is-empty) {
$cache = $'($env.HOME)/.cache/nu-power/kube.json'
$file = $"($env.HOME)/.kube/config"
} else {
$cache = $"($env.HOME)/.cache/nu-power/kube-($env.KUBECONFIG | str replace -a '/' ':').json"
$file = $env.KUBECONFIG
}
if not ($file | path exists) { return null }
ensure-cache $cache $file {
do -i {
kubectl config get-contexts
| from ssv -a
| where CURRENT == '*'
| get 0
}
}
}
def kube_stat [] {
{|bg|
let ctx = (kube ctx)
if ($ctx | is-empty) {
[$bg ""]
} else {
let theme = $env.NU_POWER_THEME.kube
let config = $env.NU_POWER_CONFIG.kube
let p = if $config.reverse {
$"($theme.namespace)($ctx.NAMESPACE)($theme.separator)($config.separator)($theme.context)($ctx.NAME)"
} else {
$"($theme.context)($ctx.NAME)($theme.separator)($config.separator)($theme.namespace)($ctx.NAMESPACE)"
}
[$bg $"($p)"]
}
}
}
export-env {
power register kube (kube_stat) {
context: cyan
separator: purple
namespace: yellow
} {
reverse: false
separator: ':'
}
}

View File

@@ -0,0 +1,16 @@
export def atuin_stat [] {
{|bg|
let theme = $env.NU_POWER_THEME.atuin
if not ($env.ATUIN_SESSION? | is-empty) {
[$bg '']
} else {
['#504945' '']
}
}
}
export-env {
power register atuin (atuin_stat) {
on: white
}
}

View File

@@ -0,0 +1,21 @@
# use shells to to show workspaces
def workspaces [] {
shells | each {|item index|
if $item.active {
$"(ansi green)($index) "
} else {
$"(ansi blue)($index) "
}
}| str join
}
def create_right_prompt [] {
let time_segment = ([
(date now | format date '%r'),
" ",
(workspaces)
] | str join)
$time_segment
}
$env.PROMPT_COMMAND_RIGHT = { create_right_prompt }

View File

@@ -0,0 +1,22 @@
export def create_left_prompt [] {
let path_segment = if (is-admin) {
$"(ansi red_bold)($env.PWD)"
} else {
$"(ansi green_bold)($env.PWD)"
}
let duration_segment = do {
let duration_secs = ($env.CMD_DURATION_MS | into int) / 1000
if ($duration_secs >= 5) {
$"(ansi yellow_bold)($duration_secs | math round | into string | append "sec" | str join | into duration) "
} else {
""
}
}
let exit_code_segment = if ($env.LAST_EXIT_CODE == 0) {
""
} else {
$"(ansi red_bold)($env.LAST_EXIT_CODE) "
}
[$duration_segment, $exit_code_segment, $path_segment] | str join
}

View File

@@ -0,0 +1,11 @@
def create_left_prompt [] {
^starship prompt --cmd-duration $env.CMD_DURATION_MS $'--status=($env.LAST_EXIT_CODE)'
}
$env.PROMPT_COMMAND = { create_left_prompt }
# avoid same PROMPT_INDICATOR
$env.PROMPT_INDICATOR = { "" }
$env.PROMPT_INDICATOR_VI_INSERT = { ": " }
$env.PROMPT_INDICATOR_VI_NORMAL = { "〉" }
$env.PROMPT_MULTILINE_INDICATOR = { "::: " }