diff --git a/prompt/oh-my.nu b/prompt/oh-my.nu new file mode 100644 index 0000000..7fda8ae --- /dev/null +++ b/prompt/oh-my.nu @@ -0,0 +1,362 @@ +# 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 = ($nu.cwd | str starts-with $nu.home-path) + if ($is_home_in_path == $true) { + if ($os == "Windows") { + let home = ($nu.home-path | str find-replace -a '\\' '/') + let pwd = ($nu.cwd | str find-replace -a '\\' '/') + $pwd | str find-replace $home '~' + } else { + $nu.cwd | str find-replace $nu.home-path '~' + } + } else { + $nu.cwd + } +} + +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) + + if (($apath | str length) > ($term_width / 2)) { + # split out by path separator into tokens + let splits = ($apath | split row (char psep)) + + # get all the tokens except the last + let tokens = (for x in 1..(($splits | length) - 2) { + $"($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 | get 0)($R)") + + # collect + $tokens | str collect $"($T)/" + } else { + # $"($P)($apath)($R)" + + # FIXME: This is close but it fails with folder with space. I'm not sure why. + # let splits = ($apath | split row (char psep)) + # let splits_len = ($splits | length) + # let tokens = (for x in 0..($splits_len - 1) { + # if ($x < ($splits_len - 1)) { + # $"/($T)(($splits | get $x | split chars).0)($R)" + # } + # }) + # let tokens = ($tokens | append $"/($PB)(($splits | last).0)($R)") + # $tokens | str collect $"($T)" + + # FIXME: This is close but it fails with folder with space. I'm not sure why. + # cd "/Applications/Hex Fiend.app/" + #    ~/H/A/Hex Fiend.app  + # should be + #    ~/A/Hex Fiend.app  + let splits = ($apath | split row (char psep)) + let splits_len = ($splits | length) + if ($splits_len == 0) { + # We're at / on the file system + $"/($T)" + } else { + let top_part = ($splits | first ($splits_len - 1)) + let end_part = ($splits | last) + let tokens = (for x in $top_part { + $"/($T)(($x | split chars).0)($R)" + }) + let tokens = ($tokens | append $"/($PB)($end_part.0)($R)") + $tokens | str collect $"($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: "↑", # 2191 + BEHIND_ICON: "↓", # 2193 + NO_CHANGE_ICON: "✔", + HAS_CHANGE_ICON: "*", + INDEX_CHANGE_ICON: "♦", + WT_CHANGE_ICON: "✚", + CONFLICTED_CHANGE_ICON: "✖", + UNTRACKED_CHANGE_ICON: "…", + INSERT_SYMBOL_ICON: "❯", + HAMBURGER_ICON: "≡", # 2261 + NOT_HAMBURGER_ICON: "≢", # 2262 + GITHUB_ICON: "", # f408 + BRANCH_ICON: "", # e0a0 + REBASE_ICON: "", # e728 + TAG_ICON: "" # f412 + } +} + +def get_icon_by_name [name] { + do -i { get_icons_list | get $name } +} + +def get_os_icon [os] { + # f17c = tux, f179 = apple, f17a = windows + if ($os == Darwin) { + (char -u f179) + } else if ($os =~ Linux) { + (char -u f17c) + } else if ($os == Windows) { + (char -u f17a) + } 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 │ +# │ branch │ main │ +# │ remote │ upstream/main │ +# ╰─────────────────────┴───────────────╯ + +def git_prompt [] { + let gs = (gstat) + let os = ((sys).host.name) + # replace this 30 with whatever the width of the terminal is + let display_path = (path_abbrev_if_needed (home_abbrev $os) 30) + 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) && + ($wt_change_cnt <= 0) && + ($conflicted_cnt <= 0) && + ($untracked_cnt <= 0) { + $true + } else { + $false + } + ) + + # 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" + + # 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 == $true) { $'($N_COLOR)($NO_CHANGE_ICON)($R)' } else { $'($H_COLOR)($HAS_CHANGE_ICON)($R)' } + )" + ) + + # 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 collect) + + let is_home_in_path = ($nu.cwd | str starts-with $nu.home-path) + let path_segment = (if (($is_home_in_path) && ($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 collect + } 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 collect + }) + + 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 collect + }) + + let indicator_segment = (if ($branch_name == "") { + [ + (ansi { fg: "#3465A4" bg: ($TERM_BG)}) # color + (char -u e0b0) #  + ($R) # reset color + ] | str collect + } else { + [ + (ansi { fg: ($GIT_BG) bg: ($TERM_BG)}) # color + (char -u e0b0) #  + ($R) # reset color + ] | str collect + }) + + # assemble all segments for final prompt printing + [ + ($os_segment) + ($path_segment) + ($git_segment) + ($indicator_segment) + ] | str collect +}