From Scratch to Emacs, The Adventurous Configurator’s Handbook

Emacs, the world-class, extensible, customisable, free/libre text editor, has stood the test of time and continues to be a powerful tool for both developers and writers alike. Its reputation for complexity belies its potential: it is an undiscovered country, a treasure chest filled with gold, jewels, and priceless artifacts. Embarking on a journey with Emacs is much like setting sail on the high seas of productivity, and in this guide, we aim to help you navigate these waters with ease.
This guide is a treasure map, of sorts, to help you chart your course in the vast ocean of Emacs functionalities. We will help you equip your Emacs ship with an arsenal of powerful tools and configurations to bolster your productivity, simplify your workflow, and make your voyage smoother. From the helm to the cannon, from the crow’s nest to the rigging, you’ll find that your Emacs ship will be ready for whatever storm the high seas might throw at you.
Whether you’re a seasoned Emacs sailor or a landlubber who’s only just beginning to learn the ropes, this guide is intended to help you tailor your Emacs experience to your needs and preferences. By the end of our journey together, you’ll have transformed your Emacs from a humble sailboat to a powerful, fully-equipped galleon, ready to tackle the toughest coding or writing challenges you might face.
So, hoist the Jolly Roger, set your course, and let’s set sail on the open sea of Emacs optimisation!
Chapter 1: Sailing the Seas
Packages: The Mariner’s Charts and Tools
Our maiden voyage begins by setting our charts and readying our tools. Just as a ship needs maps to navigate the high seas, so do we need the MELPA1 https://melpa.org/ repository, our trove of packages to steer Emacs.
Add the following markings to your captain’s log (init.el), to invite
MELPA onboard and ensure our tools are always shiny and ready. For every
new dawn we sail, our package list shall be refreshed.
(require 'package)
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("org" . "http://orgmode.org/elpa/")
("gnu" . "http://elpa.gnu.org/packages/")))
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))

