cli.zsh 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. #!/usr/bin/env zsh
  2. function omz {
  3. [[ $# -gt 0 ]] || {
  4. _omz::help
  5. return 1
  6. }
  7. local command="$1"
  8. shift
  9. # Subcommand functions start with _ so that they don't
  10. # appear as completion entries when looking for `omz`
  11. (( $+functions[_omz::$command] )) || {
  12. _omz::help
  13. return 1
  14. }
  15. _omz::$command "$@"
  16. }
  17. function _omz {
  18. local -a cmds subcmds
  19. cmds=(
  20. 'changelog:Print the changelog'
  21. 'help:Usage information'
  22. 'plugin:Manage plugins'
  23. 'pr:Manage Oh My Zsh Pull Requests'
  24. 'theme:Manage themes'
  25. 'update:Update Oh My Zsh'
  26. )
  27. if (( CURRENT == 2 )); then
  28. _describe 'command' cmds
  29. elif (( CURRENT == 3 )); then
  30. case "$words[2]" in
  31. changelog) local -a refs
  32. refs=("${(@f)$(command git -C "$ZSH" for-each-ref --format="%(refname:short):%(subject)" refs/heads refs/tags)}")
  33. _describe 'command' refs ;;
  34. plugin) subcmds=(
  35. 'disable:Disable plugin(s)'
  36. 'enable:Enable plugin(s)'
  37. 'info:Get plugin information'
  38. 'list:List plugins'
  39. 'load:Load plugin(s)'
  40. )
  41. _describe 'command' subcmds ;;
  42. pr) subcmds=('clean:Delete all Pull Request branches' 'test:Test a Pull Request')
  43. _describe 'command' subcmds ;;
  44. theme) subcmds=('list:List themes' 'set:Set a theme in your .zshrc file' 'use:Load a theme')
  45. _describe 'command' subcmds ;;
  46. esac
  47. elif (( CURRENT == 4 )); then
  48. case "${words[2]}::${words[3]}" in
  49. plugin::(disable|enable|load))
  50. local -aU valid_plugins
  51. if [[ "${words[3]}" = disable ]]; then
  52. # if command is "disable", only offer already enabled plugins
  53. valid_plugins=($plugins)
  54. else
  55. valid_plugins=("$ZSH"/plugins/*/{_*,*.plugin.zsh}(.N:h:t) "$ZSH_CUSTOM"/plugins/*/{_*,*.plugin.zsh}(.N:h:t))
  56. # if command is "enable", remove already enabled plugins
  57. [[ "${words[3]}" = enable ]] && valid_plugins=(${valid_plugins:|plugins})
  58. fi
  59. _describe 'plugin' valid_plugins ;;
  60. plugin::info)
  61. local -aU plugins=("$ZSH"/plugins/*/{_*,*.plugin.zsh}(.N:h:t) "$ZSH_CUSTOM"/plugins/*/{_*,*.plugin.zsh}(.N:h:t))
  62. _describe 'plugin' plugins ;;
  63. theme::(set|use))
  64. local -aU themes=("$ZSH"/themes/*.zsh-theme(.N:t:r) "$ZSH_CUSTOM"/**/*.zsh-theme(.N:r:gs:"$ZSH_CUSTOM"/themes/:::gs:"$ZSH_CUSTOM"/:::))
  65. _describe 'theme' themes ;;
  66. esac
  67. elif (( CURRENT > 4 )); then
  68. case "${words[2]}::${words[3]}" in
  69. plugin::(enable|disable|load))
  70. local -aU valid_plugins
  71. if [[ "${words[3]}" = disable ]]; then
  72. # if command is "disable", only offer already enabled plugins
  73. valid_plugins=($plugins)
  74. else
  75. valid_plugins=("$ZSH"/plugins/*/{_*,*.plugin.zsh}(.N:h:t) "$ZSH_CUSTOM"/plugins/*/{_*,*.plugin.zsh}(.N:h:t))
  76. # if command is "enable", remove already enabled plugins
  77. [[ "${words[3]}" = enable ]] && valid_plugins=(${valid_plugins:|plugins})
  78. fi
  79. # Remove plugins already passed as arguments
  80. # NOTE: $(( CURRENT - 1 )) is the last plugin argument completely passed, i.e. that which
  81. # has a space after them. This is to avoid removing plugins partially passed, which makes
  82. # the completion not add a space after the completed plugin.
  83. local -a args=(${words[4,$(( CURRENT - 1))]})
  84. valid_plugins=(${valid_plugins:|args})
  85. _describe 'plugin' valid_plugins ;;
  86. esac
  87. fi
  88. return 0
  89. }
  90. compdef _omz omz
  91. ## Utility functions
  92. function _omz::confirm {
  93. # If question supplied, ask it before reading the answer
  94. # NOTE: uses the logname of the caller function
  95. if [[ -n "$1" ]]; then
  96. _omz::log prompt "$1" "${${functrace[1]#_}%:*}"
  97. fi
  98. # Read one character
  99. read -r -k 1
  100. # If no newline entered, add a newline
  101. if [[ "$REPLY" != $'\n' ]]; then
  102. echo
  103. fi
  104. }
  105. function _omz::log {
  106. # if promptsubst is set, a message with `` or $()
  107. # will be run even if quoted due to `print -P`
  108. setopt localoptions nopromptsubst
  109. # $1 = info|warn|error|debug
  110. # $2 = text
  111. # $3 = (optional) name of the logger
  112. local logtype=$1
  113. local logname=${3:-${${functrace[1]#_}%:*}}
  114. # Don't print anything if debug is not active
  115. if [[ $logtype = debug && -z $_OMZ_DEBUG ]]; then
  116. return
  117. fi
  118. # Choose coloring based on log type
  119. case "$logtype" in
  120. prompt) print -Pn "%S%F{blue}$logname%f%s: $2" ;;
  121. debug) print -P "%F{white}$logname%f: $2" ;;
  122. info) print -P "%F{green}$logname%f: $2" ;;
  123. warn) print -P "%S%F{yellow}$logname%f%s: $2" ;;
  124. error) print -P "%S%F{red}$logname%f%s: $2" ;;
  125. esac >&2
  126. }
  127. ## User-facing commands
  128. function _omz::help {
  129. cat >&2 <<EOF
  130. Usage: omz <command> [options]
  131. Available commands:
  132. help Print this help message
  133. changelog Print the changelog
  134. plugin <command> Manage plugins
  135. pr <command> Manage Oh My Zsh Pull Requests
  136. theme <command> Manage themes
  137. update Update Oh My Zsh
  138. EOF
  139. }
  140. function _omz::changelog {
  141. local version=${1:-HEAD} format=${3:-"--text"}
  142. if ! command git -C "$ZSH" show-ref --verify refs/heads/$version &>/dev/null && \
  143. ! command git -C "$ZSH" show-ref --verify refs/tags/$version &>/dev/null && \
  144. ! command git -C "$ZSH" rev-parse --verify "${version}^{commit}" &>/dev/null; then
  145. cat >&2 <<EOF
  146. Usage: omz changelog [version]
  147. NOTE: <version> must be a valid branch, tag or commit.
  148. EOF
  149. return 1
  150. fi
  151. "$ZSH/tools/changelog.sh" "$version" "${2:-}" "$format"
  152. }
  153. function _omz::plugin {
  154. (( $# > 0 && $+functions[_omz::plugin::$1] )) || {
  155. cat >&2 <<EOF
  156. Usage: omz plugin <command> [options]
  157. Available commands:
  158. disable <plugin> Disable plugin(s)
  159. enable <plugin> Enable plugin(s)
  160. info <plugin> Get information of a plugin
  161. list List all available Oh My Zsh plugins
  162. load <plugin> Load plugin(s)
  163. EOF
  164. return 1
  165. }
  166. local command="$1"
  167. shift
  168. _omz::plugin::$command "$@"
  169. }
  170. function _omz::plugin::disable {
  171. if [[ -z "$1" ]]; then
  172. echo >&2 "Usage: omz plugin disable <plugin> [...]"
  173. return 1
  174. fi
  175. # Check that plugin is in $plugins
  176. local -a dis_plugins=()
  177. for plugin in "$@"; do
  178. if [[ ${plugins[(Ie)$plugin]} -eq 0 ]]; then
  179. _omz::log warn "plugin '$plugin' is not enabled."
  180. continue
  181. fi
  182. dis_plugins+=("$plugin")
  183. done
  184. # Exit if there are no enabled plugins to disable
  185. if [[ ${#dis_plugins} -eq 0 ]]; then
  186. return 1
  187. fi
  188. # Remove plugins substitution awk script
  189. local awk_subst_plugins="\
  190. gsub(/\s+(${(j:|:)dis_plugins})/, \"\") # with spaces before
  191. gsub(/(${(j:|:)dis_plugins})\s+/, \"\") # with spaces after
  192. gsub(/\((${(j:|:)dis_plugins})\)/, \"\") # without spaces (only plugin)
  193. "
  194. # Disable plugins awk script
  195. local awk_script="
  196. # if plugins=() is in oneline form, substitute disabled plugins and go to next line
  197. /^\s*plugins=\([^#]+\).*\$/ {
  198. $awk_subst_plugins
  199. print \$0
  200. next
  201. }
  202. # if plugins=() is in multiline form, enable multi flag and disable plugins if they're there
  203. /^\s*plugins=\(/ {
  204. multi=1
  205. $awk_subst_plugins
  206. print \$0
  207. next
  208. }
  209. # if multi flag is enabled and we find a valid closing parenthesis, remove plugins and disable multi flag
  210. multi == 1 && /^[^#]*\)/ {
  211. multi=0
  212. $awk_subst_plugins
  213. print \$0
  214. next
  215. }
  216. multi == 1 && length(\$0) > 0 {
  217. $awk_subst_plugins
  218. if (length(\$0) > 0) print \$0
  219. next
  220. }
  221. { print \$0 }
  222. "
  223. awk "$awk_script" ~/.zshrc > ~/.zshrc.new \
  224. && command mv -f ~/.zshrc ~/.zshrc.bck \
  225. && command mv -f ~/.zshrc.new ~/.zshrc
  226. # Exit if the new .zshrc file wasn't created correctly
  227. [[ $? -eq 0 ]] || {
  228. local ret=$?
  229. _omz::log error "error disabling plugins."
  230. return $ret
  231. }
  232. # Exit if the new .zshrc file has syntax errors
  233. if ! zsh -n ~/.zshrc; then
  234. _omz::log error "broken syntax in ~/.zshrc. Rolling back changes..."
  235. command mv -f ~/.zshrc ~/.zshrc.new
  236. command mv -f ~/.zshrc.bck ~/.zshrc
  237. return 1
  238. fi
  239. # Restart the zsh session if there were no errors
  240. _omz::log info "plugins disabled: ${(j:, :)dis_plugins}."
  241. # Old zsh versions don't have ZSH_ARGZERO
  242. local zsh="${ZSH_ARGZERO:-${functrace[-1]%:*}}"
  243. # Check whether to run a login shell
  244. [[ "$zsh" = -* || -o login ]] && exec -l "${zsh#-}" || exec "$zsh"
  245. }
  246. function _omz::plugin::enable {
  247. if [[ -z "$1" ]]; then
  248. echo >&2 "Usage: omz plugin enable <plugin> [...]"
  249. return 1
  250. fi
  251. # Check that plugin is not in $plugins
  252. local -a add_plugins=()
  253. for plugin in "$@"; do
  254. if [[ ${plugins[(Ie)$plugin]} -ne 0 ]]; then
  255. _omz::log warn "plugin '$plugin' is already enabled."
  256. continue
  257. fi
  258. add_plugins+=("$plugin")
  259. done
  260. # Exit if there are no plugins to enable
  261. if [[ ${#add_plugins} -eq 0 ]]; then
  262. return 1
  263. fi
  264. # Enable plugins awk script
  265. local awk_script="
  266. # if plugins=() is in oneline form, substitute ) with new plugins and go to the next line
  267. /^\s*plugins=\([^#]+\).*\$/ {
  268. sub(/\)/, \" $add_plugins&\")
  269. print \$0
  270. next
  271. }
  272. # if plugins=() is in multiline form, enable multi flag
  273. /^\s*plugins=\(/ {
  274. multi=1
  275. }
  276. # if multi flag is enabled and we find a valid closing parenthesis,
  277. # add new plugins and disable multi flag
  278. multi == 1 && /^[^#]*\)/ {
  279. multi=0
  280. sub(/\)/, \" $add_plugins&\")
  281. print \$0
  282. next
  283. }
  284. { print \$0 }
  285. "
  286. awk "$awk_script" ~/.zshrc > ~/.zshrc.new \
  287. && command mv -f ~/.zshrc ~/.zshrc.bck \
  288. && command mv -f ~/.zshrc.new ~/.zshrc
  289. # Exit if the new .zshrc file wasn't created correctly
  290. [[ $? -eq 0 ]] || {
  291. local ret=$?
  292. _omz::log error "error enabling plugins."
  293. return $ret
  294. }
  295. # Exit if the new .zshrc file has syntax errors
  296. if ! zsh -n ~/.zshrc; then
  297. _omz::log error "broken syntax in ~/.zshrc. Rolling back changes..."
  298. command mv -f ~/.zshrc ~/.zshrc.new
  299. command mv -f ~/.zshrc.bck ~/.zshrc
  300. return 1
  301. fi
  302. # Restart the zsh session if there were no errors
  303. _omz::log info "plugins enabled: ${(j:, :)add_plugins}."
  304. # Old zsh versions don't have ZSH_ARGZERO
  305. local zsh="${ZSH_ARGZERO:-${functrace[-1]%:*}}"
  306. # Check whether to run a login shell
  307. [[ "$zsh" = -* || -o login ]] && exec -l "${zsh#-}" || exec "$zsh"
  308. }
  309. function _omz::plugin::info {
  310. if [[ -z "$1" ]]; then
  311. echo >&2 "Usage: omz plugin info <plugin>"
  312. return 1
  313. fi
  314. local readme
  315. for readme in "$ZSH_CUSTOM/plugins/$1/README.md" "$ZSH/plugins/$1/README.md"; do
  316. if [[ -f "$readme" ]]; then
  317. (( ${+commands[less]} )) && less "$readme" || cat "$readme"
  318. return 0
  319. fi
  320. done
  321. if [[ -d "$ZSH_CUSTOM/plugins/$1" || -d "$ZSH/plugins/$1" ]]; then
  322. _omz::log error "the '$1' plugin doesn't have a README file"
  323. else
  324. _omz::log error "'$1' plugin not found"
  325. fi
  326. return 1
  327. }
  328. function _omz::plugin::list {
  329. local -a custom_plugins builtin_plugins
  330. custom_plugins=("$ZSH_CUSTOM"/plugins/*(-/N:t))
  331. builtin_plugins=("$ZSH"/plugins/*(-/N:t))
  332. # If the command is being piped, print all found line by line
  333. if [[ ! -t 1 ]]; then
  334. print -l ${(q-)custom_plugins} ${(q-)builtin_plugins}
  335. return
  336. fi
  337. if (( ${#custom_plugins} )); then
  338. print -P "%U%BCustom plugins%b%u:"
  339. print -l ${(q-)custom_plugins} | column -x
  340. fi
  341. if (( ${#builtin_plugins} )); then
  342. (( ${#custom_plugins} )) && echo # add a line of separation
  343. print -P "%U%BBuilt-in plugins%b%u:"
  344. print -l ${(q-)builtin_plugins} | column -x
  345. fi
  346. }
  347. function _omz::plugin::load {
  348. if [[ -z "$1" ]]; then
  349. echo >&2 "Usage: omz plugin load <plugin> [...]"
  350. return 1
  351. fi
  352. local plugins=("$@")
  353. local plugin base has_completion=0
  354. for plugin in $plugins; do
  355. if [[ -d "$ZSH_CUSTOM/plugins/$plugin" ]]; then
  356. base="$ZSH_CUSTOM/plugins/$plugin"
  357. elif [[ -d "$ZSH/plugins/$plugin" ]]; then
  358. base="$ZSH/plugins/$plugin"
  359. else
  360. _omz::log warn "plugin '$plugin' not found"
  361. continue
  362. fi
  363. # Check if its a valid plugin
  364. if [[ ! -f "$base/_$plugin" && ! -f "$base/$plugin.plugin.zsh" ]]; then
  365. _omz::log warn "'$plugin' is not a valid plugin"
  366. continue
  367. # It it is a valid plugin, add its directory to $fpath unless it is already there
  368. elif (( ! ${fpath[(Ie)$base]} )); then
  369. fpath=("$base" $fpath)
  370. fi
  371. # Check if it has completion to reload compinit
  372. if [[ -f "$base/_$plugin" ]]; then
  373. has_completion=1
  374. fi
  375. # Load the plugin
  376. if [[ -f "$base/$plugin.plugin.zsh" ]]; then
  377. source "$base/$plugin.plugin.zsh"
  378. fi
  379. done
  380. # If we have completion, we need to reload the completion
  381. # We pass -D to avoid generating a new dump file, which would overwrite our
  382. # current one for the next session (and we don't want that because we're not
  383. # actually enabling the plugins for the next session).
  384. # Note that we still have to pass -d "$_comp_dumpfile", so that compinit
  385. # doesn't use the default zcompdump location (${ZDOTDIR:-$HOME}/.zcompdump).
  386. if (( has_completion )); then
  387. compinit -D -d "$_comp_dumpfile"
  388. fi
  389. }
  390. function _omz::pr {
  391. (( $# > 0 && $+functions[_omz::pr::$1] )) || {
  392. cat >&2 <<EOF
  393. Usage: omz pr <command> [options]
  394. Available commands:
  395. clean Delete all PR branches (ohmyzsh/pull-*)
  396. test <PR_number_or_URL> Fetch PR #NUMBER and rebase against master
  397. EOF
  398. return 1
  399. }
  400. local command="$1"
  401. shift
  402. _omz::pr::$command "$@"
  403. }
  404. function _omz::pr::clean {
  405. (
  406. set -e
  407. builtin cd -q "$ZSH"
  408. # Check if there are PR branches
  409. local fmt branches
  410. fmt="%(color:bold blue)%(align:18,right)%(refname:short)%(end)%(color:reset) %(color:dim bold red)%(objectname:short)%(color:reset) %(color:yellow)%(contents:subject)"
  411. branches="$(command git for-each-ref --sort=-committerdate --color --format="$fmt" "refs/heads/ohmyzsh/pull-*")"
  412. # Exit if there are no PR branches
  413. if [[ -z "$branches" ]]; then
  414. _omz::log info "there are no Pull Request branches to remove."
  415. return
  416. fi
  417. # Print found PR branches
  418. echo "$branches\n"
  419. # Confirm before removing the branches
  420. _omz::confirm "do you want remove these Pull Request branches? [Y/n] "
  421. # Only proceed if the answer is a valid yes option
  422. [[ "$REPLY" != [yY$'\n'] ]] && return
  423. _omz::log info "removing all Oh My Zsh Pull Request branches..."
  424. command git branch --list 'ohmyzsh/pull-*' | while read branch; do
  425. command git branch -D "$branch"
  426. done
  427. )
  428. }
  429. function _omz::pr::test {
  430. # Allow $1 to be a URL to the pull request
  431. if [[ "$1" = https://* ]]; then
  432. 1="${1:t}"
  433. fi
  434. # Check the input
  435. if ! [[ -n "$1" && "$1" =~ ^[[:digit:]]+$ ]]; then
  436. echo >&2 "Usage: omz pr test <PR_NUMBER_or_URL>"
  437. return 1
  438. fi
  439. # Save current git HEAD
  440. local branch
  441. branch=$(builtin cd -q "$ZSH"; git symbolic-ref --short HEAD) || {
  442. _omz::log error "error when getting the current git branch. Aborting..."
  443. return 1
  444. }
  445. # Fetch PR onto ohmyzsh/pull-<PR_NUMBER> branch and rebase against master
  446. # If any of these operations fail, undo the changes made
  447. (
  448. set -e
  449. builtin cd -q "$ZSH"
  450. # Get the ohmyzsh git remote
  451. command git remote -v | while read remote url _; do
  452. case "$url" in
  453. https://github.com/ohmyzsh/ohmyzsh(|.git)) found=1; break ;;
  454. git@github.com:ohmyzsh/ohmyzsh(|.git)) found=1; break ;;
  455. esac
  456. done
  457. (( $found )) || {
  458. _omz::log error "could not found the ohmyzsh git remote. Aborting..."
  459. return 1
  460. }
  461. # Fetch pull request head
  462. _omz::log info "fetching PR #$1 to ohmyzsh/pull-$1..."
  463. command git fetch -f "$remote" refs/pull/$1/head:ohmyzsh/pull-$1 || {
  464. _omz::log error "error when trying to fetch PR #$1."
  465. return 1
  466. }
  467. # Rebase pull request branch against the current master
  468. _omz::log info "rebasing PR #$1..."
  469. command git rebase master ohmyzsh/pull-$1 || {
  470. command git rebase --abort &>/dev/null
  471. _omz::log warn "could not rebase PR #$1 on top of master."
  472. _omz::log warn "you might not see the latest stable changes."
  473. _omz::log info "run \`zsh\` to test the changes."
  474. return 1
  475. }
  476. _omz::log info "fetch of PR #${1} successful."
  477. )
  478. # If there was an error, abort running zsh to test the PR
  479. [[ $? -eq 0 ]] || return 1
  480. # Run zsh to test the changes
  481. _omz::log info "running \`zsh\` to test the changes. Run \`exit\` to go back."
  482. command zsh -l
  483. # After testing, go back to the previous HEAD if the user wants
  484. _omz::confirm "do you want to go back to the previous branch? [Y/n] "
  485. # Only proceed if the answer is a valid yes option
  486. [[ "$REPLY" != [yY$'\n'] ]] && return
  487. (
  488. set -e
  489. builtin cd -q "$ZSH"
  490. command git checkout "$branch" -- || {
  491. _omz::log error "could not go back to the previous branch ('$branch')."
  492. return 1
  493. }
  494. )
  495. }
  496. function _omz::theme {
  497. (( $# > 0 && $+functions[_omz::theme::$1] )) || {
  498. cat >&2 <<EOF
  499. Usage: omz theme <command> [options]
  500. Available commands:
  501. list List all available Oh My Zsh themes
  502. set <theme> Set a theme in your .zshrc file
  503. use <theme> Load a theme
  504. EOF
  505. return 1
  506. }
  507. local command="$1"
  508. shift
  509. _omz::theme::$command "$@"
  510. }
  511. function _omz::theme::list {
  512. local -a custom_themes builtin_themes
  513. custom_themes=("$ZSH_CUSTOM"/**/*.zsh-theme(-.N:r:gs:"$ZSH_CUSTOM"/themes/:::gs:"$ZSH_CUSTOM"/:::))
  514. builtin_themes=("$ZSH"/themes/*.zsh-theme(-.N:t:r))
  515. # If the command is being piped, print all found line by line
  516. if [[ ! -t 1 ]]; then
  517. print -l ${(q-)custom_themes} ${(q-)builtin_themes}
  518. return
  519. fi
  520. if (( ${#custom_themes} )); then
  521. print -P "%U%BCustom themes%b%u:"
  522. print -l ${(q-)custom_themes} | column -x
  523. fi
  524. if (( ${#builtin_themes} )); then
  525. (( ${#custom_themes} )) && echo # add a line of separation
  526. print -P "%U%BBuilt-in themes%b%u:"
  527. print -l ${(q-)builtin_themes} | column -x
  528. fi
  529. }
  530. function _omz::theme::set {
  531. if [[ -z "$1" ]]; then
  532. echo >&2 "Usage: omz theme set <theme>"
  533. return 1
  534. fi
  535. # Check that theme exists
  536. if [[ ! -f "$ZSH_CUSTOM/$1.zsh-theme" ]] \
  537. && [[ ! -f "$ZSH_CUSTOM/themes/$1.zsh-theme" ]] \
  538. && [[ ! -f "$ZSH/themes/$1.zsh-theme" ]]; then
  539. _omz::log error "%B$1%b theme not found"
  540. return 1
  541. fi
  542. # Enable theme in .zshrc
  543. local awk_script='
  544. !set && /^\s*ZSH_THEME=[^#]+.*$/ {
  545. set=1
  546. sub(/^\s*ZSH_THEME=[^#]+.*$/, "ZSH_THEME=\"'$1'\" # set by `omz`")
  547. print $0
  548. next
  549. }
  550. { print $0 }
  551. END {
  552. # If no ZSH_THEME= line was found, return an error
  553. if (!set) exit 1
  554. }
  555. '
  556. awk "$awk_script" ~/.zshrc > ~/.zshrc.new \
  557. || {
  558. # Prepend ZSH_THEME= line to .zshrc if it doesn't exist
  559. cat <<EOF
  560. ZSH_THEME="$1" # set by \`omz\`
  561. EOF
  562. cat ~/.zshrc
  563. } > ~/.zshrc.new \
  564. && command mv -f ~/.zshrc ~/.zshrc.bck \
  565. && command mv -f ~/.zshrc.new ~/.zshrc
  566. # Exit if the new .zshrc file wasn't created correctly
  567. [[ $? -eq 0 ]] || {
  568. local ret=$?
  569. _omz::log error "error setting theme."
  570. return $ret
  571. }
  572. # Exit if the new .zshrc file has syntax errors
  573. if ! zsh -n ~/.zshrc; then
  574. _omz::log error "broken syntax in ~/.zshrc. Rolling back changes..."
  575. command mv -f ~/.zshrc ~/.zshrc.new
  576. command mv -f ~/.zshrc.bck ~/.zshrc
  577. return 1
  578. fi
  579. # Restart the zsh session if there were no errors
  580. _omz::log info "'$1' theme set correctly."
  581. # Old zsh versions don't have ZSH_ARGZERO
  582. local zsh="${ZSH_ARGZERO:-${functrace[-1]%:*}}"
  583. # Check whether to run a login shell
  584. [[ "$zsh" = -* || -o login ]] && exec -l "${zsh#-}" || exec "$zsh"
  585. }
  586. function _omz::theme::use {
  587. if [[ -z "$1" ]]; then
  588. echo >&2 "Usage: omz theme use <theme>"
  589. return 1
  590. fi
  591. # Respect compatibility with old lookup order
  592. if [[ -f "$ZSH_CUSTOM/$1.zsh-theme" ]]; then
  593. source "$ZSH_CUSTOM/$1.zsh-theme"
  594. elif [[ -f "$ZSH_CUSTOM/themes/$1.zsh-theme" ]]; then
  595. source "$ZSH_CUSTOM/themes/$1.zsh-theme"
  596. elif [[ -f "$ZSH/themes/$1.zsh-theme" ]]; then
  597. source "$ZSH/themes/$1.zsh-theme"
  598. else
  599. _omz::log error "%B$1%b theme not found"
  600. return 1
  601. fi
  602. }
  603. function _omz::update {
  604. local last_commit=$(cd "$ZSH"; git rev-parse HEAD)
  605. # Run update script
  606. if [[ "$1" != --unattended ]]; then
  607. ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh" --interactive
  608. else
  609. ZSH="$ZSH" zsh -f "$ZSH/tools/upgrade.sh"
  610. fi
  611. # Update last updated file
  612. zmodload zsh/datetime
  613. echo "LAST_EPOCH=$(( EPOCHSECONDS / 60 / 60 / 24 ))" >! "${ZSH_CACHE_DIR}/.zsh-update"
  614. # Remove update lock if it exists
  615. command rm -rf "$ZSH/log/update.lock"
  616. # Restart the zsh session if there were changes
  617. if [[ "$1" != --unattended && "$(cd "$ZSH"; git rev-parse HEAD)" != "$last_commit" ]]; then
  618. # Old zsh versions don't have ZSH_ARGZERO
  619. local zsh="${ZSH_ARGZERO:-${functrace[-1]%:*}}"
  620. # Check whether to run a login shell
  621. [[ "$zsh" = -* || -o login ]] && exec -l "${zsh#-}" || exec "$zsh"
  622. fi
  623. }