- Introduction
- Preliminaries
- History file settings
- Zsh plugins with zinit
- Plugin settings
- Setting for colored man pages
- Keybindings
- Functions
- Set the terminal title
- Completion settings
- Zsh options
- Aliases
- Settings for external programs
For a while I was using zsh with the oh-my-zsh framework. However, as I became more advanced in using Linux, and started writing my own dotfiles, I realised I had no idea what this thing was doing, and decided to examine my shell using habits to see if I even needed oh-my-zsh at all. It turns out that I wasn’t really using much of what oh-my-zsh offers (I wasn’t even using any significant plugins), so I could get rid of this framework. I did however want to make my config literate, in order to be able to note the thought process behind each setting and where I found various code snippets, and there’s no better way to make one’s config literate than Emacs’s org mode, with which I’m already acquainted due to my other dotfiles.
I put in the beginning of this org file the string
#+property: header-args :tangle .zshrc
which tells the src blocks to get tangled (= exported) to a file called .zshrc, so that zsh will be able to see it. For the src blocks themselves, I’m using begin_src sh
as this gets syntax highlighting on both emacs itself and Github.
First disable the compinit taking place in the global /etc/zsh/zshrc
on Ubuntu and its derivatives, since that makes the first initialisation of the terminal a lot slower (recommended from here). This must be placed in .zshenv
.
# Skip the not really helping Ubuntu global compinit skip_global_compinit=1
The history file is necessary, as this is what’s used for reverse history search. I named it .zsh_history
instead of the default .histfile
and increased the number of entries to save according to their setting on oh-my-zsh.
HISTFILE=~/.zsh_history
HISTSIZE=50000
SAVEHIST=10000
I decided to use a plugin manager after all, since I started using more than one plugins. I first used zplug, since it seemed to me as the simplest to use. After a little while, I switched to zinit instead, a switch that came about for misguided tbh reasons, because the first initialisation of my terminal emulator after startup took half a minute (!) and thought zplug was the culprit, having read about zplug being way slower than other plugin managers on reddit. It turns out however this was the fault of the global zshrc file starting an additional compinit (something that either was probably fixed on oh-my-zsh, or I had thought long loading times were part and parcel of me using a framework, since I hadn’t noticed anything like this when I was using that). Since I made the switch however, I decided I might as well stay with zinit, even though I don’t use enough plugins to be able to feel the speed difference compared to zplug. After using for a little bit however, I think I prefer it over zplug because it actually has a separate command to update zinit itself (zinit self-update
), while zplug afaik implemented self-managing by having itself load as a plugin with special conditions. Also I ended up using some of the more complicated features I thought I wouldn’t use, and for those cases zinit’s ice
way (that is, putting any complexity modifiers on the previous line and to apply only for the plugin immediately afterwards) feels cleaner to me than attaching these invocations to the plugin loading itself, as zplug does.
First download and source zinit itself. Note:
After installing and reloading the shell compile Zinit with zinit self-update.
if [[ ! -d ~/.zinit ]];then
mkdir ~/.zinit
git clone https://github.com/zdharma-continuum/zinit.git ~/.zinit
fi
source ~/.zinit/zinit.zsh
Now add the plugins. I’m loading them with light
, because I don’t really care about the extra features load
provides.
I didn’t feel like rolling my own prompt, so I’m using the agkozak zsh prompt, which is really close to what I would have made anyways.
zinit light agkozak/agkozak-zsh-prompt
I also install zsh-auto-notify. This notifies when a process is over via the system notifications, so that I don’t have to look over to the terminal window all the time.
zinit light MichaelAquilina/zsh-auto-notify
I also install zsh-you-should-use. This plugin lets me know if there any aliases for the commands I typed in their full form.
zinit light MichaelAquilina/zsh-you-should-use
Put the git info on the left side of the agkozak zsh prompt (see here).
AGKOZAK_LEFT_PROMPT_ONLY=1
In regards to zsh-auto-notify
, don’t show notifications for the text editor micro and the music player mocp, since it’s normal that these run for more than 10 seconds.
AUTO_NOTIFY_IGNORE+=("micro")
AUTO_NOTIFY_IGNORE+=("mocp")
Set the message of zsh-you-should-use
to appear after the execution of the command.
export YSU_MESSAGE_POSITION="after"
Here I have the man pages be colored without any plugins. I used to use the colored-man-pages plugin from oh-my-zsh, but as I started looking into the oh-my-zsh codebase to see what I could safely discard or replace, I found
out that this didn’t need a plugin nor any sort of complicated code to be configured, as all it takes is to write export LESS_TERMCAP_
in the rc files (see Russell Parker | Adding Colors to man and Make the less Command More Powerful - Top Bug Net). Note that this draws the colors from the colors 1-8 of the used terminal emulator colorscheme, and thus the resulting look will depend on said colorscheme.
export LESS_TERMCAP_md=$(tput bold; tput setaf 1)
export LESS_TERMCAP_me=$(tput sgr0)
export LESS_TERMCAP_mb=$(tput bold; tput setaf 2)
export LESS_TERMCAP_us=$(tput bold; tput setaf 2)
export LESS_TERMCAP_ue=$(tput rmul; tput sgr0)
export LESS_TERMCAP_so=$(tput bold; tput setaf 3; tput setab 4)
export LESS_TERMCAP_se=$(tput rmso; tput sgr0)
Bindkey: Let’s just use emacs keybindings, as I’m not the kind to want to have vim keybindings everywhere.
bindkey -e
Found from here. Note that with the emacs keymap, M-b and M-f can be used for the same purpose, they’re just a little harder to remember.
bindkey "^[[1;5C" forward-word
bindkey "^[[1;5D" backward-word
A behavior that oh-my-zsh has that I wanted to keep was using the up and down arrow to find matches from the history for the command currently being typed e.g. pressing up/down after writing man z
will search in the history for commands beginning with man
and of which the first letter of second word was z. I found the necessary config here. For the record, the functions up-line-or-beginning-search
and down-line-or-beginning search
, which are enabled here, are, according to man zshcontrib
, similar to the builtin functions up-line-or-search
and down-line-or-search
, but they search for a line which matches the current line up to the current cursor position, rather than the first word on the line.
# start typing + [Up-Arrow] - fuzzy find history forward
if [[ "${terminfo[kcuu1]}" != "" ]]; then
autoload -U up-line-or-beginning-search
zle -N up-line-or-beginning-search
bindkey "${terminfo[kcuu1]}" up-line-or-beginning-search
fi
# start typing + [Down-Arrow] - fuzzy find history backward
if [[ "${terminfo[kcud1]}" != "" ]]; then
autoload -U down-line-or-beginning-search
zle -N down-line-or-beginning-search
bindkey "${terminfo[kcud1]}" down-line-or-beginning-search
fi
I found via man zshcontrib
about the existence of many functions that come with zsh but aren’t enabled by default. One of them is history-beginning-search-menu
. This invokes a menu with numbers including those history commands that match the string that was typed, and a match can be selected by typing the appropriate number. When invoking in addition the command with the same name, but with the suffix -end
, the cursor goes to the end of the command after the match has been selected, otherwise it remains after the matched characters, and when combined with the command with the suffix -space
, any space in the line is matched as a wildcard, thus effectively making the search fuzzy (see the relevant entry in ~man zshcontrib~ and the introductory comments of said function). I use here Ctrl-H for this functionality, as its default function is like backspace, which is kinda useless, as I can use the real backspace for that.
autoload -Uz history-beginning-search-menu-space-end history-beginning-search-menu
zle -N history-beginning-search-menu-space-end history-beginning-search-menu
bindkey "^H" history-beginning-search-menu-space-end
Another function I found via man zshcontrib
is edit-command-line
. This function edits the current command line using the visual editor, which seems rather useful, as when I want to compose long commands, I often write them on the text editor and then paste them on the command line. This helps in that after saving the changes and quitting from the temp file presented, the just written command appears in the command line, ready to be executed, no selecting and copying/pasting required. This function needs to be bound to a key, and I decided to bind it to Ctrl-x Ctrl-e, as I already have this in my muscle memory, since this is the keybinding I use to execute lisp code in the *scratch*
buffer in emacs.
autoload -Uz edit-command-line
zle -N edit-command-line
bindkey "^X^E" edit-command-line
Custom function to create a directory and cd into it immediately (found from here)
mkcd() { mkdir "$1"; cd "$1"; }
Function to integrate the zsh commands kill-whole-line
and yank
(bound by default to C-u
and C-y
respectively) with the system clipboard, using xsel (adapted from clipboard - zsh copy and paste like emacs - Unix & Linux Stack Exchange). This creates the widgets x-kill-whole-line
and x-yank
, that modify the kill-whole-line
and yank
commands by passing them through xsel, and then those widgets are activated and bound to C-u
and C-y
.
x-kill-whole-line () {
zle kill-whole-line
print -rn $CUTBUFFER | xsel -i -b
}
zle -N x-kill-whole-line
x-yank () {
CUTBUFFER=$(xsel -o -b </dev/null)
zle yank
}
zle -N x-yank
bindkey -e '^U' x-kill-whole-line
bindkey -e '^Y' x-yank
Function to get output from the last command to use for the current command. Found from here and bound to C-q
.
zmodload -i zsh/parameter
insert-last-command-output() {
LBUFFER+="$(eval $history[$((HISTCMD-1))])"
}
zle -N insert-last-command-output
bindkey "^Q" insert-last-command-output
Oh-my-zsh used to set the terminal title in such a manner as to show username@hostname:directory
on the titlebar of the terminal emulator, and just the directory in the tool bar of the OS, but without the framework only the title of the terminal emulator (e.g. Terminal
) is shown on both places - not very useful. I took a look at the code oh-my-zsh uses for this setting and found it too convoluted for me to use. Thankfully, a link on the top of the file pointed me to the right direction. Here I found a much simpler function (which however has the title be the same on both the terminal emulator title bar and the OS tool bar, but oh well…), and adapted it to my needs, removing the username and the hostname (as I’m the only user on my computer and I don’t connect to any remote machines). I also recall that oh-my-zsh also showed the name of the currently running command, when this was the case, and found here how to implement this natively. So currently when a command is running, the title of the terminal emulator and the OS toolbar show its name, otherwise they show the name of the current directory.
case $TERM in
xterm*)
precmd () {print -Pn "\e]0;%~\a"}
preexec () {print -Pn "\e]0;$1\a"}
;;
esac
Enable completion
autoload -Uz compinit
compinit
Setting for menu selection in completion
zstyle ':completion:*' menu select
List the completion matches in rows instead of columns.
setopt list_rows_first
Setting so that when using a glob (e.g. *) it will show a menu for completion instead of putting all the filenames that satisfy the conditions of the glob on the command. Useful if I want to act on a file but don’t exactly recall its name, and also if I wanted to act on all files of e.g. a specific filetype I wouldn’t use tab to complete.
setopt glob_complete
Enable LS_COLORS
for the completion of files and directories.
zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
See man zshoptions
.
Automatically cd when the target is a directory without having to precede the target with cd
.
setopt auto_cd
Append commands to the history file as soon as they are executed.
setopt inc_append_history
Don’t add a duplicate of the previous command into history.
setopt histignoredups
Ignore the end of file sequence (ctrl-d).
setopt ignoreeof
Prevent the forward history search shortcut (Ctrl-s) from being overtaken by the flow control (see here).
unsetopt flowcontrol
Autocorrect all wrong arguments.
setopt correct_all
Make the autocorrect prompt fancier, by coloring the wrong argument with bold red and the right with bold green and showing the full names of the available options, also colored, with Yes as bold green, No as bold yellow, Abort as bold red and Edit as bold blue (adapted from Refining Linux: ZSH Gem #4: Spell checking and auto correction, with slight help from here). Note that the look of this setting depends on the used terminal emulator colorscheme.
autoload -U colors && colors
export SPROMPT="Correct $fg_bold[red]%R$reset_color to $fg_bold[green]%r?$reset_color ($fg_bold[green]Yes$reset_color, $fg_bold[yellow]No$reset_color, $fg_bold[red]Abort$reset_color, $fg_bold[blue]Edit$reset_color) "
Here are aliases for variants of ls and grep, git status and zinit commands, but also one to have quick access to my own preferences on tty-clock.
alias ls='ls --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
alias ll='ls -lh'
alias la='ls -lah'
alias gs='git status'
alias zstatus='zinit zstatus'
alias termclock="tty-clock -b -c -C 6 -f \"%A %d/%m/%y\" -B -a 100000000 -d 0"
Also alias the dollar sign and zsh’s percent sign so that they are ignored if they are in the beginning of a command, something that would happen most likely by mistake e.g. copying a command from the internet (idea from here and here).
alias \$=''
alias \%=''
After I uninstalled oh-my-zsh, I found out that the screen wasn’t cleared anymore after quitting from the output of git-log, which was undesired, as I didn’t want the output of git-log to remain printed on my terminal. Turns out that this was also set by oh-my-zsh, and since I had come to expect this behavior, I set up here the less pager (used by git by default) with the settings that oh-my-zsh had.
export LESS=-R
export MICRO_TRUECOLOR=1