Author SHA1 Message Date
Robby RussellandClaude Opus 5.5 4c184381d0 fix(key-bindings): make numeric keypad work in application mode
We switch the terminal into application keypad mode (smkx) so terminfo
key bindings are valid, but that also makes the numeric keypad send
escape sequences (\eOp..\eOy, \eOM, ...) that were never bound. Keypad
Enter rang the bell instead of running the command.

Map each keypad sequence back to the plain key with `bindkey -s`, so it
behaves exactly like the main keyboard key in emacs, viins and vicmd.

Fixes #13928

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:03:38 -07:00
21 changed files with 76 additions and 339 deletions
+1 -1
View File
@@ -75,7 +75,7 @@ dependencies:
plugins/zsh-syntax-highlighting:
repo: zsh-users/zsh-syntax-highlighting
branch: master
version: e6683f20e5c38d3e5447f45845d04ddda75d0f23
version: 2fc57d63067c18b1100ecdbf684fa5baf49459d1
precopy: |
set -e
version=$(<.version)
+17
View File
@@ -110,6 +110,23 @@ bindkey -M emacs '^[[1;5D' backward-word
bindkey -M viins '^[[1;5D' backward-word
bindkey -M vicmd '^[[1;5D' backward-word
# [Keypad] - application mode (enabled above) makes the keypad send escape
# sequences instead of characters, so map them back to the plain keys
() {
local seq key
local -A keypad=(
'^[Op' '0' '^[Oq' '1' '^[Or' '2' '^[Os' '3' '^[Ot' '4'
'^[Ou' '5' '^[Ov' '6' '^[Ow' '7' '^[Ox' '8' '^[Oy' '9'
'^[On' '.' '^[Ol' ',' '^[Ok' '+' '^[Om' '-' '^[Oj' '*'
'^[Oo' '/' '^[OX' '=' '^[OM' '^M'
)
for seq key in "${(@kv)keypad}"; do
bindkey -M emacs -s "$seq" "$key"
bindkey -M viins -s "$seq" "$key"
bindkey -M vicmd -s "$seq" "$key"
done
}
bindkey '\ew' kill-region # [Esc-w] - Kill from the cursor to the mark
bindkey -s '\el' '^q ls\n' # [Esc-l] - run command: ls
+1 -1
View File
@@ -14,6 +14,6 @@ function copyfile {
return 1
fi
clipcopy $1 || return 1
clipcopy $1
echo ${(%):-"%B$1%b copied to clipboard."}
}
-74
View File
@@ -1,74 +0,0 @@
#!/usr/bin/env zsh
emulate -LR zsh
source "${1:-${0:A:h:h}/copyfile.plugin.zsh}"
test_dir=$(mktemp -d) || exit 1
trap 'command rm -rf -- "$test_dir"' EXIT
typeset -i clipboard_status=0 failures=0 tests=0
typeset output expected_output
clipcopy() {
print -r -- "$#" "$1" >| "$test_dir/call"
if (( clipboard_status )); then
print -u2 -r -- "clipboard backend failed"
return $clipboard_status
fi
command cat -- "$1" >| "$test_dir/clipboard"
}
check_copyfile() {
local description="$1" expected_status="$2" expected_stdout="$3" expected_stderr="$4"
shift 4
output=$(copyfile "$@" 2> "$test_dir/stderr")
local actual_status=$?
(( tests++ ))
if [[ $actual_status == $expected_status && $output == "$expected_stdout" && $(<"$test_dir/stderr") == "$expected_stderr" ]]; then
print -r -- "PASS: $description"
else
print -u2 -r -- "FAIL: $description"
print -u2 -r -- " status: $actual_status (expected $expected_status)"
print -u2 -r -- " stdout: ${(qqq)output} (expected ${(qqq)expected_stdout})"
print -u2 -r -- " stderr: $(<"$test_dir/stderr") (expected $expected_stderr)"
(( failures++ ))
fi
}
for filename in plain.txt 'file with spaces.txt' 'unicode-ą.txt' empty.txt; do
if [[ $filename == empty.txt ]]; then
: >| "$test_dir/$filename"
else
print -r -- "contents of $filename" >| "$test_dir/$filename"
fi
expected_output=${(%):-"%B$test_dir/$filename%b copied to clipboard."}
clipboard_status=0
check_copyfile "successful copy of $filename" 0 "$expected_output" '' "$test_dir/$filename"
if [[ $(<"$test_dir/call") != "1 $test_dir/$filename" ]] || ! command cmp -s -- "$test_dir/$filename" "$test_dir/clipboard"; then
print -u2 -r -- "FAIL: clipboard received a different argument or contents for $filename"
(( failures++ ))
fi
for clipboard_status in 1 42 127; do
check_copyfile "clipboard exit $clipboard_status for $filename" 1 '' 'clipboard backend failed' "$test_dir/$filename"
done
done
command rm -f -- "$test_dir/call"
check_copyfile 'missing argument' 1 'Usage: copyfile <file>' ''
check_copyfile 'empty argument' 1 'Usage: copyfile <file>' '' ''
check_copyfile 'nonexistent file' 1 "Error: '$test_dir/missing.txt' is not a valid file." '' "$test_dir/missing.txt"
check_copyfile 'directory argument' 1 "Error: '$test_dir' is not a valid file." '' "$test_dir"
if [[ -e "$test_dir/call" ]]; then
print -u2 -r -- 'FAIL: invalid input invoked the clipboard backend'
(( failures++ ))
fi
print -r -- "$tests tests, $failures failures"
(( failures == 0 ))
+1 -3
View File
@@ -101,9 +101,7 @@ EOF
(*.tar.lz4) lz4 -c -d "$full_path" | tar xvf - ;;
(*.tar.lrz) (( $+commands[lrzuntar] )) && lrzuntar "$full_path" ;;
(*.gz) (( $+commands[pigz] )) && pigz -cdk "$full_path" > "${file:t:r}" || gunzip -ck "$full_path" > "${file:t:r}" ;;
(*.bz2)
(( $+commands[pbzip2] )) && pbzip2 -cd "$full_path" > "${file:t:r}" \
|| bunzip2 -c "$full_path" > "${file:t:r}" ;;
(*.bz2) (( $+commands[pbzip2] )) && pbzip2 -d "$full_path" || bunzip2 "$full_path" ;;
(*.xz) xzcat "$full_path" > "${file:t:r}" ;;
(*.lrz) (( $+commands[lrunzip] )) && lrunzip "$full_path" ;;
(*.lz4) lz4 -d "$full_path" ;;
-1
View File
@@ -191,7 +191,6 @@ plugins=(... git)
| `grmc` | `git rm --cached` |
| `gcount` | `git shortlog --summary -n` |
| `gsh` | `git show` |
| `gshs` | `git show --stat` |
| `gsps` | `git show --pretty=short --show-signature` |
| `gstall` | `git stash --all` |
| `gstu` | `git stash --include-untracked` |
-1
View File
@@ -392,7 +392,6 @@ alias grm='git rm'
alias grmc='git rm --cached'
alias gcount='git shortlog --summary --numbered'
alias gsh='git show'
alias gshs='git show --stat'
alias gsps='git show --pretty=short --show-signature'
alias gstall='git stash --all'
alias gstaa='git stash apply'
+1 -5
View File
@@ -4,11 +4,7 @@ globalias() {
# (A) makes it an array even if there's only one element
local word=${${(Az)LBUFFER}[-1]}
if [[ $GLOBALIAS_FILTER_VALUES[(Ie)$word] -eq 0 ]]; then
# Completion also considers quoted strings and assignment values as aliases.
# Only expand when the complete shell word is an alias name.
if (( ${+aliases[$word]} || ${+galiases[$word]} )); then
zle _expand_alias
fi
zle _expand_alias
zle expand-word
fi
zle self-insert
@@ -1,81 +0,0 @@
#!/usr/bin/env zsh
# Exercise the actual ZLE widgets in an isolated interactive shell.
emulate -L zsh
setopt err_return
zmodload zsh/zpty
zmodload zsh/zselect
local test_dir=$(mktemp -d)
export GLOBALIAS_TEST_PLUGIN=${0:A:h:h}/globalias.plugin.zsh
export GLOBALIAS_TEST_RESULT=$test_dir/result
wait_for_marker() {
local marker=$1 output received
local -i deadline=$(( SECONDS + 5 ))
while (( SECONDS < deadline )); do
if zpty -r globalias-test output; then
received+=$output
[[ $received == *$marker* ]] && return 0
else
zselect -t 1 || true
fi
done
print -u2 -r -- "Timed out waiting for ZLE: $received"
return 1
}
check_expansion() {
local input=$1 expected=$2 actual
zpty -w -n globalias-test "$input"$' \x18'
wait_for_marker GLOBALIAS_TEST_CAPTURED
actual=$(< "$GLOBALIAS_TEST_RESULT")
if [[ $actual != "$expected" ]]; then
print -u2 -r -- "Input: ${(qqq)input}"
print -u2 -r -- "Expected: ${(qqq)expected}; got: ${(qqq)actual}"
return 1
fi
}
{
cat > "$test_dir/setup.zsh" <<'SETUP'
autoload -Uz compinit
compinit -D
source "$GLOBALIAS_TEST_PLUGIN"
alias 1='cd -1'
alias ll='ls -l'
alias -g G='| grep'
GLOBALIAS_FILTER_VALUES=(ll)
_globalias_test_capture() {
print -rn -- "$BUFFER" > "$GLOBALIAS_TEST_RESULT"
print -r -- $'\nGLOBALIAS_TEST_CAPTURED'
BUFFER=
zle .accept-line
}
zle -N _globalias_test_capture
bindkey '^X' _globalias_test_capture
PS1='GLOBALIAS_TEST_READY> '
RPROMPT=
SETUP
zpty -b globalias-test zsh -dfi
zpty -w globalias-test "source ${(q)test_dir}/setup.zsh"
wait_for_marker GLOBALIAS_TEST_READY
check_expansion 'DEBUG=1' 'DEBUG=1 '
check_expansion 'DEBUG="1"' 'DEBUG="1" '
check_expansion "DEBUG='1'" "DEBUG='1' "
check_expansion '1' 'cd -1 '
check_expansion '"1"' '"1" '
check_expansion "'1'" "'1' "
check_expansion '\1' '\1 '
check_expansion 'echo G' 'echo | grep '
check_expansion 'DEBUG=1 1' 'DEBUG=1 cd -1 '
check_expansion 'll' 'll '
check_expansion 'echo $((1+2))' 'echo 3 '
check_expansion 'echo {a,b}' 'echo a b '
check_expansion '' ' '
print 'All globalias expansion tests passed.'
} always {
zpty -d globalias-test 2>/dev/null || true
rm -rf -- "$test_dir"
}
+1 -31
View File
@@ -5,40 +5,10 @@ Applies some fixes for some common issues encountered with [GPG's gpg-agent](htt
More specifically, this plugin:
- Updates the `GPG_TTY` environment variable before each shell execution.
- Updates the `SSH_AUTH_SOCK` environment variable if `gpg-agent` is your SSH
agent. By default the plugin checks whether `enable-ssh-support` is turned
on. The [`ssh-support` setting](#settings) lets you say so yourself.
- Updates the `SSH_AUTH_SOCK` environment variable if `enable-ssh-support` is turned on.
To use it, add `gpg-agent` to the plugins array of your zshrc file:
```zsh
plugins=(... gpg-agent)
```
## Settings
Every time a shell starts, the plugin runs `gpgconf --list-options gpg-agent` to
find out whether `enable-ssh-support` is turned on. That check can be slow. If
you already know the answer, state it with the `ssh-support` style and the
plugin skips the check. Set it in your zshrc, before Oh My Zsh is sourced.
If you don't use `gpg-agent` as your SSH agent:
```zsh
zstyle ':omz:plugins:gpg-agent' ssh-support no
```
The plugin then only sets `GPG_TTY`. It leaves `SSH_AUTH_SOCK` alone, and it
doesn't update the agent's tty before each command, which only SSH support
needs.
If you do use `gpg-agent` as your SSH agent:
```zsh
zstyle ':omz:plugins:gpg-agent' ssh-support yes
```
The plugin then sets `SSH_AUTH_SOCK` without checking. `enable-ssh-support`
still has to be set in your `gpg-agent.conf` for SSH to work.
Leave the style unset to keep the automatic check.
+1 -16
View File
@@ -1,16 +1,5 @@
export GPG_TTY=$TTY
# The ssh-support style says whether gpg-agent is used as the ssh agent.
# Setting it saves asking gpgconf, which can be slow.
zstyle -t ':omz:plugins:gpg-agent' ssh-support
_gpg_agent_ssh_support=$? # 0 if yes, 1 if no, 2 if not set
# Everything below is only needed for ssh support
if (( _gpg_agent_ssh_support == 1 )); then
unset _gpg_agent_ssh_support
return
fi
# Fix for passphrase prompt on the correct tty
# See https://www.gnupg.org/documentation/manuals/gnupg/Agent-Options.html#option-_002d_002denable_002dssh_002dsupport
function _gpg-agent_update-tty_preexec {
@@ -20,13 +9,9 @@ autoload -U add-zsh-hook
add-zsh-hook preexec _gpg-agent_update-tty_preexec
# If enable-ssh-support is set, fix ssh agent integration
if (( _gpg_agent_ssh_support == 0 )) \
|| [[ $(gpgconf --list-options gpg-agent 2>/dev/null \
| awk -F: '$1=="enable-ssh-support" {print $10}') = 1 ]]; then
if [[ $(gpgconf --list-options gpg-agent 2>/dev/null | awk -F: '$1=="enable-ssh-support" {print $10}') = 1 ]]; then
unset SSH_AGENT_PID
if [[ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]]; then
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
fi
fi
unset _gpg_agent_ssh_support
-15
View File
@@ -6,21 +6,6 @@ This plugin adds aliases and autocompletion for Laravel [Artisan](https://larave
plugins=(... laravel)
```
## Settings
By default the aliases run `php artisan`. To use a different PHP command, such as
`herd php` (which runs the PHP version the [Laravel Herd](https://herd.laravel.com) site
is isolated to, instead of Herd's global version) or a specific version like `php8.4`,
set this before Oh My Zsh is sourced:
```zsh
zstyle ':omz:plugins:laravel' php 'herd php'
```
`php` in the aliases below is then replaced with that command.
## Aliases
| Alias | Description |
|:-:|:-:|
| `artisan` | `php artisan` |
+39 -44
View File
@@ -1,56 +1,51 @@
#!zsh
# Command used to run artisan, e.g. `herd php` or `php8.4` (default: `php`)
zstyle -s ':omz:plugins:laravel' php _omz_laravel_php || _omz_laravel_php=php
alias artisan="$_omz_laravel_php artisan"
alias bob="$_omz_laravel_php artisan bob::build"
alias artisan='php artisan'
alias bob='php artisan bob::build'
# Development
alias pas="$_omz_laravel_php artisan serve"
alias pad="$_omz_laravel_php artisan dev"
alias pats="$_omz_laravel_php artisan test"
alias pas='php artisan serve'
alias pad='php artisan dev'
alias pats='php artisan test'
# Database
alias pam="$_omz_laravel_php artisan migrate"
alias pamf="$_omz_laravel_php artisan migrate:fresh"
alias pamfs="$_omz_laravel_php artisan migrate:fresh --seed"
alias pamr="$_omz_laravel_php artisan migrate:rollback"
alias pads="$_omz_laravel_php artisan db:seed"
alias padw="$_omz_laravel_php artisan db:wipe"
alias pam='php artisan migrate'
alias pamf='php artisan migrate:fresh'
alias pamfs='php artisan migrate:fresh --seed'
alias pamr='php artisan migrate:rollback'
alias pads='php artisan db:seed'
alias padw='php artisan db:wipe'
# Makers
alias pamm="$_omz_laravel_php artisan make:model"
alias pamc="$_omz_laravel_php artisan make:controller"
alias pams="$_omz_laravel_php artisan make:seeder"
alias pamt="$_omz_laravel_php artisan make:test"
alias pamfa="$_omz_laravel_php artisan make:factory"
alias pamp="$_omz_laravel_php artisan make:policy"
alias pame="$_omz_laravel_php artisan make:event"
alias pamj="$_omz_laravel_php artisan make:job"
alias paml="$_omz_laravel_php artisan make:listener"
alias pamn="$_omz_laravel_php artisan make:notification"
alias pampp="$_omz_laravel_php artisan make:provider"
alias pamcl="$_omz_laravel_php artisan make:class"
alias pamen="$_omz_laravel_php artisan make:enum"
alias pami="$_omz_laravel_php artisan make:interface"
alias pamtr="$_omz_laravel_php artisan make:trait"
alias pamv="$_omz_laravel_php artisan make:view"
alias pammig="$_omz_laravel_php artisan make:migration"
alias pamm='php artisan make:model'
alias pamc='php artisan make:controller'
alias pams='php artisan make:seeder'
alias pamt='php artisan make:test'
alias pamfa='php artisan make:factory'
alias pamp='php artisan make:policy'
alias pame='php artisan make:event'
alias pamj='php artisan make:job'
alias paml='php artisan make:listener'
alias pamn='php artisan make:notification'
alias pampp='php artisan make:provider'
alias pamcl='php artisan make:class'
alias pamen='php artisan make:enum'
alias pami='php artisan make:interface'
alias pamtr='php artisan make:trait'
alias pamv='php artisan make:view'
alias pammig='php artisan make:migration'
# Clears
alias pacac="$_omz_laravel_php artisan cache:clear"
alias pacoc="$_omz_laravel_php artisan config:clear"
alias pavic="$_omz_laravel_php artisan view:clear"
alias paroc="$_omz_laravel_php artisan route:clear"
alias paopc="$_omz_laravel_php artisan optimize:clear"
alias pacac='php artisan cache:clear'
alias pacoc='php artisan config:clear'
alias pavic='php artisan view:clear'
alias paroc='php artisan route:clear'
alias paopc='php artisan optimize:clear'
# queues
alias paqf="$_omz_laravel_php artisan queue:failed"
alias paqft="$_omz_laravel_php artisan queue:failed-table"
alias paql="$_omz_laravel_php artisan queue:listen"
alias paqr="$_omz_laravel_php artisan queue:retry"
alias paqt="$_omz_laravel_php artisan queue:table"
alias paqw="$_omz_laravel_php artisan queue:work"
unset _omz_laravel_php
alias paqf='php artisan queue:failed'
alias paqft='php artisan queue:failed-table'
alias paql='php artisan queue:listen'
alias paqr='php artisan queue:retry'
alias paqt='php artisan queue:table'
alias paqw='php artisan queue:work'
+8 -11
View File
@@ -1,5 +1,5 @@
if (( $+commands[mise] )); then
_mise_bin=$commands[mise]
_mise_bin=mise
elif [[ -x ~/.local/bin/mise ]]; then
_mise_bin=~/.local/bin/mise
else
@@ -8,6 +8,7 @@ fi
# Load mise hooks
eval "$($_mise_bin activate zsh)"
unset _mise_bin
# If the completion file doesn't exist yet, we need to autoload it and
# bind it to `mise`. Otherwise, compinit will have already done that.
@@ -17,13 +18,9 @@ if [[ ! -f "$ZSH_CACHE_DIR/completions/_mise" ]]; then
_comps[mise]=_mise
fi
# Generate completion only when missing/empty or stale relative to the mise binary.
if [[ ! -s "$ZSH_CACHE_DIR/completions/_mise" || "$_mise_bin" -nt "$ZSH_CACHE_DIR/completions/_mise" ]]; then
zmodload -F zsh/files b:zf_mv
() {
local TMPPREFIX="$ZSH_CACHE_DIR/completions/._mise"
zf_mv -f -- =( "$_mise_bin" completion zsh ) "$ZSH_CACHE_DIR/completions/_mise"
} &|
fi
unset _mise_bin
# Generate and load mise completion
zmodload -F zsh/files b:zf_mv
() {
local TMPPREFIX="$ZSH_CACHE_DIR/completions/._mise"
zf_mv -f -- =( mise completion zsh ) "$ZSH_CACHE_DIR/completions/_mise"
} &|
-2
View File
@@ -3,8 +3,6 @@
The primary job of this plugin is to provide `rbenv_prompt_info` which can be added to your theme to include Ruby
version and gemset information into your prompt.
Additionally, this plugin provides completion for the `rbenv` command. See also <https://github.com/rbenv/rbenv/#shell-completions>.
Some functionality of this plugin will not work unless you also have the rbenv plugin *gemset* installed.
https://github.com/jf/rbenv-gemset
+1 -8
View File
@@ -24,13 +24,6 @@ if [[ $FOUND_RBENV -ne 1 ]]; then
fi
if [[ $FOUND_RBENV -eq 1 ]]; then
rbenv_completions="${commands[rbenv]:A:h:h}/completions"
if [[ -f "$rbenv_completions/_rbenv" ]]; then
fpath+=("$rbenv_completions")
autoload -Uz _rbenv
compdef _rbenv rbenv
fi
eval "$(rbenv init --no-rehash - zsh)"
alias rubies="rbenv versions"
@@ -72,4 +65,4 @@ else
}
fi
unset FOUND_RBENV rbenvdirs dir rbenv_completions
unset FOUND_RBENV rbenvdirs dir
+2 -2
View File
@@ -14,9 +14,9 @@ plugins=(... ruby)
| ------- | -------------------------------------- | ---------------------------------------------------- |
| rb | `ruby` | The Ruby command |
| sgem | `sudo gem` | Run sudo gem on the system ruby, not the active ruby |
| rfind | `find . -name "*.rb" -print0 \| xargs -0 grep -n` | Find ruby file |
| rfind | `find . -name "*.rb" \| xargs grep -n` | Find ruby file |
| rrun | `ruby -e` | Execute some code: E.g: `rrun "puts 'Hello world!'"` |
| rserver | `ruby -run -e httpd . -p 8080` | Start HTTP Webrick serving local directory/files |
| rserver | `ruby -e httpd . -p 8080` | Start HTTP Webrick serving local directory/files |
| gein | `gem install` | Install a gem into the local repository |
| geun | `gem uninstall` | Uninstall gems from the local repository |
| geli | `gem list` | Display gems installed locally |
+1 -1
View File
@@ -2,7 +2,7 @@
alias sgem='sudo gem'
# Find ruby file
alias rfind='find . -name "*.rb" -print0 | xargs -0 grep -n'
alias rfind='find . -name "*.rb" | xargs grep -n'
# Shorthand Ruby
alias rb="ruby"
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/env zsh
emulate -LR zsh
source "${1:-${0:A:h:h}/ruby.plugin.zsh}"
test_dir=$(mktemp -d) || exit 1
trap 'command rm -rf -- "$test_dir"' EXIT
typeset -i failures=0 tests=0
typeset filename output
for filename in plain.rb 'file with spaces.rb' "single'quote.rb" 'double"quote.rb' $'line\nbreak.rb' 'unicode-ą.rb'; do
print -r -- 'needle' >| "$test_dir/$filename"
output=$(cd "$test_dir" && rfind needle 2> "$test_dir/stderr")
actual_status=$?
(( tests++ ))
if [[ $actual_status == 0 && $output == '1:needle' && ! -s "$test_dir/stderr" ]]; then
print -r -- "PASS: ${(qqq)filename}"
else
print -u2 -r -- "FAIL: ${(qqq)filename} (status $actual_status)"
print -u2 -r -- " stdout: ${(qqq)output}"
print -u2 -r -- " stderr: $(<"$test_dir/stderr")"
(( failures++ ))
fi
command rm -- "$test_dir/$filename"
done
print -r -- "$tests tests, $failures failures"
(( failures == 0 ))
@@ -1240,14 +1240,9 @@ _zsh_highlight_main_highlighter_check_path()
fi
tmp_path=$tmp_path:a
local candidate_new_tmp_path
while [[ $tmp_path != / ]]; do
[[ -n ${(M)ZSH_HIGHLIGHT_DIRS_BLACKLIST:#$tmp_path} ]] && return 1
candidate_new_tmp_path=${tmp_path:h}
if [[ $candidate_new_tmp_path == $tmp_path ]]; then
break
fi
tmp_path=${candidate_new_tmp_path}
tmp_path=$tmp_path:h
done
if (( in_command_position )); then
+1 -4
View File
@@ -274,14 +274,11 @@ function handle_update() {
# Ask for confirmation and only update on 'y', 'Y' or Enter
# Otherwise just show a reminder for how to update
# ㅛ (U+315B) is the character produced at the Y key position on the Korean
# Dubeolsik layout, so it is accepted too to avoid cancelling the update
# when a Korean IME is active.
printf "[oh-my-zsh] Would you like to update? [Y/n] "
read -r -k 1 option
[[ "$option" = $'\n' ]] || echo
case "$option" in
[yY$'\n']|$'ㅛ') update_ohmyzsh ;;
[yY$'\n']) update_ohmyzsh ;;
[nN]) update_last_updated_file ;&
*) echo "[oh-my-zsh] You can update manually by running \`omz update\`" ;;
esac