145 lines
No EOL
80 KiB
Markdown
145 lines
No EOL
80 KiB
Markdown
---
|
||
id: 191e19a9-a07e-479a-82bc-52f90227746c
|
||
title: |
|
||
Configuring Zsh Without Dependencies
|
||
status: ARCHIVED
|
||
tags:
|
||
- read-later
|
||
date_added: 2024-02-15 10:01:52
|
||
url_omnivore: |
|
||
https://omnivore.app/me/https-thevaluable-dev-zsh-install-configure-mouseless-18dacdbdb29
|
||
url_original: |
|
||
https://thevaluable.dev/zsh-install-configure-mouseless/
|
||
---
|
||
|
||
# Configuring Zsh Without Dependencies
|
||
|
||
## Highlights
|
||
|
||
Zsh read these files in the following order:
|
||
|
||
1. `.zshenv` \- Should only contain user’s environment variables.
|
||
2. `.zprofile` \- Can be used to execute commands just after logging in.
|
||
3. `.zshrc` \- Should be used for the shell configuration and for executing commands.
|
||
4. `.zlogin` \- Same purpose than `.zprofile`, but read just after `.zshrc`.
|
||
5. `.zlogout` \- Can be used to execute commands when a shell exit.
|
||
|
||
[source](https://omnivore.app/me/https-thevaluable-dev-zsh-install-configure-mouseless-18dacdbdb29#debc2c0b-4a8b-4073-8a0b-fbddbf99bdcb)
|
||
|
||
---
|
||
|
||
## Original
|
||
|
||
<DIV id="readability-content"><DIV data-omnivore-anchor-idx="1" class="page" id="readability-page-1"><div data-omnivore-anchor-idx="2"><main data-omnivore-anchor-idx="3" role="main"><article data-omnivore-anchor-idx="4"><header data-omnivore-anchor-idx="5"></header><section data-omnivore-anchor-idx="6"><picture data-omnivore-anchor-idx="7"><source data-omnivore-anchor-idx="8" srcset="https://proxy-prod.omnivore-image-cache.app/0x0,srshUvfS3G_ezR33RSwvxWA169d3kznE6QwTzqZ8P_6Y/https://thevaluable.dev/images/2020/zsh/zsh.webp," type="image/webp"><img data-omnivore-anchor-idx="9" data-omnivore-original-src="https://thevaluable.dev/images/2020/zsh/zsh.jpg" width="780" height="520" src="https://proxy-prod.omnivore-image-cache.app/780x520,sRAkOrK0pbDyfGGP8JZyhZzzaifzNMRVFQJs9ceY2rMQ/https://thevaluable.dev/images/2020/zsh/zsh.jpg" alt="Huey, Dewey, and Louie with a Z, S, and H t-shirt"></picture><p data-omnivore-anchor-idx="10">This article is part of a series about Zsh:</p><p data-omnivore-anchor-idx="11">There are many boring tasks we repeat day after day: creating, copying, moving or searching files, launching again and again the same tools, docker containers, and whatnot.</p><p data-omnivore-anchor-idx="12">For a developer, the shell is a precious asset which can increase your efficiency over time. It will bring powerful tools at your fingertips, and, more importantly, it will allow you to automate many parts of your workflow.</p><p data-omnivore-anchor-idx="13">To leverage these functionalities, you’ll need a powerful and flexible shell. Today, I would like to present your next best friend: the Z shell, or Zsh.</p><p data-omnivore-anchor-idx="14">If you look at the documentation (around 450 pages for the <a data-omnivore-anchor-idx="15" href="http://zsh.sourceforge.net/Doc/zsh_a4.pdf" target="_blank" rel="noopener">PDF version</a>), Zsh can feel daunting. There are so many options available, it can be difficult to come up with a basic configuration you can build upon.</p><p data-omnivore-anchor-idx="16">We’ll build, in this article, a basic Zsh config. I’ll explain the meaning of (almost) everything along the way, including:</p><ul data-omnivore-anchor-idx="17"><li data-omnivore-anchor-idx="18">What’s a Unix shell.</li><li data-omnivore-anchor-idx="19">Why Zsh is a good choice.</li><li data-omnivore-anchor-idx="20">How to install Zsh.</li><li data-omnivore-anchor-idx="21">A brief overview of:<ul data-omnivore-anchor-idx="22"><li data-omnivore-anchor-idx="23">Useful environment variables.</li><li data-omnivore-anchor-idx="24">Aliases.</li><li data-omnivore-anchor-idx="25">The Zsh options.</li><li data-omnivore-anchor-idx="26">The Zsh completion.</li><li data-omnivore-anchor-idx="27">The Zsh prompt.</li><li data-omnivore-anchor-idx="28">The Zsh directory stack.</li></ul></li><li data-omnivore-anchor-idx="29">How to configure Zsh to make it Vim-like.</li><li data-omnivore-anchor-idx="30">How to add external plugins to Zsh.</li><li data-omnivore-anchor-idx="31">External programs you can use to improve your Zsh experience.</li></ul><p data-omnivore-anchor-idx="32">Are your keyboard ready? Are you fingers warm? Did you stretch your arms? Let’s begin, then!</p><h2 data-omnivore-anchor-idx="33" id="brief-unix-shell-overview">Brief Unix Shell Overview</h2><p data-omnivore-anchor-idx="34">A shell <em data-omnivore-anchor-idx="35">interpret</em> command lines. You can type them using a prompt in an <em data-omnivore-anchor-idx="36">interactive shell</em>, or you can run shell scripts using a <em data-omnivore-anchor-idx="37">non-interactive shell</em>.</p><p data-omnivore-anchor-idx="38">The shell run just after you logged in with your user. You can imagine the shell as the layer directly above the kernel of Unix-based operating systems (including Linux). Here’s the charismatic <a data-omnivore-anchor-idx="39" href="https://youtu.be/tc4ROCJYbm0?t=248" target="_blank" rel="noopener">Brian Kernighan explaining it casually with his feet on a table</a>.</p><p data-omnivore-anchor-idx="40">When you use a graphical interface (or GUI), you click around with your mouse to perform tasks. When you use a shell, you use plain text instead.</p><p data-omnivore-anchor-idx="41">If you use a graphical interface (like a windows manager or a desktop environment), you’ll need a <em data-omnivore-anchor-idx="42">terminal emulator</em> to access the shell. In the old days, a <a data-omnivore-anchor-idx="43" href="https://en.wikipedia.org/wiki/Computer_terminal" target="_blank" rel="noopener">terminal was a real device</a>. Nowadays, it’s a program.</p><p data-omnivore-anchor-idx="44">The shell gives you access to many powerful programs. They are called CLIs, or Command Line Interfaces.</p><p data-omnivore-anchor-idx="45">At that point, you might wonder: why using a shell, instead of a graphical interface?</p><ul data-omnivore-anchor-idx="46"><li data-omnivore-anchor-idx="47">It’s difficult to get a graphical interface right, especially if your software has many functionalities. It can be simpler to build a CLI to avoid some complexity.</li><li data-omnivore-anchor-idx="48">CLIs are usually faster.</li><li data-omnivore-anchor-idx="49">A developer deals often with plain text. CLIs are great for that.</li><li data-omnivore-anchor-idx="50">Many shells, like Linux shells, allow you to pipe CLIs together in order to create a powerful transformation flow.</li><li data-omnivore-anchor-idx="51">It’s easier to automate textual commands rather than actions on a graphical interface.</li></ul><blockquote data-omnivore-anchor-idx="52"><p data-omnivore-anchor-idx="53">Play around with your command shell, and you’ll be surprised at how much more productive it makes you.</p></blockquote><p data-omnivore-anchor-idx="54">A shell is the keystone of a Mouseless Development Environment, and the most powerful tool you can use as a developer.</p><h2 data-omnivore-anchor-idx="55" id="bash-vs-zsh">Bash vs Zsh</h2><p data-omnivore-anchor-idx="56">There are other Linux shells available out there, including the famous <a data-omnivore-anchor-idx="57" href="https://en.wikipedia.org/wiki/Bash_%28Unix_shell%29" target="_blank" rel="noopener">Bash</a>. Why using Zsh?</p><ul data-omnivore-anchor-idx="58"><li data-omnivore-anchor-idx="59">The level of flexibility and customization of Zsh is crazy.</li><li data-omnivore-anchor-idx="60">You have access to a powerful completion for your favorite CLIs.</li><li data-omnivore-anchor-idx="61">The Vi mode is golden for every Vim lovers.</li><li data-omnivore-anchor-idx="62">There is an important and active community around Zsh.</li><li data-omnivore-anchor-idx="63">Bash scripts are (mostly) compatible with Zsh.</li></ul><p data-omnivore-anchor-idx="64">Bash is simpler than Zsh, but it has also less functionalities.</p><h2 data-omnivore-anchor-idx="65" id="zsh-without-oh-my-zsh">Zsh Without oh-my-zsh</h2><p data-omnivore-anchor-idx="66">You’ll see many advising you to install a Zsh framework with a crazy number of plugins, options, aliases, all already configured. The famous ones are <a data-omnivore-anchor-idx="67" href="https://ohmyz.sh/" target="_blank" rel="noopener">Oh My Zsh</a> and <a data-omnivore-anchor-idx="68" href="https://github.com/sorin-ionescu/prezto" target="_blank" rel="noopener">prezto</a>.</p><p data-omnivore-anchor-idx="69">I tried this approach for years and I think the drawbacks outweigh the benefits:</p><ul data-omnivore-anchor-idx="70"><li data-omnivore-anchor-idx="71">I have no clue what’s included in these frameworks. When I read their documentations, I can’t possibly remember everything it sets. Therefore, I barely use 10% of the functionalities.</li><li data-omnivore-anchor-idx="72">Zsh has already many functionalities and options, it’s even more daunting to have a framework on top.</li><li data-omnivore-anchor-idx="73">A framework is a big external dependency which brings more complexity. If there is a conflict with my own configuration or a bug, it can take a long time to figure out what’s happening.</li><li data-omnivore-anchor-idx="74">A framework impose rules and way of doing I don’t necessarily want, or need.</li></ul><p data-omnivore-anchor-idx="75">Don’t get me wrong: these frameworks are incredible feats. They can be useful to get some inspiration for your own configuration. But I wouldn’t use them directly.</p><h2 data-omnivore-anchor-idx="76" id="let-the-party-begin">Let The Party Begin</h2><p data-omnivore-anchor-idx="77">We’ll now configure Zsh. If the files or folders I’m speaking about don’t exist, you need to create them.</p><p data-omnivore-anchor-idx="78">This configuration was tested with a Linux based system. I have no idea about macOS, but it should work.</p><h3 data-omnivore-anchor-idx="79" id="installing-zsh">Installing Zsh</h3><p data-omnivore-anchor-idx="80">You can install Zsh like everything else:</p><ul data-omnivore-anchor-idx="81"><li data-omnivore-anchor-idx="82">Debian / Ubuntu: <code data-omnivore-anchor-idx="83" class="hljs language-cmake language-ebnf">sudo apt <span data-omnivore-anchor-idx="84" class="hljs-keyword">install</span> zsh</code></li><li data-omnivore-anchor-idx="85">Red Hat: <code data-omnivore-anchor-idx="86" class="hljs language-cmake language-ebnf">sudo yum <span data-omnivore-anchor-idx="87" class="hljs-keyword">install</span> zsh</code></li><li data-omnivore-anchor-idx="88">Arch Linux: <code data-omnivore-anchor-idx="89" class="hljs language-ebnf language-nginx"><span data-omnivore-anchor-idx="90" class="hljs-attribute">sudo pacman -S zsh</span></code></li><li data-omnivore-anchor-idx="91">macOS (with brew): <code data-omnivore-anchor-idx="92" class="hljs language-mipsasm language-armasm"><span data-omnivore-anchor-idx="93" class="hljs-keyword">brew </span><span data-omnivore-anchor-idx="94" class="hljs-keyword">install </span>zsh</code></li></ul><p data-omnivore-anchor-idx="95">Then, run it in a terminal by typing <code data-omnivore-anchor-idx="96" class="hljs language-ebnf"><span data-omnivore-anchor-idx="97" class="hljs-attribute">zsh</span></code>.</p><h3 data-omnivore-anchor-idx="98" id="zsh-config-files">Zsh Config Files</h3><p data-omnivore-anchor-idx="99">To configure Zsh for your user’s session, you can use the following files:</p><ul data-omnivore-anchor-idx="100"><li data-omnivore-anchor-idx="101"><code data-omnivore-anchor-idx="102" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="103" class="hljs-meta"><span data-omnivore-anchor-idx="104" class="hljs-meta-keyword">$ZDOTDIR</span>/.zshenv</span></code></li><li data-omnivore-anchor-idx="105"><code data-omnivore-anchor-idx="106" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="107" class="hljs-meta"><span data-omnivore-anchor-idx="108" class="hljs-meta-keyword">$ZDOTDIR</span>/.zprofile</span></code></li><li data-omnivore-anchor-idx="109"><code data-omnivore-anchor-idx="110" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="111" class="hljs-meta"><span data-omnivore-anchor-idx="112" class="hljs-meta-keyword">$ZDOTDIR</span>/.zshrc</span></code></li><li data-omnivore-anchor-idx="113"><code data-omnivore-anchor-idx="114" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="115" class="hljs-meta"><span data-omnivore-anchor-idx="116" class="hljs-meta-keyword">$ZDOTDIR</span>/.zlogin</span></code></li><li data-omnivore-anchor-idx="117"><code data-omnivore-anchor-idx="118" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="119" class="hljs-meta"><span data-omnivore-anchor-idx="120" class="hljs-meta-keyword">$ZDOTDIR</span>/.zlogout</span></code></li></ul><p data-omnivore-anchor-idx="121">In case you wonder what <code data-omnivore-anchor-idx="122" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="123" class="hljs-meta"><span data-omnivore-anchor-idx="124" class="hljs-meta-keyword">$ZDOTDIR</span></span></code> stands for, we’ll come back to it soon.</p><p data-omnivore-anchor-idx="125">Zsh read these files in the following order:</p><ol data-omnivore-anchor-idx="126"><li data-omnivore-anchor-idx="127"><code data-omnivore-anchor-idx="128" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="129" class="hljs-title">.zshenv</span></code> - Should only contain user’s environment variables.</li><li data-omnivore-anchor-idx="130"><code data-omnivore-anchor-idx="131" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="132" class="hljs-title">.zprofile</span></code> - Can be used to execute commands just after logging in.</li><li data-omnivore-anchor-idx="133"><code data-omnivore-anchor-idx="134" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="135" class="hljs-title">.zshrc</span></code> - Should be used for the shell configuration and for executing commands.</li><li data-omnivore-anchor-idx="136"><code data-omnivore-anchor-idx="137" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="138" class="hljs-title">.zlogin</span></code> - Same purpose than <code data-omnivore-anchor-idx="139" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="140" class="hljs-title">.zprofile</span></code>, but read just after <code data-omnivore-anchor-idx="141" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="142" class="hljs-title">.zshrc</span></code>.</li><li data-omnivore-anchor-idx="143"><code data-omnivore-anchor-idx="144" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="145" class="hljs-title">.zlogout</span></code> - Can be used to execute commands when a shell exit.</li></ol><p data-omnivore-anchor-idx="146">We’ll use only <code data-omnivore-anchor-idx="147" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="148" class="hljs-title">.zshenv</span></code> and <code data-omnivore-anchor-idx="149" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="150" class="hljs-title">.zshrc</span></code> in this article.</p><h3 data-omnivore-anchor-idx="151" id="zsh-config-path">Zsh Config Path</h3><p data-omnivore-anchor-idx="152">By default, Zsh will try to find the user’s configuration files in the <code data-omnivore-anchor-idx="153" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="154" class="hljs-meta"><span data-omnivore-anchor-idx="155" class="hljs-meta-keyword">$HOME</span></span></code> directory. You can change it by setting the environment variable <code data-omnivore-anchor-idx="156" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="157" class="hljs-meta"><span data-omnivore-anchor-idx="158" class="hljs-meta-keyword">$ZDOTDIR</span></span></code>.</p><p data-omnivore-anchor-idx="159">Personally, I like to have all my configuration files in <code data-omnivore-anchor-idx="160" class="hljs language-gams language-arduino"><span data-omnivore-anchor-idx="161" class="hljs-meta"><span data-omnivore-anchor-idx="162" class="hljs-meta-keyword">$HOME</span>/.config</span></code>. To do so:</p><ol data-omnivore-anchor-idx="163"><li data-omnivore-anchor-idx="164">I set the variable <code data-omnivore-anchor-idx="165" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="166" class="hljs-meta"><span data-omnivore-anchor-idx="167" class="hljs-meta-keyword">$XDG</span>_CONFIG_HOME</span></code> as following: <code data-omnivore-anchor-idx="168" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="169" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="170" class="hljs-attribute">XDG_CONFIG_HOME</span>=<span data-omnivore-anchor-idx="171" class="hljs-string">"<span data-omnivore-anchor-idx="172" class="hljs-variable">$HOME</span>/.config"</span></code>.</li><li data-omnivore-anchor-idx="173">I set the environment variable <code data-omnivore-anchor-idx="174" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="175" class="hljs-meta"><span data-omnivore-anchor-idx="176" class="hljs-meta-keyword">$ZDOTDIR</span></span></code>: <code data-omnivore-anchor-idx="177" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="178" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="179" class="hljs-attribute">ZDOTDIR</span>=<span data-omnivore-anchor-idx="180" class="hljs-string">"<span data-omnivore-anchor-idx="181" class="hljs-variable">$XDG_CONFIG_HOME</span>/zsh"</span></code>.</li><li data-omnivore-anchor-idx="182">I put the file <code data-omnivore-anchor-idx="183" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="184" class="hljs-title">.zshrc</span></code> in the <code data-omnivore-anchor-idx="185" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="186" class="hljs-meta"><span data-omnivore-anchor-idx="187" class="hljs-meta-keyword">$ZDOTDIR</span></span></code> directory.</li></ol><p data-omnivore-anchor-idx="188">Most software will use the path in <code data-omnivore-anchor-idx="189" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="190" class="hljs-meta"><span data-omnivore-anchor-idx="191" class="hljs-meta-keyword">$XDG</span>_CONFIG_HOME</span></code> to install their own config files. As a result, you’ll have a clean <code data-omnivore-anchor-idx="192" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="193" class="hljs-meta"><span data-omnivore-anchor-idx="194" class="hljs-meta-keyword">$HOME</span></span></code> directory.</p><p data-omnivore-anchor-idx="195">Unfortunately, the file <code data-omnivore-anchor-idx="196" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="197" class="hljs-title">.zshenv</span></code> <strong data-omnivore-anchor-idx="198">needs to be in your home directory</strong>. It’s where you’ll set <code data-omnivore-anchor-idx="199" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="200" class="hljs-meta"><span data-omnivore-anchor-idx="201" class="hljs-meta-keyword">$ZDOTDIR</span></span></code>. Then, every file read after <code data-omnivore-anchor-idx="202" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="203" class="hljs-title">.zshenv</span></code> can go into your <code data-omnivore-anchor-idx="204" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="205" class="hljs-meta"><span data-omnivore-anchor-idx="206" class="hljs-meta-keyword">$ZDOTDIR</span></span></code> directory.</p><h2 data-omnivore-anchor-idx="207" id="zsh-basic-config">Zsh Basic Config</h2><h3 data-omnivore-anchor-idx="208" id="environment-variables">Environment Variables</h3><p data-omnivore-anchor-idx="209">As we saw, you can set the environment variables you need for your user’s session in the file <code data-omnivore-anchor-idx="210" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="211" class="hljs-meta"><span data-omnivore-anchor-idx="212" class="hljs-meta-keyword">$HOME</span>/.zshenv</span></code>. This file should only define environment variables.</p><p data-omnivore-anchor-idx="213">For example, you can set up the <a data-omnivore-anchor-idx="214" href="https://wiki.archlinux.org/index.php/XDG_Base_Directory" target="_blank" rel="noopener">XDG Base directory</a> there, as seen above:</p><div data-omnivore-anchor-idx="215"><pre data-omnivore-anchor-idx="216" tabindex="0"><code data-omnivore-anchor-idx="217" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="218" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="219" class="hljs-attribute">XDG_CONFIG_HOME</span>=<span data-omnivore-anchor-idx="220" class="hljs-string">"<span data-omnivore-anchor-idx="221" class="hljs-variable">$HOME</span>/.config"</span>
|
||
<span data-omnivore-anchor-idx="222" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="223" class="hljs-attribute">XDG_DATA_HOME</span>=<span data-omnivore-anchor-idx="224" class="hljs-string">"<span data-omnivore-anchor-idx="225" class="hljs-variable">$XDG_CONFIG_HOME</span>/local/share"</span>
|
||
<span data-omnivore-anchor-idx="226" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="227" class="hljs-attribute">XDG_CACHE_HOME</span>=<span data-omnivore-anchor-idx="228" class="hljs-string">"<span data-omnivore-anchor-idx="229" class="hljs-variable">$XDG_CONFIG_HOME</span>/cache"</span>
|
||
</code></pre></div><p data-omnivore-anchor-idx="230">You can also make sure that any program requiring a text editor use your favorite one:</p><div data-omnivore-anchor-idx="231"><pre data-omnivore-anchor-idx="232" tabindex="0"><code data-omnivore-anchor-idx="233" class="hljs language-routeros language-cpp"><span data-omnivore-anchor-idx="234" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="235" class="hljs-attribute">EDITOR</span>=<span data-omnivore-anchor-idx="236" class="hljs-string">"nvim"</span>
|
||
<span data-omnivore-anchor-idx="237" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="238" class="hljs-attribute">VISUAL</span>=<span data-omnivore-anchor-idx="239" class="hljs-string">"nvim"</span>
|
||
</code></pre></div><p data-omnivore-anchor-idx="240">You can set some Zsh environment variables, too:</p><div data-omnivore-anchor-idx="241"><pre data-omnivore-anchor-idx="242" tabindex="0"><code data-omnivore-anchor-idx="243" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="244" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="245" class="hljs-attribute">ZDOTDIR</span>=<span data-omnivore-anchor-idx="246" class="hljs-string">"<span data-omnivore-anchor-idx="247" class="hljs-variable">$XDG_CONFIG_HOME</span>/zsh"</span>
|
||
|
||
<span data-omnivore-anchor-idx="248" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="249" class="hljs-attribute">HISTFILE</span>=<span data-omnivore-anchor-idx="250" class="hljs-string">"<span data-omnivore-anchor-idx="251" class="hljs-variable">$ZDOTDIR</span>/.zhistory"</span> # History filepath
|
||
<span data-omnivore-anchor-idx="252" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="253" class="hljs-attribute">HISTSIZE</span>=10000 # Maximum events <span data-omnivore-anchor-idx="254" class="hljs-keyword">for</span> internal history
|
||
<span data-omnivore-anchor-idx="255" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="256" class="hljs-attribute">SAVEHIST</span>=10000 # Maximum events <span data-omnivore-anchor-idx="257" class="hljs-keyword">in</span> history file
|
||
</code></pre></div><p data-omnivore-anchor-idx="258">I already explained the first line. For the other ones, they will:</p><ul data-omnivore-anchor-idx="259"><li data-omnivore-anchor-idx="260">Store your command line history in the file <code data-omnivore-anchor-idx="261" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="262" class="hljs-title">.zhistory</span></code>.</li><li data-omnivore-anchor-idx="263">Allows you to have a history of 10000 entries maximum.</li></ul><p data-omnivore-anchor-idx="264"><a data-omnivore-anchor-idx="265" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/zshenv" target="_blank" rel="noopener">Here’s my .zshenv file</a>, if you need some inspiration.</p><h3 data-omnivore-anchor-idx="266" id="aliases">Aliases</h3><p data-omnivore-anchor-idx="267">Aliases are crucial to improve your efficiency. For example, I have a bunch of aliases for git I use all the time. It’s always easier to type when it’s shorter:</p><div data-omnivore-anchor-idx="268"><pre data-omnivore-anchor-idx="269" tabindex="0"><code data-omnivore-anchor-idx="270" class="hljs language-vhdl language-monkey"><span data-omnivore-anchor-idx="271" class="hljs-keyword">alias</span> gs=<span data-omnivore-anchor-idx="272" class="hljs-symbol">'git</span> status'
|
||
<span data-omnivore-anchor-idx="273" class="hljs-keyword">alias</span> ga=<span data-omnivore-anchor-idx="274" class="hljs-symbol">'git</span> add'
|
||
<span data-omnivore-anchor-idx="275" class="hljs-keyword">alias</span> gp=<span data-omnivore-anchor-idx="276" class="hljs-symbol">'git</span> push'
|
||
<span data-omnivore-anchor-idx="277" class="hljs-keyword">alias</span> gpo=<span data-omnivore-anchor-idx="278" class="hljs-symbol">'git</span> push origin'
|
||
<span data-omnivore-anchor-idx="279" class="hljs-keyword">alias</span> gtd=<span data-omnivore-anchor-idx="280" class="hljs-symbol">'git</span> tag <span data-omnivore-anchor-idx="281" class="hljs-comment">--delete'</span>
|
||
<span data-omnivore-anchor-idx="282" class="hljs-keyword">alias</span> gtdr=<span data-omnivore-anchor-idx="283" class="hljs-symbol">'git</span> tag <span data-omnivore-anchor-idx="284" class="hljs-comment">--delete origin'</span>
|
||
<span data-omnivore-anchor-idx="285" class="hljs-keyword">alias</span> gr=<span data-omnivore-anchor-idx="286" class="hljs-symbol">'git</span> branch -r'
|
||
<span data-omnivore-anchor-idx="287" class="hljs-keyword">alias</span> gplo=<span data-omnivore-anchor-idx="288" class="hljs-symbol">'git</span> pull origin'
|
||
<span data-omnivore-anchor-idx="289" class="hljs-keyword">alias</span> gb=<span data-omnivore-anchor-idx="290" class="hljs-symbol">'git</span> branch '
|
||
<span data-omnivore-anchor-idx="291" class="hljs-keyword">alias</span> gc=<span data-omnivore-anchor-idx="292" class="hljs-symbol">'git</span> commit'
|
||
<span data-omnivore-anchor-idx="293" class="hljs-keyword">alias</span> gd=<span data-omnivore-anchor-idx="294" class="hljs-symbol">'git</span> diff'
|
||
<span data-omnivore-anchor-idx="295" class="hljs-keyword">alias</span> gco=<span data-omnivore-anchor-idx="296" class="hljs-symbol">'git</span> checkout '
|
||
<span data-omnivore-anchor-idx="297" class="hljs-keyword">alias</span> gl=<span data-omnivore-anchor-idx="298" class="hljs-symbol">'git</span> log'
|
||
<span data-omnivore-anchor-idx="299" class="hljs-keyword">alias</span> gr=<span data-omnivore-anchor-idx="300" class="hljs-symbol">'git</span> remote'
|
||
<span data-omnivore-anchor-idx="301" class="hljs-keyword">alias</span> grs=<span data-omnivore-anchor-idx="302" class="hljs-symbol">'git</span> remote show'
|
||
<span data-omnivore-anchor-idx="303" class="hljs-keyword">alias</span> glo=<span data-omnivore-anchor-idx="304" class="hljs-symbol">'git</span> log <span data-omnivore-anchor-idx="305" class="hljs-comment">--pretty="oneline"'</span>
|
||
<span data-omnivore-anchor-idx="306" class="hljs-keyword">alias</span> glol=<span data-omnivore-anchor-idx="307" class="hljs-symbol">'git</span> log <span data-omnivore-anchor-idx="308" class="hljs-comment">--graph --oneline --decorate'</span>
|
||
</code></pre></div><p data-omnivore-anchor-idx="309">I like to have my aliases in one separate file (called, surprisingly, <code data-omnivore-anchor-idx="310" class="hljs language-ebnf language-maxima"><span data-omnivore-anchor-idx="311" class="hljs-attribute">aliases</span></code>), and I source it in my <code data-omnivore-anchor-idx="312" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="313" class="hljs-title">.zshrc</span></code>:</p><div data-omnivore-anchor-idx="314"><pre data-omnivore-anchor-idx="315" tabindex="0"><code data-omnivore-anchor-idx="316" class="hljs language-gradle language-applescript"><span data-omnivore-anchor-idx="317" class="hljs-keyword">source</span> <span data-omnivore-anchor-idx="318" class="hljs-regexp">/path/</span>to<span data-omnivore-anchor-idx="319" class="hljs-regexp">/my/</span>aliases
|
||
</code></pre></div><p data-omnivore-anchor-idx="320">Here are <a data-omnivore-anchor-idx="321" href="https://github.com/Phantas0s/.dotfiles/blob/master/aliases/aliases" target="_blank" rel="noopener">all my aliases</a>.</p><h3 data-omnivore-anchor-idx="322" id="zsh-options">Zsh Options</h3><p data-omnivore-anchor-idx="323">You can set or unset many <a data-omnivore-anchor-idx="324" href="http://zsh.sourceforge.net/Doc/Release/Options.html" target="_blank" rel="noopener">Zsh options</a> using <code data-omnivore-anchor-idx="325" class="hljs language-bash language-ebnf"><span data-omnivore-anchor-idx="326" class="hljs-built_in">setopt</span></code> or <code data-omnivore-anchor-idx="327" class="hljs language-bash language-ebnf"><span data-omnivore-anchor-idx="328" class="hljs-built_in">unsetopt</span></code>. For example:</p><div data-omnivore-anchor-idx="329"><pre data-omnivore-anchor-idx="330" tabindex="0"><code data-omnivore-anchor-idx="331" class="hljs language-bash language-delphi"><span data-omnivore-anchor-idx="332" class="hljs-built_in">setopt</span> HIST_SAVE_NO_DUPS <span data-omnivore-anchor-idx="333" class="hljs-comment"># Do not write a duplicate event to the history file.</span>
|
||
<span data-omnivore-anchor-idx="334" class="hljs-built_in">unsetopt</span> HIST_SAVE_NO_DUPS <span data-omnivore-anchor-idx="335" class="hljs-comment"># Write a duplicate event to the history file</span>
|
||
</code></pre></div><p data-omnivore-anchor-idx="336">You can already do a lot of customization only using these options.</p><h3 data-omnivore-anchor-idx="337" id="zsh-completion-system">Zsh Completion System</h3><p data-omnivore-anchor-idx="338">The completion system of Zsh is one of its bigger strength, compared to other shells.</p><p data-omnivore-anchor-idx="339">To initialize the completion for the current Zsh session, you’ll need to call the function <code data-omnivore-anchor-idx="340" class="hljs language-ebnf"><span data-omnivore-anchor-idx="341" class="hljs-attribute">compinit</span></code>. More precisely, you’ll need to add this in your <code data-omnivore-anchor-idx="342" class="hljs language-ebnf"><span data-omnivore-anchor-idx="343" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="344"><pre data-omnivore-anchor-idx="345" tabindex="0"><code data-omnivore-anchor-idx="346" class="hljs language-nginx language-abnf"><span data-omnivore-anchor-idx="347" class="hljs-attribute">autoload</span> -U compinit; <span data-omnivore-anchor-idx="348" class="hljs-attribute">compinit</span>
|
||
</code></pre></div><p data-omnivore-anchor-idx="349">What does it mean?</p><p data-omnivore-anchor-idx="350">The <code data-omnivore-anchor-idx="351" class="hljs language-angelscript language-bash"><span data-omnivore-anchor-idx="352" class="hljs-built_in">auto</span>load</code> command load a file containing shell commands. To find this file, Zsh will look in the directories of the <em data-omnivore-anchor-idx="353">Zsh file search path</em>, defined in the variable <code data-omnivore-anchor-idx="354" class="hljs language-gams language-arcade"><span data-omnivore-anchor-idx="355" class="hljs-meta"><span data-omnivore-anchor-idx="356" class="hljs-meta-keyword">$fpath</span></span></code>, and search a file called <code data-omnivore-anchor-idx="357" class="hljs language-ebnf"><span data-omnivore-anchor-idx="358" class="hljs-attribute">compinit</span></code>.</p><p data-omnivore-anchor-idx="359">When <code data-omnivore-anchor-idx="360" class="hljs language-ebnf"><span data-omnivore-anchor-idx="361" class="hljs-attribute">compinit</span></code> is found, its content will be loaded as a <em data-omnivore-anchor-idx="362">function</em>. The function name will be the name of the file. You can then call this function like any other shell function.</p><div data-omnivore-anchor-idx="363"><p data-omnivore-anchor-idx="364">What about the semi-colon <code data-omnivore-anchor-idx="365" class="hljs language-abnf language-ini"><span data-omnivore-anchor-idx="366" class="hljs-comment">;</span></code>? It’s just a handy way to separate commands. It’s the same as calling <code data-omnivore-anchor-idx="367" class="hljs language-ebnf"><span data-omnivore-anchor-idx="368" class="hljs-attribute">compinit</span></code> on a new line.</p></div><p data-omnivore-anchor-idx="369">Why using autoload, and not sourcing the file by doing <code data-omnivore-anchor-idx="370" class="hljs language-gradle language-arcade"><span data-omnivore-anchor-idx="371" class="hljs-keyword">source</span> ~<span data-omnivore-anchor-idx="372" class="hljs-regexp">/path/</span>of<span data-omnivore-anchor-idx="373" class="hljs-regexp">/compinit</span></code>?</p><ul data-omnivore-anchor-idx="374"><li data-omnivore-anchor-idx="375">It avoids name conflicts if you have an executable with the same name.</li><li data-omnivore-anchor-idx="376">It doesn’t expand aliases thanks to the <code data-omnivore-anchor-idx="377" class="hljs language-diff language-haml"><span data-omnivore-anchor-idx="378" class="hljs-deletion">-U</span></code> option.</li><li data-omnivore-anchor-idx="379">It will load the function only when it’s needed (lazy-loading). It comes in handy to speed up Zsh startup.</li></ul><p data-omnivore-anchor-idx="380">Then, let’s add the following;</p><div data-omnivore-anchor-idx="381"><pre data-omnivore-anchor-idx="382" tabindex="0"><code data-omnivore-anchor-idx="383" class="hljs language-applescript language-dts">_comp_options+=(globdots) <span data-omnivore-anchor-idx="384" class="hljs-comment"># With hidden files</span>
|
||
source /<span data-omnivore-anchor-idx="385" class="hljs-keyword">my</span>/path/<span data-omnivore-anchor-idx="386" class="hljs-keyword">to</span>/zsh/completion.zsh
|
||
</code></pre></div><p data-omnivore-anchor-idx="387">The first line will complete <a data-omnivore-anchor-idx="388" href="https://wiki.archlinux.org/index.php/Dotfiles" target="_blank" rel="noopener">dotfiles</a>.</p><p data-omnivore-anchor-idx="389">The second line source <a data-omnivore-anchor-idx="390" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/completion.zsh" target="_blank" rel="noopener">this file</a>. It’s my personal config for the Zsh completion. I’ve written an <a data-omnivore-anchor-idx="391" href="https://thevaluable.dev/zsh-completion-guide-examples/">article about that</a> if you’re interested to dive more into the completion system.</p><p data-omnivore-anchor-idx="392">Now, the completion should work:</p><ul data-omnivore-anchor-idx="393"><li data-omnivore-anchor-idx="394">If you type <code data-omnivore-anchor-idx="395" class="hljs language-avrasm language-ebnf"><span data-omnivore-anchor-idx="396" class="hljs-keyword">cp</span></code> and hit the tab key, you’ll see that Zsh will complete the command.</li><li data-omnivore-anchor-idx="397">If you type <code data-omnivore-anchor-idx="398" class="hljs language-avrasm language-nginx"><span data-omnivore-anchor-idx="399" class="hljs-keyword">cp</span> -</code> and hit the tab key, Zsh will display the possible arguments for the command.</li></ul><picture data-omnivore-anchor-idx="400"><source data-omnivore-anchor-idx="401" srcset="https://proxy-prod.omnivore-image-cache.app/0x0,sfiBcmObf97NIeUibtBNabIdkPJ0sEqndbvVxv52zQmk/https://thevaluable.dev/images/2020/zsh/auto_complete.webp," type="image/webp"><img data-omnivore-anchor-idx="402" data-omnivore-original-src="https://thevaluable.dev/images/2020/zsh/auto_complete.png" width="780" height="520" src="https://proxy-prod.omnivore-image-cache.app/780x520,shXcZ-OuRAMljwivxvaLoPqZ9COFjw6mY9KEnyqQBCKQ/https://thevaluable.dev/images/2020/zsh/auto_complete.png" alt="Zsh completion in action"></picture><h3 data-omnivore-anchor-idx="403" id="pimp-my-zsh-prompt">Pimp My Zsh Prompt</h3><p data-omnivore-anchor-idx="404">What would be the shell experience without a nice prompt? Dull. Tasteless. Depressing.</p><p data-omnivore-anchor-idx="405">Let’s be honest here: Zsh default prompt is ugly. We need to change it, before our eyes start crying some blood. My needs are simple:</p><ul data-omnivore-anchor-idx="406"><li data-omnivore-anchor-idx="407">The prompt needs to be on one line. I had display problems with two lines.</li><li data-omnivore-anchor-idx="408">The prompt needs to display some git info when necessary.</li></ul><p data-omnivore-anchor-idx="409">From there, I created <a data-omnivore-anchor-idx="410" href="https://github.com/Phantas0s/purification/blob/master/prompt_purification_setup" target="_blank" rel="noopener">my own prompt</a> from <a data-omnivore-anchor-idx="411" href="https://github.com/therealklanni/purity" target="_blank" rel="noopener">another one</a>. It looks like that:</p><picture data-omnivore-anchor-idx="412"><source data-omnivore-anchor-idx="413" srcset="https://proxy-prod.omnivore-image-cache.app/0x0,sEbG09G1juFPo63j_VjykyvXcIJNnKy9th3zqNYTNLns/https://thevaluable.dev/images/2020/zsh/prompt.webp," type="image/webp"><img data-omnivore-anchor-idx="414" data-omnivore-original-src="https://thevaluable.dev/images/2020/zsh/prompt.png" width="780" height="520" src="https://proxy-prod.omnivore-image-cache.app/780x520,sYO_QmAuKGoSirbH4ZiWqkffidw64lNbq8Ixd6jKGOew/https://thevaluable.dev/images/2020/zsh/prompt.png" alt="Zsh prompt"></picture><p data-omnivore-anchor-idx="415">If you open the prompt script, you’ll see that it’s pretty simple:</p><ul data-omnivore-anchor-idx="416"><li data-omnivore-anchor-idx="417">I set two environment variables: <code data-omnivore-anchor-idx="418" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="419" class="hljs-meta"><span data-omnivore-anchor-idx="420" class="hljs-meta-keyword">$PROMPT</span></span></code> and <code data-omnivore-anchor-idx="421" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="422" class="hljs-meta"><span data-omnivore-anchor-idx="423" class="hljs-meta-keyword">$RPROMPT</span></span></code>. The first one format the left prompt, the second display git information on the far right.</li><li data-omnivore-anchor-idx="424">You can add some formatting styles using, for example, <code data-omnivore-anchor-idx="425" class="hljs language-haml language-cos"><span data-omnivore-anchor-idx="426" class="hljs-tag">%<span data-omnivore-anchor-idx="427" class="hljs-selector-tag">F</span>{blue}</span>%f</code> to change the color, or <code data-omnivore-anchor-idx="428" class="hljs language-cos language-gcode"><span data-omnivore-anchor-idx="429" class="hljs-built_in">%Bmy</span>-cool-prompt<span data-omnivore-anchor-idx="430" class="hljs-built_in">%b</span></code> to make everything bold.</li></ul><p data-omnivore-anchor-idx="431">This prompt doesn’t need any external <a data-omnivore-anchor-idx="432" href="https://thevaluable.dev/cohesion-coupling-guide-examples/">dependency</a>. You can copy it right away and modify it as much as you want.</p><p data-omnivore-anchor-idx="433"><a data-omnivore-anchor-idx="434" href="http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html" target="_blank" rel="noopener">Here’s everything you need, to create the prompt of your dream</a>.</p><p data-omnivore-anchor-idx="435">To load the prompt, you need to add something like that in your <code data-omnivore-anchor-idx="436" class="hljs language-ebnf"><span data-omnivore-anchor-idx="437" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="438"><pre data-omnivore-anchor-idx="439" tabindex="0"><code data-omnivore-anchor-idx="440" class="hljs language-applescript language-arcade">fpath=(/<span data-omnivore-anchor-idx="441" class="hljs-keyword">my</span>/path/<span data-omnivore-anchor-idx="442" class="hljs-keyword">to</span>/zsh/prompt $fpath)
|
||
autoload -Uz name_of_the_prompt_file; name_of_the_prompt_file
|
||
</code></pre></div><p data-omnivore-anchor-idx="443">The first line will add the folder containing the prompt to <code data-omnivore-anchor-idx="444" class="hljs language-gams language-arcade"><span data-omnivore-anchor-idx="445" class="hljs-meta"><span data-omnivore-anchor-idx="446" class="hljs-meta-keyword">$fpath</span></span></code>, as discussed above. It will also ensure that any function declared in the folder <code data-omnivore-anchor-idx="447" class="hljs language-applescript language-awk">/<span data-omnivore-anchor-idx="448" class="hljs-keyword">my</span>/path/<span data-omnivore-anchor-idx="449" class="hljs-keyword">to</span>/zsh/prompt</code> will overwrite every other ones with the same name, in other <code data-omnivore-anchor-idx="450" class="hljs language-ebnf"><span data-omnivore-anchor-idx="451" class="hljs-attribute">fpath</span></code> folders.</p><p data-omnivore-anchor-idx="452">The second line autoload the prompt itself.</p><p data-omnivore-anchor-idx="453">This prompt require <a data-omnivore-anchor-idx="454" href="https://fontawesome.com/v4.7.0/" target="_blank" rel="noopener">font awesome 4</a> for the git icons. You can download the font and install it, or you can change the icons.</p><h3 data-omnivore-anchor-idx="455" id="zsh-directory-stack">Zsh Directory Stack</h3><p data-omnivore-anchor-idx="456">Zsh has commands to <a data-omnivore-anchor-idx="457" href="http://zsh.sourceforge.net/Intro/intro_6.html" target="_blank" rel="noopener">push and pop directories on a directory stack</a>.</p><p data-omnivore-anchor-idx="458">By manipulating this stack, you can set up an history of directory visited, and be able to jump back to these directories.</p><p data-omnivore-anchor-idx="459">First, let’s set some options in your <code data-omnivore-anchor-idx="460" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="461" class="hljs-title">.zshrc</span></code>:</p><div data-omnivore-anchor-idx="462"><pre data-omnivore-anchor-idx="463" tabindex="0"><code data-omnivore-anchor-idx="464" class="hljs language-bash language-nginx"><span data-omnivore-anchor-idx="465" class="hljs-built_in">setopt</span> AUTO_PUSHD <span data-omnivore-anchor-idx="466" class="hljs-comment"># Push the current directory visited on the stack.</span>
|
||
<span data-omnivore-anchor-idx="467" class="hljs-built_in">setopt</span> PUSHD_IGNORE_DUPS <span data-omnivore-anchor-idx="468" class="hljs-comment"># Do not store duplicates in the stack.</span>
|
||
<span data-omnivore-anchor-idx="469" class="hljs-built_in">setopt</span> PUSHD_SILENT <span data-omnivore-anchor-idx="470" class="hljs-comment"># Do not print the directory stack after pushd or popd.</span>
|
||
</code></pre></div><p data-omnivore-anchor-idx="471">Then, you can create these aliases:</p><div data-omnivore-anchor-idx="472"><pre data-omnivore-anchor-idx="473" tabindex="0"><code data-omnivore-anchor-idx="474" class="hljs language-bash language-perl"><span data-omnivore-anchor-idx="475" class="hljs-built_in">alias</span> d=<span data-omnivore-anchor-idx="476" class="hljs-string">'dirs -v'</span>
|
||
<span data-omnivore-anchor-idx="477" class="hljs-keyword">for</span> index ({1..9}) <span data-omnivore-anchor-idx="478" class="hljs-built_in">alias</span> <span data-omnivore-anchor-idx="479" class="hljs-string">"<span data-omnivore-anchor-idx="480" class="hljs-variable">$index</span>"</span>=<span data-omnivore-anchor-idx="481" class="hljs-string">"cd +<span data-omnivore-anchor-idx="482" class="hljs-variable">${index}</span>"</span>; <span data-omnivore-anchor-idx="483" class="hljs-built_in">unset</span> index
|
||
</code></pre></div><p data-omnivore-anchor-idx="484">What does it do?</p><ul data-omnivore-anchor-idx="485"><li data-omnivore-anchor-idx="486">Every directory visited will populate the stack.</li><li data-omnivore-anchor-idx="487">When you use the alias <code data-omnivore-anchor-idx="488" class="hljs language-ebnf"><span data-omnivore-anchor-idx="489" class="hljs-attribute">d</span></code>, it will display the directories on the stack prefixed with a number.</li><li data-omnivore-anchor-idx="490">The line <code data-omnivore-anchor-idx="491" class="hljs language-perl language-bash"><span data-omnivore-anchor-idx="492" class="hljs-keyword">for</span> <span data-omnivore-anchor-idx="493" class="hljs-keyword">index</span> ({<span data-omnivore-anchor-idx="494" class="hljs-number">1</span>..<span data-omnivore-anchor-idx="495" class="hljs-number">9</span>}) alias <span data-omnivore-anchor-idx="496" class="hljs-string">"$index"</span>=<span data-omnivore-anchor-idx="497" class="hljs-string">"cd +<span data-omnivore-anchor-idx="498" class="hljs-subst">${<span data-omnivore-anchor-idx="499" class="hljs-keyword">index</span>}</span>"</span>; unset <span data-omnivore-anchor-idx="500" class="hljs-keyword">index</span></code> will create aliases from 1 to 9. They will allow you to jump directly in whatever directory on your stack.</li></ul><p data-omnivore-anchor-idx="501">For example, if you execute <code data-omnivore-anchor-idx="502" class="hljs language-angelscript language-lsl"><span data-omnivore-anchor-idx="503" class="hljs-number">1</span></code> in Zsh, you’ll jump to the directory prefixed with <code data-omnivore-anchor-idx="504" class="hljs language-angelscript language-lsl"><span data-omnivore-anchor-idx="505" class="hljs-number">1</span></code> in your stack list.</p><p data-omnivore-anchor-idx="506">You can also increase <code data-omnivore-anchor-idx="507" class="hljs language-angelscript language-lsl">index ({<span data-omnivore-anchor-idx="508" class="hljs-number">1.</span><span data-omnivore-anchor-idx="509" class="hljs-number">.9</span>})</code> to <code data-omnivore-anchor-idx="510" class="hljs language-angelscript language-lsl">index ({<span data-omnivore-anchor-idx="511" class="hljs-number">1.</span><span data-omnivore-anchor-idx="512" class="hljs-number">.100</span>})</code> for example, if you want to be able to jump back to 100 directories.</p><p data-omnivore-anchor-idx="513">For example, you can do that:</p><div data-omnivore-anchor-idx="514"><pre data-omnivore-anchor-idx="515" tabindex="0"><code data-omnivore-anchor-idx="516" class="hljs language-jboss-cli language-arduino">~ > <span data-omnivore-anchor-idx="517" class="hljs-keyword">cd</span> <span data-omnivore-anchor-idx="518" class="hljs-string">.config</span>
|
||
~<span data-omnivore-anchor-idx="519" class="hljs-string">/.config</span> > <span data-omnivore-anchor-idx="520" class="hljs-keyword">cd</span> devdash
|
||
~<span data-omnivore-anchor-idx="521" class="hljs-string">/.config/devdash</span> > <span data-omnivore-anchor-idx="522" class="hljs-keyword">cd</span> <span data-omnivore-anchor-idx="523" class="hljs-string">..</span>
|
||
~<span data-omnivore-anchor-idx="524" class="hljs-string">/.config</span> > <span data-omnivore-anchor-idx="525" class="hljs-keyword">cd</span> i3
|
||
~<span data-omnivore-anchor-idx="526" class="hljs-string">/.config/i3</span> > <span data-omnivore-anchor-idx="527" class="hljs-keyword">cd</span> <span data-omnivore-anchor-idx="528" class="hljs-string">..</span>
|
||
~<span data-omnivore-anchor-idx="529" class="hljs-string">/.config</span> > d
|
||
0 ~<span data-omnivore-anchor-idx="530" class="hljs-string">/.config</span>
|
||
1 ~<span data-omnivore-anchor-idx="531" class="hljs-string">/.config/i3</span>
|
||
2 ~<span data-omnivore-anchor-idx="532" class="hljs-string">/.config/devdash</span>
|
||
3 ~
|
||
~<span data-omnivore-anchor-idx="533" class="hljs-string">/.config</span> > 2
|
||
~<span data-omnivore-anchor-idx="534" class="hljs-string">/.config/devdash</span> >
|
||
</code></pre></div><h3 data-omnivore-anchor-idx="535" id="zsh-by-default">Zsh By Default</h3><p data-omnivore-anchor-idx="536">When you’re ready psychologically to set Zsh as your default shell, you can run these commands:</p><ul data-omnivore-anchor-idx="537"><li data-omnivore-anchor-idx="538">For Linux: <code data-omnivore-anchor-idx="539" class="hljs language-reasonml language-arcade">chsh -s <span data-omnivore-anchor-idx="540" class="hljs-constructor">$(<span data-omnivore-anchor-idx="541" class="hljs-params">which</span> <span data-omnivore-anchor-idx="542" class="hljs-params">zsh</span>)</span></code></li><li data-omnivore-anchor-idx="543">For macOS: <code data-omnivore-anchor-idx="544" class="hljs language-reasonml language-bash">sudo sh -c <span data-omnivore-anchor-idx="545" class="hljs-string">"echo $(which zsh) >> /etc/shells"</span><span data-omnivore-anchor-idx="546" class="hljs-operator"> && </span>chsh -s <span data-omnivore-anchor-idx="547" class="hljs-constructor">$(<span data-omnivore-anchor-idx="548" class="hljs-params">which</span> <span data-omnivore-anchor-idx="549" class="hljs-params">zsh</span>)</span></code></li></ul><p data-omnivore-anchor-idx="550">A good soul on Reddit whispered me that Zsh is now the default shell from macOS Catalina onwards, so you don’t necessarily need the above command.</p><p data-omnivore-anchor-idx="551">Zsh is now part of your life. Congratulation!</p><h2 data-omnivore-anchor-idx="552" id="zsh-with-vim-flavors">Zsh With Vim Flavors</h2><p data-omnivore-anchor-idx="553">For editing purposes, <a data-omnivore-anchor-idx="554" href="https://thevaluable.dev/phpstorm-vs-vim/">Vim is my best friend</a>. I love when CLIs use some Vim key binding, and Zsh gives you even more than that. If you’d like to learn Vim, this <a data-omnivore-anchor-idx="555" href="https://thevaluable.dev/vim-commands-beginner/" target="_blank" rel="noopener">series of articles</a> can help.</p><h3 data-omnivore-anchor-idx="556" id="activating-vi-mode">Activating Vi Mode</h3><p data-omnivore-anchor-idx="557">Zsh has a Vi mode you can enable by adding the following in your <code data-omnivore-anchor-idx="558" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="559" class="hljs-title">.zshrc</span></code>:</p><div data-omnivore-anchor-idx="560"><pre data-omnivore-anchor-idx="561" tabindex="0"><code data-omnivore-anchor-idx="562" class="hljs language-routeros language-bash">bindkey -v
|
||
<span data-omnivore-anchor-idx="563" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="564" class="hljs-attribute">KEYTIMEOUT</span>=1
|
||
</code></pre></div><p data-omnivore-anchor-idx="565">You can now switch between INSERT and NORMAL mode (called also COMMAND mode) with the <code data-omnivore-anchor-idx="566" class="hljs language-ebnf"><span data-omnivore-anchor-idx="567" class="hljs-attribute">ESC</span></code> key, and use the familiar Vim keystrokes to edit what you’re typing in your shell prompt. I write the different modes in uppercase here for clarity, but it doesn’t have to be.</p><p data-omnivore-anchor-idx="568">The second line <code data-omnivore-anchor-idx="569" class="hljs language-routeros language-angelscript"><span data-omnivore-anchor-idx="570" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="571" class="hljs-attribute">KEYTIMEOUT</span>=1</code> makes the switch between modes quicker.</p><h3 data-omnivore-anchor-idx="572" id="changing-cursor">Changing Cursor</h3><p data-omnivore-anchor-idx="573">A visual indicator to show the current mode (NORMAL or INSERT) could be nice. In Vim, my cursor is a beam <code data-omnivore-anchor-idx="574" class="hljs language-1c"><span data-omnivore-anchor-idx="575" class="hljs-string">|</span></code> when I’m in INSERT mode, and a block <code data-omnivore-anchor-idx="576" class="hljs language-undefined">█</code> when I’m in NORMAL mode. I wanted the same for Zsh.</p><p data-omnivore-anchor-idx="577">You can add the following in your <code data-omnivore-anchor-idx="578" class="hljs language-ebnf"><span data-omnivore-anchor-idx="579" class="hljs-attribute">zshrc</span></code>, or autoload it from a file, <a data-omnivore-anchor-idx="580" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/plugins/cursor_mode" target="_blank" rel="noopener">as I did</a>.</p><div data-omnivore-anchor-idx="581"><pre data-omnivore-anchor-idx="582" tabindex="0"><code data-omnivore-anchor-idx="583" class="hljs language-bash language-perl"><span data-omnivore-anchor-idx="584" class="hljs-function"><span data-omnivore-anchor-idx="585" class="hljs-title">cursor_mode</span></span>() {
|
||
<span data-omnivore-anchor-idx="586" class="hljs-comment"># See https://ttssh2.osdn.jp/manual/4/en/usage/tips/vim.html for cursor shapes</span>
|
||
cursor_block=<span data-omnivore-anchor-idx="587" class="hljs-string">'\e[2 q'</span>
|
||
cursor_beam=<span data-omnivore-anchor-idx="588" class="hljs-string">'\e[6 q'</span>
|
||
|
||
<span data-omnivore-anchor-idx="589" class="hljs-keyword">function</span> <span data-omnivore-anchor-idx="590" class="hljs-built_in">zle</span>-keymap-select {
|
||
<span data-omnivore-anchor-idx="591" class="hljs-keyword">if</span> [[ <span data-omnivore-anchor-idx="592" class="hljs-variable">${KEYMAP}</span> == vicmd ]] ||
|
||
[[ <span data-omnivore-anchor-idx="593" class="hljs-variable">$1</span> = <span data-omnivore-anchor-idx="594" class="hljs-string">'block'</span> ]]; <span data-omnivore-anchor-idx="595" class="hljs-keyword">then</span>
|
||
<span data-omnivore-anchor-idx="596" class="hljs-built_in">echo</span> -ne <span data-omnivore-anchor-idx="597" class="hljs-variable">$cursor_block</span>
|
||
<span data-omnivore-anchor-idx="598" class="hljs-keyword">elif</span> [[ <span data-omnivore-anchor-idx="599" class="hljs-variable">${KEYMAP}</span> == main ]] ||
|
||
[[ <span data-omnivore-anchor-idx="600" class="hljs-variable">${KEYMAP}</span> == viins ]] ||
|
||
[[ <span data-omnivore-anchor-idx="601" class="hljs-variable">${KEYMAP}</span> = <span data-omnivore-anchor-idx="602" class="hljs-string">''</span> ]] ||
|
||
[[ <span data-omnivore-anchor-idx="603" class="hljs-variable">$1</span> = <span data-omnivore-anchor-idx="604" class="hljs-string">'beam'</span> ]]; <span data-omnivore-anchor-idx="605" class="hljs-keyword">then</span>
|
||
<span data-omnivore-anchor-idx="606" class="hljs-built_in">echo</span> -ne <span data-omnivore-anchor-idx="607" class="hljs-variable">$cursor_beam</span>
|
||
<span data-omnivore-anchor-idx="608" class="hljs-keyword">fi</span>
|
||
}
|
||
|
||
<span data-omnivore-anchor-idx="609" class="hljs-built_in">zle</span>-line-<span data-omnivore-anchor-idx="610" class="hljs-function"><span data-omnivore-anchor-idx="611" class="hljs-title">init</span></span>() {
|
||
<span data-omnivore-anchor-idx="612" class="hljs-built_in">echo</span> -ne <span data-omnivore-anchor-idx="613" class="hljs-variable">$cursor_beam</span>
|
||
}
|
||
|
||
<span data-omnivore-anchor-idx="614" class="hljs-built_in">zle</span> -N <span data-omnivore-anchor-idx="615" class="hljs-built_in">zle</span>-keymap-select
|
||
<span data-omnivore-anchor-idx="616" class="hljs-built_in">zle</span> -N <span data-omnivore-anchor-idx="617" class="hljs-built_in">zle</span>-line-init
|
||
}
|
||
|
||
cursor_mode
|
||
</code></pre></div><p data-omnivore-anchor-idx="618">You can now speak about beams and blocks with passion and verve.</p><h3 data-omnivore-anchor-idx="619" id="vim-mapping-for-completion">Vim Mapping For Completion</h3><p data-omnivore-anchor-idx="620">To give Zsh more of a Vim taste, we can set up the keys <code data-omnivore-anchor-idx="621" class="hljs language-ebnf"><span data-omnivore-anchor-idx="622" class="hljs-attribute">hjkl</span></code> to navigate the completion menu.</p><p data-omnivore-anchor-idx="623">First, add the following to your <code data-omnivore-anchor-idx="624" class="hljs language-ebnf"><span data-omnivore-anchor-idx="625" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="626"><pre data-omnivore-anchor-idx="627" tabindex="0"><code data-omnivore-anchor-idx="628" class="hljs language-vim language-bash">zmodload zsh/complist
|
||
bindkey -M menuselect <span data-omnivore-anchor-idx="629" class="hljs-string">'h'</span> <span data-omnivore-anchor-idx="630" class="hljs-keyword">vi</span>-backward-char
|
||
bindkey -M menuselect <span data-omnivore-anchor-idx="631" class="hljs-string">'k'</span> <span data-omnivore-anchor-idx="632" class="hljs-keyword">vi</span>-<span data-omnivore-anchor-idx="633" class="hljs-keyword">up</span>-<span data-omnivore-anchor-idx="634" class="hljs-built_in">line</span>-<span data-omnivore-anchor-idx="635" class="hljs-built_in">or</span>-<span data-omnivore-anchor-idx="636" class="hljs-keyword">history</span>
|
||
bindkey -M menuselect <span data-omnivore-anchor-idx="637" class="hljs-string">'l'</span> <span data-omnivore-anchor-idx="638" class="hljs-keyword">vi</span>-forward-char
|
||
bindkey -M menuselect <span data-omnivore-anchor-idx="639" class="hljs-string">'j'</span> <span data-omnivore-anchor-idx="640" class="hljs-keyword">vi</span>-down-<span data-omnivore-anchor-idx="641" class="hljs-built_in">line</span>-<span data-omnivore-anchor-idx="642" class="hljs-built_in">or</span>-<span data-omnivore-anchor-idx="643" class="hljs-keyword">history</span>
|
||
</code></pre></div><p data-omnivore-anchor-idx="644">We load here the Zsh module <code data-omnivore-anchor-idx="645" class="hljs language-ebnf"><span data-omnivore-anchor-idx="646" class="hljs-attribute">complist</span></code>. Modules have functionalities which are not part of the Zsh’s core, but they can be loaded on demand. <a data-omnivore-anchor-idx="647" href="http://zsh.sourceforge.net/Doc/Release/Zsh-Modules.html" target="_blank" rel="noopener">Many different modules are available</a> for your needs.</p><p data-omnivore-anchor-idx="648">Here, the module <code data-omnivore-anchor-idx="649" class="hljs language-ebnf"><span data-omnivore-anchor-idx="650" class="hljs-attribute">complist</span></code> give you access to the keymap <code data-omnivore-anchor-idx="651" class="hljs language-ebnf"><span data-omnivore-anchor-idx="652" class="hljs-attribute">menuselect</span></code>, to customize the menu selection during completion, including how to select what you want.</p><p data-omnivore-anchor-idx="653">In general, the command <code data-omnivore-anchor-idx="654" class="hljs language-armasm language-bash"><span data-omnivore-anchor-idx="655" class="hljs-keyword">bindkey </span>-M</code> bind a key to a specific <em data-omnivore-anchor-idx="656">keymap</em>. A keymap is a set of keystrokes bind to specific Zsh functions. In this case, the keymap <code data-omnivore-anchor-idx="657" class="hljs language-ebnf"><span data-omnivore-anchor-idx="658" class="hljs-attribute">menuselect</span></code> bind keystrokes with selecting something in a list.</p><p data-omnivore-anchor-idx="659">To list all the keymaps available (depending on the modules you’ve loaded), you can run in your shell <code data-omnivore-anchor-idx="660" class="hljs language-armasm language-bash"><span data-omnivore-anchor-idx="661" class="hljs-keyword">bindkey </span>-l</code> (for <code data-omnivore-anchor-idx="662" class="hljs language-ebnf"><span data-omnivore-anchor-idx="663" class="hljs-attribute">l</span></code>ist). You can also <a data-omnivore-anchor-idx="664" href="http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Keymaps" target="_blank" rel="noopener">find the default ones here</a>.</p><p data-omnivore-anchor-idx="665">Last thing: you should always load the module <code data-omnivore-anchor-idx="666" class="hljs language-undefined">zsh/complist</code> <em data-omnivore-anchor-idx="667">before</em> autoloading <code data-omnivore-anchor-idx="668" class="hljs language-ebnf"><span data-omnivore-anchor-idx="669" class="hljs-attribute">compinit</span></code>.</p><h3 data-omnivore-anchor-idx="670" id="editing-command-lines-in-vim">Editing Command Lines In Vim</h3><p data-omnivore-anchor-idx="671">Good news: you can use your favorite editor to edit the commands you’re typing in your prompt! Let’s add these lines in your <code data-omnivore-anchor-idx="672" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="673" class="hljs-title">.zshrc</span></code> to do so:</p><div data-omnivore-anchor-idx="674"><pre data-omnivore-anchor-idx="675" tabindex="0"><code data-omnivore-anchor-idx="676" class="hljs language-vim language-gauss">autoload -Uz <span data-omnivore-anchor-idx="677" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="678" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="679" class="hljs-built_in">line</span>
|
||
zle -<span data-omnivore-anchor-idx="680" class="hljs-keyword">N</span> <span data-omnivore-anchor-idx="681" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="682" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="683" class="hljs-built_in">line</span>
|
||
bindkey -M vicmd v <span data-omnivore-anchor-idx="684" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="685" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="686" class="hljs-built_in">line</span>
|
||
</code></pre></div><p data-omnivore-anchor-idx="687">Here, we autoload <code data-omnivore-anchor-idx="688" class="hljs language-vim language-gauss"><span data-omnivore-anchor-idx="689" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="690" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="691" class="hljs-built_in">line</span></code>, a function from the module <a data-omnivore-anchor-idx="692" href="https://linux.die.net/man/1/zshcontrib" target="_blank" rel="noopener">zshcontrib</a>, which includes many contributions from Zsh users. This specific function let you edit a command line in your visual editor, defined by the environment variable <code data-omnivore-anchor-idx="693" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="694" class="hljs-meta"><span data-omnivore-anchor-idx="695" class="hljs-meta-keyword">$VISUAL</span></span></code> (or <code data-omnivore-anchor-idx="696" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="697" class="hljs-meta"><span data-omnivore-anchor-idx="698" class="hljs-meta-keyword">$EDITOR</span></span></code>). Great! That’s what we wanted.</p><p data-omnivore-anchor-idx="699">We already saw <code data-omnivore-anchor-idx="700" class="hljs language-armasm language-bash"><span data-omnivore-anchor-idx="701" class="hljs-keyword">bindkey </span>-M</code>. Using the keymap <code data-omnivore-anchor-idx="702" class="hljs language-ebnf"><span data-omnivore-anchor-idx="703" class="hljs-attribute">vicmd</span></code>, we can bind commands to some NORMAL mode keystrokes. It means that, when you’re in NORMAL mode, you can hit <code data-omnivore-anchor-idx="704" class="hljs language-ebnf"><span data-omnivore-anchor-idx="705" class="hljs-attribute">v</span></code> to directly edit your command in your editor.</p><h3 data-omnivore-anchor-idx="706" id="adding-text-objects">Adding Text Objects</h3><p data-omnivore-anchor-idx="707">If you use the Vi-mode of Zsh for a while, you’ll notice that there are no text objects for quotes or brackets: impossible to do something like <code data-omnivore-anchor-idx="708" class="hljs language-1c language-vim">da<span data-omnivore-anchor-idx="709" class="hljs-string">"</span></code> (to delete a quoted substring) or <code data-omnivore-anchor-idx="710" class="hljs language-isbl language-stata"><span data-omnivore-anchor-idx="711" class="hljs-function"><span data-omnivore-anchor-idx="712" class="hljs-title">ci</span>(</span></code> (to change inside parenthesis). Zsh supports these, you just need to generate and bind them to specific Zsh widgets:</p><div data-omnivore-anchor-idx="713"><pre data-omnivore-anchor-idx="714" tabindex="0"><code data-omnivore-anchor-idx="715" class="hljs language-bash language-properties"><span data-omnivore-anchor-idx="716" class="hljs-built_in">autoload</span> -Uz select-bracketed select-quoted
|
||
<span data-omnivore-anchor-idx="717" class="hljs-built_in">zle</span> -N select-quoted
|
||
<span data-omnivore-anchor-idx="718" class="hljs-built_in">zle</span> -N select-bracketed
|
||
<span data-omnivore-anchor-idx="719" class="hljs-keyword">for</span> km <span data-omnivore-anchor-idx="720" class="hljs-keyword">in</span> viopp visual; <span data-omnivore-anchor-idx="721" class="hljs-keyword">do</span>
|
||
<span data-omnivore-anchor-idx="722" class="hljs-built_in">bindkey</span> -M <span data-omnivore-anchor-idx="723" class="hljs-variable">$km</span> -- <span data-omnivore-anchor-idx="724" class="hljs-string">'-'</span> vi-up-line-or-history
|
||
<span data-omnivore-anchor-idx="725" class="hljs-keyword">for</span> c <span data-omnivore-anchor-idx="726" class="hljs-keyword">in</span> {a,i}<span data-omnivore-anchor-idx="727" class="hljs-variable">${(s..)^:-\'\"\`\|,./:;=+@}</span>; <span data-omnivore-anchor-idx="728" class="hljs-keyword">do</span>
|
||
<span data-omnivore-anchor-idx="729" class="hljs-built_in">bindkey</span> -M <span data-omnivore-anchor-idx="730" class="hljs-variable">$km</span> <span data-omnivore-anchor-idx="731" class="hljs-variable">$c</span> select-quoted
|
||
<span data-omnivore-anchor-idx="732" class="hljs-keyword">done</span>
|
||
<span data-omnivore-anchor-idx="733" class="hljs-keyword">for</span> c <span data-omnivore-anchor-idx="734" class="hljs-keyword">in</span> {a,i}<span data-omnivore-anchor-idx="735" class="hljs-variable">${(s..)^:-'()[]{}</span><>bB<span data-omnivore-anchor-idx="736" class="hljs-string">'}; do
|
||
bindkey -M $km $c select-bracketed
|
||
done
|
||
done
|
||
</span></code></pre></div><p data-omnivore-anchor-idx="737">If you want to know more about Zsh widgets, I’ve <a data-omnivore-anchor-idx="738" href="https://thevaluable.dev/zsh-line-editor-configuration-mouseless/">written another article about that</a>, where I also explain the code above.</p><h3 data-omnivore-anchor-idx="739" id="surrounding">Surrounding</h3><p data-omnivore-anchor-idx="740">Zsh also allows us to mimic the famous <a data-omnivore-anchor-idx="741" href="https://github.com/tpope/vim-surround" target="_blank" rel="noopener">Tim Pope’s surround plugin</a>. Just add the following to your <code data-omnivore-anchor-idx="742" class="hljs language-ebnf"><span data-omnivore-anchor-idx="743" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="744"><pre data-omnivore-anchor-idx="745" tabindex="0"><code data-omnivore-anchor-idx="746" class="hljs language-smali language-dsconfig">autoload -Uz surround
|
||
zle -N delete-surround surround
|
||
zle -N<span data-omnivore-anchor-idx="747" class="hljs-built_in"> add-surround </span>surround
|
||
zle -N change-surround surround
|
||
bindkey -M vicmd cs change-surround
|
||
bindkey -M vicmd ds delete-surround
|
||
bindkey -M vicmd ys<span data-omnivore-anchor-idx="748" class="hljs-built_in"> add-surround
|
||
</span>bindkey -M visual S<span data-omnivore-anchor-idx="749" class="hljs-built_in"> add-surround
|
||
</span></code></pre></div><p data-omnivore-anchor-idx="750">You can then use <code data-omnivore-anchor-idx="751" class="hljs language-ebnf language-stata"><span data-omnivore-anchor-idx="752" class="hljs-attribute">cs</span></code> (change surrounding), <code data-omnivore-anchor-idx="753" class="hljs language-ebnf language-stata"><span data-omnivore-anchor-idx="754" class="hljs-attribute">ds</span></code> (delete surrounding), <code data-omnivore-anchor-idx="755" class="hljs language-ebnf"><span data-omnivore-anchor-idx="756" class="hljs-attribute">ys</span></code> (add surrounding) in Zsh’s NORMAL mode.</p><h2 data-omnivore-anchor-idx="757" id="zsh-plugins">Zsh Plugins</h2><p data-omnivore-anchor-idx="758">The term “plugin”, as I use it, has nothing official. People often speak about Zsh plugins as external pieces of configuration you can add to your own.</p><p data-omnivore-anchor-idx="759">There are many of these plugins available for Zsh. Many of them are part of Zsh frameworks.</p><h3 data-omnivore-anchor-idx="760" id="zsh-completions">Zsh Completions</h3><p data-omnivore-anchor-idx="761">By default, Zsh can complete already many popular CLIs like <code data-omnivore-anchor-idx="762" class="hljs language-bash language-dos"><span data-omnivore-anchor-idx="763" class="hljs-built_in">cd</span></code>, <code data-omnivore-anchor-idx="764" class="hljs language-avrasm language-ebnf"><span data-omnivore-anchor-idx="765" class="hljs-keyword">cp</span></code>, <code data-omnivore-anchor-idx="766" class="hljs language-ebnf"><span data-omnivore-anchor-idx="767" class="hljs-attribute">git</span></code>, and so on.</p><p data-omnivore-anchor-idx="768">The plugin <a data-omnivore-anchor-idx="769" href="https://github.com/zsh-users/zsh-completions" target="_blank" rel="noopener">zsh-completions</a> add even more completions. The <a data-omnivore-anchor-idx="770" href="https://github.com/zsh-users/zsh-completions/tree/master/src" target="_blank" rel="noopener">list of the newly supported CLIs is here</a></p><p data-omnivore-anchor-idx="771">If you don’t use any of the program listed, you don’t need this plugin.</p><p data-omnivore-anchor-idx="772">I added <code data-omnivore-anchor-idx="773" class="hljs language-ebnf"><span data-omnivore-anchor-idx="774" class="hljs-attribute">zsh-completion</span></code> as a <a data-omnivore-anchor-idx="775" href="https://github.com/Phantas0s/.dotfiles/blob/master/.gitmodules" target="_blank" rel="noopener">git submodule in my dotfiles</a>. Then, you can automatically add every completion to your <code data-omnivore-anchor-idx="776" class="hljs language-ebnf"><span data-omnivore-anchor-idx="777" class="hljs-attribute">fpath</span></code>, in your zshrc:</p><div data-omnivore-anchor-idx="778"><pre data-omnivore-anchor-idx="779" tabindex="0"><code data-omnivore-anchor-idx="780" class="hljs language-elixir language-ruby">fpath=(<span data-omnivore-anchor-idx="781" class="hljs-regexp">/path/to</span><span data-omnivore-anchor-idx="782" class="hljs-regexp">/my/zsh</span><span data-omnivore-anchor-idx="783" class="hljs-regexp">/plugins/zsh</span>-completions/src <span data-omnivore-anchor-idx="784" class="hljs-variable">$fpath</span>)
|
||
</code></pre></div><p data-omnivore-anchor-idx="785">You don’t need to load every completion file, one by one. If you look at the beginning of one of these files, you’ll see <code data-omnivore-anchor-idx="786" class="hljs language-ebnf"><span data-omnivore-anchor-idx="787" class="hljs-attribute">compdef</span></code>. It’s a function from Zsh which load automagically the completion when it’s needed. The completion file itself only needs to be included in your <code data-omnivore-anchor-idx="788" class="hljs language-ebnf"><span data-omnivore-anchor-idx="789" class="hljs-attribute">fpath</span></code>.</p><p data-omnivore-anchor-idx="790">You can also cherry-pick the specific completions you want.</p><h3 data-omnivore-anchor-idx="791" id="zsh-syntax-highlighting">Zsh Syntax Highlighting</h3><p data-omnivore-anchor-idx="792">What about syntax highlighting in Zsh? That’s what <a data-omnivore-anchor-idx="793" href="https://github.com/zsh-users/zsh-syntax-highlighting" target="_blank" rel="noopener">zsh-syntax-highlighting</a> is about.</p><p data-omnivore-anchor-idx="794">You can source it directly:</p><div data-omnivore-anchor-idx="795"><pre data-omnivore-anchor-idx="796" tabindex="0"><code data-omnivore-anchor-idx="797" class="hljs language-vim language-dts"><span data-omnivore-anchor-idx="798" class="hljs-keyword">source</span> /path/<span data-omnivore-anchor-idx="799" class="hljs-keyword">to</span>/my/zsh/plugins/zsh-<span data-omnivore-anchor-idx="800" class="hljs-keyword">syntax</span>-highlighting/zsh-<span data-omnivore-anchor-idx="801" class="hljs-keyword">syntax</span>-highlighting.zsh
|
||
</code></pre></div><p data-omnivore-anchor-idx="802">There is one minor downside however: it seems to be currently incompatible with the surround widget we’ve seen above. If you want to use both, you need to use the branch <a data-omnivore-anchor-idx="803" href="https://github.com/zsh-users/zsh-syntax-highlighting/tree/feature/redrawhook" target="_blank" rel="noopener">feature/redrawhook</a>.</p><p data-omnivore-anchor-idx="804">You should source this plugin at the bottom of your <code data-omnivore-anchor-idx="805" class="hljs language-ebnf"><span data-omnivore-anchor-idx="806" class="hljs-attribute">zshrc</span></code>. Everything loaded before will then be able to use syntax highlighting if needed.</p><h3 data-omnivore-anchor-idx="807" id="jumping-to-a-parent-directory-easily">Jumping To A Parent Directory Easily</h3><p data-omnivore-anchor-idx="808">Do you like to type <code data-omnivore-anchor-idx="809" class="hljs language-routeros language-gams">cd <span data-omnivore-anchor-idx="810" class="hljs-built_in">..</span>/<span data-omnivore-anchor-idx="811" class="hljs-built_in">..</span>/<span data-omnivore-anchor-idx="812" class="hljs-built_in">..</span></code> to come back to the great-grand-parent of the current folder?</p><p data-omnivore-anchor-idx="813">Me neither.</p><p data-omnivore-anchor-idx="814">It’s where <a data-omnivore-anchor-idx="815" href="https://github.com/Tarrasch/zsh-bd" target="_blank" rel="noopener">bd</a> can help you. Imagine that you’re in the folder <code data-omnivore-anchor-idx="816" class="hljs language-awk language-crystal">~<span data-omnivore-anchor-idx="817" class="hljs-regexp">/a/</span>b<span data-omnivore-anchor-idx="818" class="hljs-regexp">/c/</span>d</code>. You can jump directly to <code data-omnivore-anchor-idx="819" class="hljs language-ebnf language-livecodeserver"><span data-omnivore-anchor-idx="820" class="hljs-attribute">a</span></code> with the command <code data-omnivore-anchor-idx="821" class="hljs language-armasm language-ebnf"><span data-omnivore-anchor-idx="822" class="hljs-keyword">bd </span>a</code>.</p><p data-omnivore-anchor-idx="823">The Zsh completion is even included. Awesomeness!</p><p data-omnivore-anchor-idx="824">To use it, you need to source the file <a data-omnivore-anchor-idx="825" href="https://github.com/Tarrasch/zsh-bd/blob/master/bd.zsh" target="_blank" rel="noopener">bd.zsh</a>.</p><h2 data-omnivore-anchor-idx="826" id="custom-scripts">Custom Scripts</h2><p data-omnivore-anchor-idx="827">Using a shell allows you to automate many parts of your workflow with shell scripts. That’s a huge benefit you should take advantage of.</p><p data-omnivore-anchor-idx="828">I keep most of <a data-omnivore-anchor-idx="829" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/scripts.zsh" target="_blank" rel="noopener">my scripts in one file</a> and I <a data-omnivore-anchor-idx="830" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/README.md" target="_blank" rel="noopener">document them</a> (roughly) for me to remember what’s in there, and for others to get inspired.</p><p data-omnivore-anchor-idx="831">I source the functions in my <code data-omnivore-anchor-idx="832" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="833" class="hljs-title">.zshrc</span></code>, but you could autoload them too.</p><p data-omnivore-anchor-idx="834">While working, ask yourself what tasks you do again and again, to automate them as much as you can. This is the real power of the shell, and it will make your whole workflow more fun.</p><h2 data-omnivore-anchor-idx="835" id="external-programs">External Programs</h2><p data-omnivore-anchor-idx="836">A shell without CLIs would be useless. Here are my personal favorites to expand Zsh functionalities.</p><h3 data-omnivore-anchor-idx="837" id="multiplex-your-zsh-with-tmux">Multiplex Your Zsh With tmux</h3><p data-omnivore-anchor-idx="838">I’ve already <a data-omnivore-anchor-idx="839" href="https://thevaluable.dev/tmux-boost-productivity-terminal/">written about tmux here</a>. It’s a terminal multiplexer with a tonne of functionalities: you can split your terminal in many windows or panes, synchronize them, and keep your sessions alive even without terminal. You can even extend it with plugins helping you automating your whole shell workflow.</p><h3 data-omnivore-anchor-idx="840" id="fuzzy-search-with-fzf">Fuzzy Search With fzf</h3><p data-omnivore-anchor-idx="841">The fuzzy finder <code data-omnivore-anchor-idx="842" class="hljs language-ebnf"><span data-omnivore-anchor-idx="843" class="hljs-attribute">fzf</span></code> is a fast and powerful tool. You can use it to search anything you want, like a file, an entry in your command line history, or a specific git commit message.</p><p data-omnivore-anchor-idx="844">I wrote (or copied and pasted) a bunch of <a data-omnivore-anchor-idx="845" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/scripts_fzf.zsh" target="_blank" rel="noopener">scripts using zsh</a> too, to search through git logs or <code data-omnivore-anchor-idx="846" class="hljs language-ebnf"><span data-omnivore-anchor-idx="847" class="hljs-attribute">tmuxp</span></code> projects.</p><p data-omnivore-anchor-idx="848">There are different ways to install <code data-omnivore-anchor-idx="849" class="hljs language-ebnf"><span data-omnivore-anchor-idx="850" class="hljs-attribute">fzf</span></code>. You’ll need first the executable. Then, I would recommend sourcing the files:</p><ul data-omnivore-anchor-idx="851"><li data-omnivore-anchor-idx="852"><code data-omnivore-anchor-idx="853" class="hljs language-css language-gauss"><span data-omnivore-anchor-idx="854" class="hljs-selector-tag">key-bindings</span><span data-omnivore-anchor-idx="855" class="hljs-selector-class">.zsh</span></code>, which will include some practical keystrokes like <code data-omnivore-anchor-idx="856" class="hljs language-ebnf"><span data-omnivore-anchor-idx="857" class="hljs-attribute">Ctrl-h</span></code> or <code data-omnivore-anchor-idx="858" class="hljs language-ebnf language-excel"><span data-omnivore-anchor-idx="859" class="hljs-attribute">Ctrl-t</span></code></li><li data-omnivore-anchor-idx="860"><code data-omnivore-anchor-idx="861" class="hljs language-css"><span data-omnivore-anchor-idx="862" class="hljs-selector-tag">completion</span><span data-omnivore-anchor-idx="863" class="hljs-selector-class">.zsh</span></code>, for <code data-omnivore-anchor-idx="864" class="hljs language-ebnf"><span data-omnivore-anchor-idx="865" class="hljs-attribute">fzf</span></code> completion.</li></ul><p data-omnivore-anchor-idx="866">If you use Arch Linux, you’ll need to install the package <code data-omnivore-anchor-idx="867" class="hljs language-ebnf"><span data-omnivore-anchor-idx="868" class="hljs-attribute">fzf</span></code> and simply source these two files in your <code data-omnivore-anchor-idx="869" class="hljs language-ebnf"><span data-omnivore-anchor-idx="870" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="871"><pre data-omnivore-anchor-idx="872" tabindex="0"><code data-omnivore-anchor-idx="873" class="hljs language-gradle language-awk"><span data-omnivore-anchor-idx="874" class="hljs-keyword">source</span> <span data-omnivore-anchor-idx="875" class="hljs-regexp">/usr/</span>share<span data-omnivore-anchor-idx="876" class="hljs-regexp">/fzf/</span>completion.zsh
|
||
<span data-omnivore-anchor-idx="877" class="hljs-keyword">source</span> <span data-omnivore-anchor-idx="878" class="hljs-regexp">/usr/</span>share<span data-omnivore-anchor-idx="879" class="hljs-regexp">/fzf/</span>key-bindings.zsh
|
||
</code></pre></div><p data-omnivore-anchor-idx="880">Otherwise, you’ll need to follow the installation process from fzf’s README file.</p><h2 data-omnivore-anchor-idx="881" id="the-z-shell-is-now-yours">The Z-Shell Is Now Yours</h2><p data-omnivore-anchor-idx="882">You should now have a clean and lean Zsh configuration, and you should understand enough of it to customize it.</p><p data-omnivore-anchor-idx="883">What did we learn with this article?</p><ul data-omnivore-anchor-idx="884"><li data-omnivore-anchor-idx="885">Zsh reads its configuration files in a precise order.</li><li data-omnivore-anchor-idx="886">You can set (or unset) many Zsh options depending on your needs.</li><li data-omnivore-anchor-idx="887">The completion system of Zsh is one of its best feature.</li><li data-omnivore-anchor-idx="888">Zsh directory stack allow you to jump easily in directories you’ve already visited.</li><li data-omnivore-anchor-idx="889">If you like Vim, Zsh allows you to use keystrokes from the Vim world. You can even edit your commands directly in Vim.</li><li data-omnivore-anchor-idx="890">External plugins can be found on The Internet, to improve even further the Zsh experience.</li><li data-omnivore-anchor-idx="891">You should go crazy on shell scripting, to automate your workflow as much as you can.</li><li data-omnivore-anchor-idx="892">External programs can enhance your experience with the shell, like <code data-omnivore-anchor-idx="893" class="hljs language-ebnf"><span data-omnivore-anchor-idx="894" class="hljs-attribute">tmux</span></code> or <code data-omnivore-anchor-idx="895" class="hljs language-ebnf"><span data-omnivore-anchor-idx="896" class="hljs-attribute">fzf</span></code>.</li></ul><p data-omnivore-anchor-idx="897">All your colleagues will be jealous. Guaranteed.</p></section></article></main></div></DIV></DIV> |