Evil Mode: Hoisting the Jolly Roger of Editing
Our ship now sails under the black flag of “Evil mode”2 https://github.com/emacs-evil/evil, an homage to
the legendary seafarers of Vim, allowing us to navigate our text with
precision and ease. Heave the following ropes into your init.el
logbook to hoist the black flag.
(require 'package)
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("org" . "http://orgmode.org/elpa/")
("gnu" . "http://elpa.gnu.org/packages/")))
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
(unless (package-installed-p 'evil)
(package-install 'evil))
(require 'evil)
(evil-mode 1)

Window Size: Calibrating the Spyglass
Our ship’s porthole is our window into the vast sea of code. Currently, it’s too small, like a miser’s eyepiece. We can widen our view with the following enchantment:
(require 'package)
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("org" . "http://orgmode.org/elpa/")
("gnu" . "http://elpa.gnu.org/packages/")))
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
(unless (package-installed-p 'evil)
(package-install 'evil))
(require 'evil)
(evil-mode 1)
(add-to-list 'default-frame-alist '(height . 40))
(add-to-list 'default-frame-alist '(width . 90))
For the sake of our journey, we won’t veer too far off course, but remember, the horizon is yours to command.

Fonts: Inking the Captain’s Quill
Our final touch for this chapter of the voyage, we shall imbue the ship with a touch of elegance, by changing the ship’s lettering, the font of all frames. Here, we choose the scribe’s favorite, “Fira Code”.
(require 'package)
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("org" . "http://orgmode.org/elpa/")
("gnu" . "http://elpa.gnu.org/packages/")))
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
(unless (
package-installed-p 'evil)
(package-install 'evil))
(require 'evil)
(evil-mode 1)
(add-to-list 'default-frame-alist '(height . 40))
(add-to-list 'default-frame-alist '(width . 90))
(set-frame-font "Fira Code-12" nil t)

Tool bar and menu bar: The Ship’s Helm and Mainsail
Keep your helm clean by removing unnecessary clutter such as the default tool bar and menu bar. This can be achieved with a couple of simple commands.
(require 'package)
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("org" . "http://orgmode.org/elpa/")
("gnu" . "http://elpa.gnu.org/packages/")))
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
(unless (package-installed-p 'evil)
(package-install 'evil))
(require 'evil)
(evil-mode 1)
(add-to-list 'default-frame-alist '(height . 40))
(add-to-list 'default-frame-alist '(width . 90))
(set-frame-font "Fira Code-12" nil t)
;; Remove the menu bar and tool bar
(tool-bar-mode -1)
(menu-bar-mode -1)

Projectile: The Cannon of Project Navigation
Elevate your project management skills by utilising Projectile3 https://github.com/bbatsov/projectile. This tool not only helps in navigating projects but can also be customised to use dired4 https://www.gnu.org/software/emacs/manual/html_node/emacs/Dired.html for opening files in a project. By default, Projectile uses whatever method it thinks is most appropriate for opening files in a project, but we want to use dired. With this configuration, whenever you switch to a project, Projectile will open the project’s root in dired.
;; ... same as before
;; Projectile
(unless (package-installed-p 'projectile)
(package-install 'projectile))
(require 'projectile)
(projectile-mode +1)
(setq projectile-switch-project-action 'projectile-dired)

Mini-frame: The Crow’s Nest of Display
Observe your Emacs environment from a higher perspective with the mini-frame. Install it and place it at the top for an optimal viewing experience.
;; Mini-frame
(unless (package-installed-p 'mini-frame)
(package-install 'mini-frame))
(require 'mini-frame)
(mini-frame-mode 1)
(setq mini-frame-show-parameters
'((top . 0)
(width . 0.7)
(left . 0.5)))

Ivy: The Rigging of Completion
Ivy5 https://github.com/abo-abo/swiper is your personal assistant for completion in Emacs. Enhance its
functionalities by integrating ivy-posframe6 https://github.com/tumashu/ivy-posframe which displays Ivy in
a child frame. Here we’ll install both packages and configure them.
;; Ivy support
(unless (package-installed-p 'ivy)
(package-install 'ivy))
(unless (package-installed-p 'ivy-posframe)
(package-install 'ivy-posframe))
(require 'ivy)
(ivy-mode 1)
(require 'ivy-posframe)
(setq ivy-display-function #'ivy-posframe-display-at-frame-center)
(setq ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-frame-center)))
(ivy-posframe-mode 1)

Doom modeline: The Jolly Roger of Status Lines
Now we install an improved modeline, which is the Doom modeline7 https://github.com/seagle0128/doom-modeline.
;; Doom modeline
(unless (package-installed-p 'doom-modeline)
(package-install 'doom-modeline))
(require 'doom-modeline)
(doom-modeline-mode 1)

Startup projects: The Voyage’s Beginnings
Always keep your projects within reach by displaying a list of recent
projects every time Emacs starts. To do this, we tell it to remember
your recent projects. The alien indexing method will cause Projectile to
use external utilities like git to provide a speed boost. The ivy
completion system will use Ivy for project selection. A function creates
a new buffer called *Projectile Projects*, erases any existing content
in it, inserts a list of the projects known to Projectile, and displays
the buffer.
The new “Projectile” section will look like this:
;; Projectile
(unless (package-installed-p 'projectile)
(package-install 'projectile))
(require 'projectile)
;;; Enable Projectile and configure it to remember recent projects
(projectile-mode +1)
(setq projectile-switch-project-action 'projectile-dired)
(setq projectile-indexing-method 'alien)
(setq projectile-completion-system 'ivy)
;;; Create a function to display recent projects on startup
(defun display-projectile-list-on-startup ()
(let ((buf (get-buffer-create "*Projectile Projects*")))
(with-current-buffer buf
(erase-buffer)
(dolist (project (projectile-relevant-known-projects))
(insert project "\n")))
(switch-to-buffer buf)))
The page is still quite bare-bones, but we’ll add more features as we go along.

Markdown: The Sea Chart of Formatting
Since we’re writing this page in Markdown, let’s go ahead and install a
Markdown mode8 https://github.com/jrblevin/markdown-mode. We’ll install a Markdown major mode, a TOC generation
package, and a formatting package. We also need to tell Emacs to use the
Markdown mode for files with the .md extension.
;; Markdown
(dolist (package '(markdown-mode markdown-toc markdownfmt))
(unless (package-installed-p package)
(package-install package)))
(require 'markdown-mode)
(add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))

But … wait. The code blocks are ugly and do not have syntax
highlighting. To fix this, we first enable global font lock mode, which
will enable syntax highlighting for all major modes. Then we tell Emacs
to use the markdown-fontify-code-blocks-natively function to highlight
code blocks. markdown-mode should automatically use syntax
highlighting for code blocks, but it depends on the major mode
associated with the language used in the code block. We also have to
make sure the major mode for that language is installed. For example, if
you have a Python code block, you would need python-mode
(actually, this also comes with Emacs).
;;; Enable font-lock globally (syntax highlighting for code blocks)
(global-font-lock-mode 1)
;; ...
(setq markdown-fontify-code-blocks-natively t)
Looks better now!

Scrolling conservatively: The Steady Course of Viewing
By default, Emacs scrolls by nearly “full screens” (the exact behavior is influenced by the scroll-step and scroll-conservatively variables).
To make Emacs scroll line by line, you can set the scroll-conservatively
variable to a high value. You can add the following line to your
init.el file:
(setq scroll-conservatively 101)
The number 101 is somewhat arbitrary, it just needs to be greater than
100. When scroll-conservatively is set to a value greater than 100,
Emacs will always keep the cursor at least that many lines from the top
and bottom of the window, effectively causing it to scroll one line at a
time.
Speedbar: The Swift Current of Shortcuts
One of my favourite features in Doom Emacs is the speedbar. Doom Emacs
uses a package named which-key to provide this and it is a package
that displays available keybindings in a popup.
This package is very handy to discover available keybindings and to
learn new ones. which-key-mode makes it so that whenever you start a
key sequence, and then wait for a short period of time, which-key will
pop up a buffer with all possible completions of the key sequence. This
sequence (sometimes called the “leader key”) will be set in the config
as M-SPC.
We will also use general.el, a package that provides a more convenient
way to define key bindings. The first keybindings we will define is the
p for a “projects” section and f to find all files in a project.
;; Speed bar
(dolist (package '(which-key general))
(unless (package-installed-p package)
(package-install package)))
(require 'which-key)
(which-key-mode)
(require 'general)
(general-create-definer my-leader-def
:states '(normal visual insert emacs)
:prefix "M-SPC"
:non-normal-prefix "M-SPC")
(my-leader-def
"p" '(:ignore t :which-key "projects")
"pf" 'projectile-find-file)
(setq which-key-idle-delay 0.5) ;; default 0.5

A Few Cosmetic Changes: Polishing the Captain’s Quarters
We will now do a few cosmetic changes. The theme we will use is
modus-operandi9 https://protesilaos.com/emacs/modus-themes. This theme is bundled with Emacs 27, so we don’t
need to install it. We will also change the font in the Markdown code
blocks to the same as the main font. Additionally, we will add syntax
highlighting in Markdown mode to wikilinks.
;; Font name
(defvar my-font-name "Fira Code")
(set-frame-font (concat my-font-name "-12") nil t)
(set-face-attribute 'fixed-pitch nil :family my-font-name :height 120)
;;; ...
(setq markdown-enable-wiki-links t)
;; Match Markdown font main one
(set-face-attribute 'markdown-code-face nil :family my-font-name)
(set-face-attribute 'markdown-pre-face nil :family my-font-name)
;;; ...
;; Modus operandi
(load-theme 'modus-operandi t)

Buffer splitting: Dividing the Ship’s Log
Something I use quite offen is splitting the window vertically or
horizontally. In addition, if I’m inside a project, I want the new
window to be in the same project and show me a list of files so that I
can easily open one of them. To do this, we define two functions, one
for vertical splitting and one for horizontal splitting and assign to
keys for the speedbar. The keys will be M-SPC b s r (Buffers -> split
-> right) and M-SPC b s t (Buffers -> split -> top) for vertical
and horizontal splitting, respectively.
;; Buffer splitting
(defun split-right-and-list-project-files ()
"Split the current window into two, left and right. The left one contains the current contents and the right one contains a projectile-list of all the files in the project."
(interactive)
(split-window-right)
(other-window 1)
(projectile-find-file))
(defun split-below-and-list-project-files ()
"Split the current window into two, top and bottom. The top one contains the current contents and the bottom one contains a projectile-list of all the files in the project."
(interactive)
(split-window-below)
(other-window 1)
(projectile-find-file))
(my-leader-def
"b" '(:ignore t :which-key "Buffers")
"bs" '(:ignore t :which-key "Split")
"bsr" '(split-right-and-list-project-files :which-key "Right split")
"bst" '(split-below-and-list-project-files :which-key "Top split"))

Chapter 2: The Archipelago of Languages
Rust: The Ironclad Island
To add support for Rust we use the rustic10 https://github.com/brotzeit/rustic package.
rustic provides a more comprehensive environment for Rust development
essentially combinating several packages (rust-mode, cargo,
flycheck, lsp-mode, etc.) providing syntax highlighting,
formatting, linting, running Cargo commands and so on.
We also will use rustic-lsp-server with Rust Analyzer11 https://rust-analyzer.github.io/ as the
language server (altough RLS is also supported).
company will be added for autocompletion and rustic-lsp-server’s
inlay type hints will be enabled.
rustic will have auto-formatting on save enabled.
;; Rust
(unless (package-installed-p 'rustic)
(package-install 'rustic))
(require 'rustic)
;; use rust-analyzer as the LSP server
(setq rustic-lsp-server 'rust-analyzer)
;;; Company
(unless (package-installed-p 'company)
(package-install 'company))
(require 'company)
;; Enable company mode in all buffers
(add-hook 'after-init-hook 'global-company-mode)
;; Set company backends for lsp-mode
(push 'company-capf company-backends)
;;; lsp-mode
(unless (package-installed-p 'lsp-mode)
(package-install 'lsp-mode))
(require 'lsp-mode)
;; Enable lsp-mode in rustic-mode
(add-hook 'rustic-mode-hook #'lsp)
;; Use company-capf as the completion provider
(add-hook 'lsp-mode-hook (lambda () (set (make-local-variable 'company-backends) '(company-capf))))
;; Enable rustfmt on save
(setq rustic-format-on-save t)
;; Enable inlay hints
(setq lsp-rust-analyzer-display-inlay-hints t)
(setq lsp-inlay-hints-mode t)
We will also add
keybindings for common Rust/Cargo commands, such a build, run, test,
format, check and clippy.
;;; Speedbar for Rust
(my-leader-def
:states '(normal visual emacs)
:keymaps 'override
"l" '(:ignore t :which-key "Language")
"r" '(:ignore t :which-key "Rust"))
(my-leader-def
:states '(normal visual emacs)
:keymaps 'override
"l r b" '(rustic-cargo-build :which-key "Build")
"l r r" '(rustic-cargo-run :which-key "Run")
"l r t" '(rustic-cargo-test :which-key "Test")
"l r f" '(rustic-cargo-fmt :which-key "Format")
"l r c" '(rustic-cargo-check :which-key "Check")
"l r p" '(rustic-cargo-clippy :which-key "Clippy"))

Python: The Serpent’s Atoll
In this section, we make Python programming in Emacs a breeze by integrating the Elpy12 https://github.com/jorgenschaefer/elpy package. Elpy adds a multitude of enhancements to the Python editing experience, including code navigation, automatic formatting, refactoring helpers, and more.
First, we check if Elpy is installed and install it if necessary:
(unless (package-installed-p 'elpy)
(package-install 'elpy))
(require 'elpy)
Next, we enable Elpy for all Python buffers and configure IPython to
be the default Python interpreter:
(elpy-enable)
(setq python-shell-interpreter "ipython"
python-shell-interpreter-args "-i --simple-prompt")
We also specify the Python 3 interpreter for Elpy’s RPC and enable the Python Language Server Protocol (LSP) for Python mode:
(setq elpy-rpc-python-command "python3")
(add-hook 'python-mode-hook #'lsp-deferred)
(setq lsp-python-ms-python-executable-cmd "python3")
Flycheck13 https://www.flycheck.org/en/latest/, a modern syntax checking package, is then configured to replace Elpy’s default Flymake:
(when (require 'flycheck nil t)
(setq elpy-modules (delq 'elpy-module-flymake elpy-modules))
(add-hook 'elpy-mode-hook 'flycheck-mode))
Our scripting journey gets even more exciting as we set sail to the land
of Virtual Environments. We define a function set-python-virtualenv
that automatically sets the Python virtual environment based on the
Projectile project root:
(defun set-python-virtualenv ()
"Set `python-shell-virtualenv-root' based on projectile's project root."
(interactive)
... )
(add-hook 'python-mode-hook 'set-python-virtualenv)
Lastly, we bind the treasure map (which-key) with commonly used Python commands, helping us navigate the Python seas smoothly:
(my-leader-def
:states '(normal visual emacs)
:keymaps 'override
"lp" '(:ignore t :which-key "Python")
"lpv" '(set-python-virtualenv :which-key "Set Virtualenv")
"lpr" '(run-python :which-key "Run Python REPL")
"lpf" '(python-format-buffer :which-key "Format buffer")
"lpd" '(elpy-goto-definition :which-key "Go to definition"))
This marks the end of our Python setup chapter. We have set up a powerful Python development environment in Emacs, complete with syntax checking, virtual environment support, and interactive Python commands, all just a few key presses away. With these tools at hand, your Python adventure in Emacs will be a sailing pleasure!

Go: The Swift Current Isle
As our adventure in this programming environment continues, we delve into the rich ecosystem of Go, bringing out its full potential in Emacs.
The story begins with the installation of the go-mode14 https://github.com/dominikh/go-mode.el package. If
the package isn’t already part of the inventory (installed), our script
takes the initiative to acquire it:
(unless (package-installed-p 'go-mode)
(package-install 'go-mode))
Following this, we require or load the go-mode package to prepare
Emacs for Go development:
(require 'go-mode)
The support crew, company mode, is called upon to enhance the user
experience by providing autocompletion features globally:
(add-hook 'after-init-hook 'global-company-mode)
Next, Language Server Protocol (LSP) support is hooked into go-mode to
offer a plethora of language features like linting, code navigation, and
much more:
(add-hook 'go-mode-hook #'lsp-deferred)
(setq lsp-gopls-server-path (expand-file-name "~/go/bin/gopls"))
(setq lsp-enable-snippet nil) ;; disable snippet support
(setq lsp-gopls-staticcheck t) ;; enable gopls' staticcheck linter
(setq lsp-eldoc-render-all t) ;; display documentation in minibuffer while typing
To keep our code neat and imports organised, gofmt (set to
goimports) is triggered before every save. Additional save hooks are
also set up to automatically format the buffer and organise imports on
saving a Go file:
(setq gofmt-command "goimports")
(add-hook 'before-save-hook 'gofmt-before-save)
(defun lsp-go-install-save-hooks ()
(add-hook 'before-save-hook #'lsp-format-buffer t t)
(add-hook 'before-save-hook #'lsp-organize-imports t t))
(add-hook 'go-mode-hook #'lsp-go-install-save-hooks)
The adventure wouldn’t be complete without a handy map and compass. So,
we set up key bindings (using which-key) under M-SPC l g for Go:
(my-leader-def
:states '(normal visual emacs)
:keymaps 'override
"lg" '(:ignore t :which-key "Go")
"lgf" '(gofmt :which-key "Format code")
"lgt" '(compile :which-key "Run test")
"lgi" '(lsp-goto-implementation :which-key "Go to implementation")
"lgd" '(lsp-find-definition :which-key "Go to definition")
"lgr" '(lsp-find-references :which-key "Find references")
"lgh" '(lsp-describe-thing-at-point :which-key "Describe thing at point"))
This setup offers a more navigable and interactive environment, enabling you to journey through your Go code like an expert adventurer, efficiently uncovering the treasures of your project.

YAML: Charting the Indentation Ocean
;; First, we ensure that the yaml-mode package is installed. If not, Emacs is instructed to download and install it.
(unless (package-installed-p 'yaml-mode)
(package-refresh-contents)
(package-install 'yaml-mode))
With the indispensable yaml-mode installed and readied, our vessel for
navigating the YAML seas, we now link it with every YAML file we
encounter during our journey.
(require 'yaml-mode)
(add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-mode))
One of the quirks of our YAML sea is the importance of proper
indentation. But worry not! We’ve equipped our ship with an
auto-indenting feature. Whenever you press the enter key (C-m), a new
line is created, and the cursor is automatically positioned with the
correct indentation.
(add-hook 'yaml-mode-hook
(lambda ()
(define-key yaml-mode-map "\C-m" 'newline-and-indent)))
Our journey in the YAML sea often involves vast and complex structures. To make our journey manageable, we employ code folding, a valuable map that allows us to hide and reveal portions of our code as needed.
(add-hook 'yaml-mode-hook
(lambda ()
(hs-minor-mode 1)))
As we chart our course, we also enable company-mode to provide us with
suggestions, turning our journey into a collective effort with a
supportive crew.
(add-hook 'yaml-mode-hook 'company-mode)
We’re not alone on these seas. To keep us from straying off course, we
enlist the help of a yamllint lookout in our crow’s nest, alerting us
whenever we deviate from the ideal YAML structure. Before we embark,
ensure you’ve enlisted yamllint by installing it with
pip install yamllint.
;; Add YAML lint. Must have `pip install yamllint` installed.
(add-hook 'yaml-mode-hook 'flycheck-mode)
Lastly, to aid our navigation through the layers of indentation, we make
use of indent-guide. Like a dotted line in the ocean, it illuminates
our path, making the structure of our YAML document clear and visible.
(unless (package-installed-p 'indent-guide)
(package-install 'indent-guide))
(require '
indent-guide)
(add-hook 'yaml-mode-hook 'indent-guide-mode)
(set-face-foreground 'indent-guide-face "lightgray")
(set-face-background 'indent-guide-face nil)
(setq indent-guide-char ":")
With these tools at our disposal, our journey through the YAML seas will be a breeze, and we’ll be able to navigate the waves of indentation with ease and confidence.

Chapter 3: In the Hold: The Craftsman’s Kit
Navigating the Git Galleon: Version Control on the High Seas
Once you have set sail on the vast seas of code, it is crucial to wield the power of version control to navigate your way through the ever-changing tides of development. Git, the legendary version control system, will become your trusted companion on this adventurous journey. In this chapter, we will equip ourselves with the tools and knowledge to master Git and unlock its hidden treasures.
To harness the power of Git within Emacs, we need to gather the
necessary tools. Let’s start by installing the evil-collection
package, which provides Evil keybindings for various Emacs modes:
(unless (package-installed-p 'evil-collection)
(package-install 'evil-collection))
(require 'evil-collection)
Evil Collection enhances your Emacs experience by bringing Evil keybindings to Magit, among other modes.
The heart of our Git journey lies within Magit15 https://magit.vc/, the legendary Git
interface for Emacs. We must install the magit package to summon its
power:
(unless (package-installed-p 'magit)
(package-install 'magit))
Magit will serve as our compass, guiding us through the treacherous waters of version control.
To ensure that our Git knowledge permeates various programming realms,
we will bind the magit-refresh-status command to our modes of choice.
Let’s create a list of modes and add the hook to each one:
(setq my-modes '(rust-mode-hook
python-mode-hook
go-mode-hook
markdown-mode-hook
yaml-mode-hook))
(mapc (lambda (mode)
(add-hook mode (lambda () (add-hook 'after-save-hook 'magit-refresh-status))))
my-modes)
By doing so, we guarantee that whenever we save a file in one of these modes, Magit will refresh its status, keeping us informed about the state of our repositories.
Our quest wouldn’t be complete without discovering the hidden
repositories lurking in the shadows. With the power of Projectile, we
can uncover their secrets. Let’s map over the known projects, filter out
those without a .git directory, and prepare our repository list:
(setq magit-repo-dirs
(mapcar
(lambda (dir)
(substring dir 0 -1))
(cl-remove-if-not
(lambda (project)
(unless (file-remote-p project)
(file-directory-p (concat project "/.git/"))))
(projectile-relevant-known-projects))))
Now, we possess a list of repositories, each ready to be explored and conquered.
With our arsenal fully assembled, it’s time to unleash the power of Git
with our custom keybindings. Using the my-leader-def macro, we can
assign keybindings that will guide us through the Git seas:
(my-leader-def
:states '(normal visual emacs)
:keymaps 'override
"g" '(:ignore t :which-key "Git")
"gs" '(magit-status :which-key "Show status")
"gb" '(magit-branch :which-key "Show all branches")
"gco" '(magit-checkout :which-key "Checkout branch")
"gr" '(magit-rebase :which-key "Rebase on a branch")
"gsq" '(magit-rebase-squash :which
-key "Squash commits")
"gc" '(magit-commit :which-key "Commit")
"gp" '(magit-push-popup :which-key "Push"))
These keybindings will be your guiding stars as you navigate through Git’s vast expanse, allowing you to perform actions like displaying the repository status, managing branches, checking out branches, rebasing, squashing commits, committing changes, and pushing to remote repositories.
Sometimes, even in the face of adversity, we encounter unexpected
challenges. In this case, the unbound suffix ':' error arises when
trying to access Evil’s evil-ex prompt. Fear not, for we shall reclaim
our territory:
(with-eval-after-load 'evil-maps
(define-key evil-motion-state-map (kbd ":") 'evil-ex))
(defun my-magit-setup ()
(define-key evil-normal-state-local-map (kbd ":") 'evil-ex))
(add-hook 'magit-status-mode-hook 'my-magit-setup)
With these configurations, we restore the full power of Evil’s evil-ex
prompt, allowing us to issue commands fearlessly.
To ensure a smooth voyage, we must tame the unseen forces that could
disrupt our Git journey. One such force is the “unbound suffix
<mouse movement>” error. By advising the
magit-key-mode-error-message function, we can suppress this error when
it’s triggered by mouse movement:
(defun ignore-mouse (ret)
(let ((debug-on-message "unbound suffix <mouse movement>"))
(if (called-interactively-p 'interactive)
ret
(ignore-errors (funcall ret)))))
(advice-add 'magit-key-mode-error-message :around 'ignore-mouse)
With this protection in place, you can navigate Magit buffers using your mouse without being interrupted by the “unbound suffix” error.
Congratulations! With these Git tools at your disposal, you are now equipped to embark on your version control adventures. May you sail through the Git seas with confidence and mastery, leaving a trail of impeccable code in your wake.

Kubernetes: Commanding the Container Fleet
Once upon a time, in the deep blue Emacs sea, there was a brave adventurer on a quest to tame the fearsome beast of Kubernetes. The tale begins with the collection of tools required for the daring voyage.
(unless (package-installed-p 'kubernetes)
(package-install 'kubernetes))
(require 'kubernetes)
(with-eval-after-load 'kubernetes
(unless (package-installed-p 'kubernetes-evil)
(package-refresh-contents)
(package-install 'kubernetes-evil)))
Our mariner, with the trusty map of package-installed-p, first ensured
the company of the mighty warrior, kubernetes. When found missing,
package-install was invoked to summon the warrior. The brave
kubernetes16 https://github.com/kubernetes-el/kubernetes-el was then made to pledge allegiance to the mariner. A
similar course of action was taken with the wily mercenary,
kubernetes-evil, ensuring a formidable party for the adventure ahead.
(setq kubernetes-parent-directories '("k8s" "manifests" "config"))
With the team assembled, the mariner set out on the mighty vessel,
seeking out the treacherous terrains named k8s, manifests, and
config. The name of these lands was encrypted in a secret scroll
labeled kubernetes-parent-directories.
;; Function to check if a parent directory exists
(defun kubernetes-enable-mode-if-parent-directory-exists ()
(let ((parent-dir (file-name-nondirectory (directory-file-name (file-name-directory buffer-file-name)))))
(when (member parent-dir kubernetes-parent-directories)
(kubernetes-mode))))
;; Add a hook to enable kubernetes-mode based on parent directory
(add-hook 'yaml-mode-hook 'kubernetes-enable-mode-if-parent-directory-exists)
With a clever device - a hook paired with a function, our mariner
ensured that upon sailing through the stormy YAML sea, the presence of
one of the desired lands triggered the transformation of the ship,
morphing it into a kubernetes-mode leviathan, ready to face any storm.
(setq kubernetes-namespace "default")
As they ventured forth, the mariner christened their current realm as
the default namespace - their home in the chaotic sea, their sanctuary
amidst the turmoil.
;; Function to deploy the current buffer in Kubernetes
(defun kubernetes-deploy-buffer ()
"Deploy the currently opened buffer using kubectl apply -f."
(interactive)
(let ((file (buffer-file-name)))
(if (not (and file (file-exists-p file)))
(message "Buffer is not visiting a file.")
(async-shell-command (concat "kubectl apply -f " file)))))
Armed with a magical charm, the kubernetes-deploy-buffer, the mariner
could summon their mighty forces into action at will. A mere invocation
was enough to deploy the fleet on the battlefield.

;; Function to list Kubernetes namespaces and switch to selected namespace
(defun kubernetes-switch-namespace ()
"List available namespaces and switch to selected namespace."
(interactive)
(let ((output-buffer (get-buffer-create "*kubernetes-namespace*")))
(with-current-buffer output-buffer
(erase-buffer)
(call-process "kubectl" nil t nil "get" "namespaces"))
(let ((namespace (completing-read "Switch to namespace: "
(split-string (with-current-buffer output-buffer (buffer-string)) "\n" t))))
(unless (string-empty-p namespace)
(async-shell-command (concat "kubectl config set-context --current --namespace=" namespace))
(message "Switched to namespace: %s" namespace)))))
The mariner also possessed the mystical power to switch their ship to
any other namespace with the spell, kubernetes-switch-namespace. This
allowed them to nimbly navigate through the whirlpools of the Kube-sea.
(my-leader-def
:states '(normal visual emacs)
:keymaps 'override
"k" '(:ignore t :which-key "Kubernetes")
"kf" '(kubernetes-forward-resource :which-key "Forward resource")
"kb" '(kubernetes-backward-resource :which-key "Backward resource")
"kcr" '(kubernetes-create-resource :which-key "Create resource")
"kdel" '(kubernetes-delete-resource :which-key "Delete resource")
"ked" '(kubernetes-describe-resource :which-key "Describe resource")
"kex" '(kubernetes-exec-popup :which-key "Execute command in resource")
"kl" '(kubernetes-logs-popup :which-key "View resource logs")
"kp" '(kubernetes-pods :which-key "List pods")
"ks" '(kubernetes-set-namespace :which-key "Set namespace")
"kd" '(kubernetes-deploy-buffer :which-key "Deploy buffer")
"kn" '(kubernetes-switch-namespace :which-key "Switch namespace")
"ko" '(kubernetes-overview :which-key "Kubernetes overview"))
Lastly, a grand tapestry was woven, a guide to the whole adventure. Each spell, each charm, and each command were carefully etched onto this map, creating a comprehensive guide for our brave mariner.
This tale, fellow Emacs sailor, is one of thrill, exploration, and conquest, sailing the tumultuous Kubernetes sea. With tools and treasures abound, we traverse these waters, ready for what lies ahead. May your Emacs voyage be as fruitful and your adventures as exciting as our brave mariner’s journey.

Containers: The Legend of the Layered Vessel
Ahoy there, brave Emacs sailor! Let’s prepare for our new adventure in the stormy Docker seas, in search of legendary Containers, each akin to a floating island, ripe with opportunities and treasures!
(unless (package-installed-p 'docker)
(package-install 'docker))
(require 'docker)
(add-hook 'docker-mode-hook (lambda () (docker-container-ls)))
The journey begins in our homeland, ensuring that the courageous Docker
is aboard, ready to guide us through treacherous tides. With the trusty
docker-mode-hook, our ship is ever-ready to unfurl its sails,
revealing a tableau of Docker containers, awaiting exploration.
(defun docker-container-prompt (prompt)
"Prompt for a Docker container name."
(completing-read prompt (docker-container-names)))
(defun docker-container-names ()
"Retrieve a list of Docker container names."
(split-string (shell-command-to-string "docker ps -a --format '{{.Names}}'") "\n" t))
Our mariner conjured the spell docker-container-prompt that whispered
the names of all Docker containers, calling upon them like sirens of the
sea. An even more potent spell docker-container-names was forged to
summon the entire list of container names, each echoing like a chant
from the deep Docker sea.
(defun docker-kill-prompt ()
"Prompt and kill a Docker container."
(interactive)
(let ((container-name (docker-container-prompt "Kill container:")))
(docker-container-kill container-name)))
(defun docker-restart-prompt ()
"Prompt and restart a Docker container."
(interactive)
(let ((container-name (docker-container-prompt "Restart container:")))
(docker-container-restart container-name)))
(defun docker-start-prompt ()
"Prompt and start a Docker container."
(interactive)
(let ((container-name (docker-container-prompt "Start container:")))
(docker-container-start container-name)))
(defun docker-stop-prompt ()
"Prompt and stop a Docker container."
(interactive)
(let ((container-name (docker-container-prompt "Stop container:")))
(docker-container-stop container-name)))
(defun docker-tag-prompt ()
"Prompt and tag a Docker container."
(interactive)
(let ((container-name (docker-container-prompt "Tag container:")))
(docker-container-tag container-name)))
The mariner’s toolkit contained many powerful charms, each more wondrous
than the last. One could subdue an unruly container, the
docker-kill-prompt. Another breathed life into a dormant one, the
docker-start-prompt. If a container became sluggish,
docker-restart-prompt was used to rejuvenate it, while
docker-stop-prompt could coax an overly active one into submission.
Lastly, the docker-tag-prompt was used to mark the containers with
unique insignia, aiding the mariner in their journeys.
(my-leader-def
:states '(normal visual emacs)
:keymaps 'override
"c" '(:ignore t :which-key "Containers")
"ci" '(docker-images :which-key "Images (New Buffer)")
"cl" '(docker-logs :which-key "Logs (New Buffer)")
"cn" '(docker-network :which-key "Network (New Buffer)")
"cp" '(docker-ps :which-key "PS (New Buffer)")
"ck" '(docker-kill-prompt :which-key "Kill (Prompt)")
"cr" '(docker-restart-prompt :which-key "Restart (Prompt)")
"cs" '(docker-start-prompt :which-key "Start (Prompt)")
"cS" '(docker-stop-prompt :which-key "Stop (Prompt)")
"ct" '(docker-tag-prompt :which-key "Tag (Prompt)")
"ca" '(docker-attach :which-key "Attach")
"cb" '(docker-build-buffer :which-key "Build Buffer")
"cc" '(docker-compose :which-key "Compose")
"ce" '(docker-exec :which-key "Exec"))
Then a grand guide was etched onto a gleaming shell, the ‘my-leader-def’. This was a guide to navigate the Docker sea with ease, with each container under the mariner’s command. Be it the birth of new containers or their demise, the mariner stood at the helm, orchestrating their symphony.

Such is our tale, dear sailor, of an epic adventure in the Docker sea. Now, it’s time for you to write your own legend in the annals of the Emacs sea.
Chapter 4. Taming the Kraken: An Adventure in Org-Mode
Anchors Aweigh: Setting Up Org-Mode
Arrr, step aboard the mighty vessel of knowledge, the S.S. Org-Mode! This be not your regular ship, it be a floating archive of ye thoughts, a library of knowledge, a ship to navigate the vast ocean of ideas. Let’s hoist the sails!
(unless (package-installed-p 'org)
(package-install 'org))
We’re stocking up on our provisions, ye see. Without the trusty Org17 https://orgmode.org/, we’d be set adrift. Be it organising your thoughts like stowing cargo or keeping a captain’s log, it be essential.
(defvar my-org-root-directory "~/notes/org/"
"Root directory for Org mode files.")
(setq org-directory my-org-root-directory)
In the heart of our ship, a chest - the root directory. A grand treasure map of sorts, guiding us to all our Org files.
(defun my-org-open-index-file ()
"Open the index file for Org mode."
(interactive)
(find-file (expand-file-name "index.org" my-org-root-directory)))
Ahoy, the captain’s log! A simple incantation to unfurl it,
my-org-open-index-file, and it’s open for us to pen our thoughts,
musings, and tales.

(setq-local face-remapping-alist '((default (:height 1.5) variable-pitch)
(header-line (:height 4.0) variable-pitch)
(org-document-title (:height 1.75) org-document-title)
(org-code (:height 1.55) org-code)
(org-verbatim (:height 1.55) org-verbatim)
(org-block (:height 1.25) org-block)
(org-block-begin-line (:height 0.7) org-block)))
The captain likes his scrolls a particular way, see. A custom array,
face-remapping-alist, sets the parchment just right, tuning the height
of different parts like adjusting the tension in the rigging of a ship.
(unless (package-installed-p 'org-modern)
(package-install 'org-modern))
(add-hook 'org-mode-hook #'org-modern-mode)
Nothing like a bit of modern charm on the old Org-Mode. With ‘org-modern’, our ship stays seaworthy and trendy.
(add-hook 'org-mode-hook (lambda () (org-indent-mode)))
The captain insists on keeping the ship’s log neat and tidy. Indentation be the order of the day!
(setq
;; Edit settings
org-auto-align-tags nil
org-tags-column 0
org-catch-invisible-edits 'show-and-error
org-special-ctrl-a/e t
org-insert-heading-respect-content t
;; Org styling, hide markup etc.
org-hide-emphasis-markers t
org-pretty-entities t
org-ellipsis "…"
;; Agenda styling
org-agenda-tags-column 0
org-agenda-block-separator ?─
org-agenda-time-grid
'((daily today require-timed)
(800 1000 1200 1400 1600 1800 2000)
" ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄")
org-agenda-current-time-string
"⭠ now ─────────────────────────────────────────────────")
A flurry of captain’s orders followed, fine-tuning the look and feel of our ship’s logs - the visibility of emphasis markers, the style of ellipses, and more.
(setq org-modern-indent t)
(custom-set-faces
'(org-document-title ((t (:inherit outline-1 :height 1.5)))))
(custom-set-faces
'(org-level-1 ((t (:inherit outline-1 :height 1.1)))))
(setq org-todo-keywords
'((sequence "TODO" "LATER" "|" "DONE")))
More customization followed, the captain being a detail-oriented fellow. Everything, from the inheritance of the document title to the task keywords, had to be shipshape.
(global-org-modern-mode)
The whole ship was adorned with the ‘org-modern’ charm, setting the tone for our long voyage.

(setq org-default-notes-file (expand-file-name "inbox.org" my-org-root-directory))
(setq org-capture-templates
`(("c" "Capture" entry (file+headline org-default-notes-file "LATER")
"* LATER %?\n\n %i\n\n %a")))
The captain had an ingenious system for capturing fleeting ideas. Into the ‘inbox.org’ they went, to be sorted and dealt with later.
(my-leader-def
:states '(normal visual emacs)
:keymaps 'override
"o" '(:ignore t :which-key "Org Mode")
"oi" '(my-org-open-index-file :which-key "Open Index File")
"oc" '(org-capture :which-key "Capture"))
Lastly, the captain etched onto his map - the leader definition. It contained the quickest routes to key tasks: opening the index file and capturing thoughts. One could sail the sea of ideas with ease.
So, fellow sailor, that be our tale. The ship be prepared, the sails set. The vast sea of thoughts, ideas, and knowledge awaits us!
