Emacs literate configuration

Table of Contents

A foreword on this config and Lisp

Welcome to my literate Emacs configuration. As many specimens of the pedigree, this config ratifies that Newtonian aphorism involving giants. Many thanks to all the unsung heroes whose Emacs Lisp snippets assemble the DNA of this file. Apropos of Lisp, I have recently had a glimpse of the enlightenment promised by so many hacker's lore. Here is one of my favorites, written by RMS:

In days long ago,
When clocks were so slow,
These basic keystrokes
Took too long for folks,
Unless they were writ
In C code to flit.
Today that's not so,
In Lisp they can go.

Org-mode literate programming

The beauty of literate configuration with org-mode is that it allows for maintaining an Emacs config, a website, and an org-mode markup file at the same time. For example, in the header of this file you can find the following code:

#+TITLE: Emacs literate configuration
#+PROPERTY: header-args :tangle ../init.el
#+OPTIONS: toc:2 num:nil
#+SETUPFILE: https://fniessen.github.io/org-html-themes/org/theme-readtheorg.setup

With these options, I tangle (every time I save, see the org-mode hook) the source code blocks in this config to my Emacs init.el, and I set up the ReadTheOrg theme that the website uses. A somehow tricky aspect is to get syntax highlighting when exporting to HTML with org-mode using Emacs in batch mode. You can take a look at my build-site.el script in the source repo to see how I achieve this in practice.

Automatic configuration with Nix

Although it is possible to configure Emacs with Nix, I prefer to use the Elisp configuration. However, I do use the following overlay, together with Cachix to have the latest commits of the Emacs' master branch:

nixpkgs.overlays = [
  # Emacs overlay
  (import (builtins.fetchTarball {
    url = https://github.com/nix-community/emacs-overlay/archive/master.tar.gz;
  }))
];

environment.systemPackages = with pkgs; [
  emacsGitNativeComp
];

My NixOS configuration is version controlled with git, and can automatically deploy my Emacs distribution with home-manager:

# Emacs configuration
home.file.".emacs.d/" = {
  source = pkgs.fetchFromGitHub {
    owner = "Panadestein";
    repo = "emacsd";
    rev = "f0713755fda2578638246ddb99e0cf26763cdd47";
    sha256 = "00230dm4ssjxyn5crw7qjazs0zk8y2s3ir7ddq5p7rb7x4glgxh4";
  };
  recursive = true;
  onChange = builtins.readFile /etc/nixos/dotfiles/set_emacs.sh;
};
#!/usr/bin/env bash

cd "$HOME"/.emacs.d
mkdir -p ./lib ./snippets/irp-mode ./undo
emacs --batch --eval "(require 'org)" --eval '(org-babel-tangle-file "./content/index.org")'

General configuration

To comply with the Emacs conventions for libraries, the tangled init.el must have the following header and footer:

;;; init.el --- Panadestein's Emacs configuration -*- lexical-binding: t -*-

;; Copyright (C) 2020-2022 Ramón L. Panadés Barrueta

;; Author: Ramón L. Panadés Barrueta <rpana92@gmail.com>
;; Keywords: internal
;; URL: https://panadestein.github.io/emacsd/

;;; Commentary:
;; A fully fledged, reproducible Emacs configuration

;;; Code:

Setting up a package manager

One of the cornerstones of any Emacs distribution is the package manager. In my case, I was mostly a happy user of use-package with the package.el backend. However, I did find myself in the need of using a more recent version of some packages (like org-mode). While there are tools like Quelpa that allow this without ditching package.el, I decided to go for the option that ensures reproducibility: straight.el. With this purely functional package manager, I feel closer to the high ideals of the Nix community.

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

In order to use straight.el properly, we need to add the following content to our early-init.el file:

;;; early-init.el --- Emacs early init file
;;; Commentary:
;; Code to run before init.el is loaded.
;;; Code:

;; Disable package.el
(setq package-enable-at-startup nil)

(provide 'early-init)
;;; early-init.el ends here

We can also have a nice integration with use-package:

(straight-use-package 'use-package)
(use-package straight
  :custom (straight-use-package-by-default t)
  :bind  (("C-<f2>" . hydra-straight-helper/body)))

For reference, I still show here how to set use-package with package.el. First we add the GNU ELPA and MELPA archives, and then require use-package:

(setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
                         ("melpa" . "https://melpa.org/packages/"))
      package-quickstart nil)

(eval-and-compile
  (unless (and (fboundp 'package-installed-p)
               (package-installed-p 'use-package))
    (package-refresh-contents)
    (package-install 'use-package))
  (if init-file-debug  
      (setq use-package-compute-statistics t)
    (setq use-package-compute-statistics nil))
  (setq use-package-always-ensure t)
  (setq use-package-expand-minimally t)
  (require 'use-package))

One editor to rule them all, one editor to find them

Here I use the use-package macro to keep all Emacs internal settings organized in one single block. The variables have self-explanatory names, but I have added a few comments for the sake of clarity.

(use-package emacs
  :straight nil
  :preface
  (defun my-reload-emacs ()
    "Reload the Emacs configuration"
    (interactive)
    (load-file "~/.emacs.d/init.el"))
  (defun revert-buffer-no-confirm ()
    "Revert buffer without confirmation."
    (interactive) (revert-buffer t t))
  :config
  ;; Emacs internal options
  (setq-default initial-scratch-message ";; Welcome Panadestein!!"
                ring-bell-function #'ignore
                user-full-name "Ramón L. Panadés-Barrueta, PhD"
                inhibit-startup-screen t
                custom-safe-themes t)
  (setq backup-directory-alist
        `(("." . ,(concat user-emacs-directory "backups"))))
  (if init-file-debug
      (setq warning-minimum-level :debug)
    (setq warning-minimum-level :emergency))
  (menu-bar-mode -1)
  (tool-bar-mode -1)
  (show-paren-mode 1)
  (fringe-mode '(0 . 0))
  ;; No scroll bar, always full screen
  (add-to-list 'default-frame-alist
               '(vertical-scroll-bars . nil))
  (add-to-list 'default-frame-alist '(fullscreen . maximized))
  ;; Font
  (add-to-list 'default-frame-alist '(font . "Fira Code-18"))
  ;; Terminal transparency
  (face-spec-set 'default
                 '((((type tty)) :background "unspecified-bg")))
  ;; Line numbers
  (when (version<= "26.0.50" emacs-version)
    (global-display-line-numbers-mode)
    (setq display-line-numbers-type 'relative))
  ;; Remember line number
  (if (fboundp #'save-place-mode)
      (save-place-mode +1)
    (setq-default save-place t))
  ;; Mimetypes
  (setq mailcap-user-mime-data
        '((type . "application/pdf")
          (viewer . pdf-view-mode)))
  :bind
  (;; Make ESC close prompts
   ("<escape>" . keyboard-escape-quit)
   ("C-c C-r" . revert-buffer-no-confirm)))

Prevent custom from messing with my config file

This helps to clear the clutter and increases reproducibility of the config

(let
    ((customization-file (expand-file-name "custom.el" user-emacs-directory)))
  (when (file-exists-p customization-file)
    (setq custom-file customization-file)
    (load custom-file 'noerror)))

Only highlight programming and text buffers

(use-package hl-line
  :straight nil
  :hook
  (prog-mode . hl-line-mode)
  (text-mode . hl-line-mode))

Old habits die hard

I cannot just trash years of Vim muscle memory workout. However, the Emacs keybindings slowly start to klappt, and for some major modes it just does not make sense to use Evil.

(use-package evil
  :straight t
  :demand t
  :config
  (evil-mode 1)
  (evil-set-undo-system 'undo-tree)
  (mapc (lambda (mode)
          (evil-set-initial-state mode 'emacs))
        '(eww-mode
          profiler-report-mode
          elfeed-mode
          helpful-mode
          pdf-view-mode
          eshell-mode
          vterm-mode)))

Linear undo system

This is one of the not so awesome quirks of Emacs IMHO. Maybe one day I will understand its power. I keep it linear for the moment.

(use-package undo-tree
  :straight t
  :init
  (global-undo-tree-mode)
  :config
  (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))
  (setq undo-tree-visualizer-timestamps t)
  (setq undo-tree-visualizer-diff t))

Themes

As Nietzsche said, the voice of beauty speaks softly; it creeps only into the most fully awakened souls. Ergo the importance of a visually pleasant editor. These are my top three themes:

(use-package doom-themes
  :straight t
  :config
  ;; Global settings (defaults)
  (setq doom-themes-enable-bold t
        doom-themes-enable-italic t)
  (load-theme 'doom-vibrant t)
  ;; Enable flashing mode-line on errors
  (doom-themes-visual-bell-config)
  ;; Enable custom neotree theme (all-the-icons must be installed!)
  (doom-themes-neotree-config)
  ;; or for treemacs users
  (setq doom-themes-treemacs-theme "doom-atom")
  (setq doom-themes-treemacs-enable-variable-pitch nil)
  (doom-themes-treemacs-config)
  ;; Corrects (and improves) org-mode's native fontification.
  (doom-themes-org-config))

(use-package gruvbox-theme
  :straight t
  :disabled
  :init (load-theme 'gruvbox-dark-soft t))

(use-package blackboard-theme
  :straight t
  :disabled
  :init (load-theme 'blackboard t))

And of course, highlight numbers in source code:

(use-package highlight-numbers
  :straight t
  :hook
  (prog-mode . highlight-numbers-mode))

Handle very long lines

Another well-known issue of Emacs, although it rarely affects me.

(use-package so-long
  :straight nil
  :hook
  (after-init-hook . global-so-long-mode))

Improved parentheses

This helps a lot when lisping.

(use-package smartparens
  :straight t
  :hook (prog-mode . smartparens-mode))

Highlight parentheses with different color

Another eye candy. Parenthesis of different colors are very helpful in Lisp though.

(use-package rainbow-delimiters
  :straight t
  :hook (prog-mode . rainbow-delimiters-mode))

Minimap

This package implements Sublime Text's minimap in Emacs. Although I am quasi-satisfied with this config, I am not ready to use it on a regular basis.

(use-package minimap
  :straight t
  :config
  (setq minimap-window-location 'right)
  (setq minimap-minimum-width 10)
  (setq minimap-dedicated-window t)
  (setq minimap-hide-cursor t)
  (setq minimap-hide-scroll-bar t)
  (setq minimap-hide-fringes t))

Fancy mode line

Another gift from the Doom Emacs community. Best modeline I have seen for Emacs. The additional package all-the-icons provides awesome icons not only for the modeline, but also for file managers and many other packages.

(use-package all-the-icons
  ;; Needs a manual `M-x all-the-icons-install-fonts`
  :straight t)

(use-package doom-modeline
  :straight t
  :init (doom-modeline-mode 1)
  :config
  (setq doom-modeline-height 55)
  (setq doom-modeline-buffer-file-name-style 'relative-to-project)
  (setq doom-line-numbers-style 'relative)
  (setq doom-modeline-major-mode-icon t)
  (setq doom-modeline-buffer-state-icon t)
  (setq doom-modeline-major-mode-color-icon t))

Multiple cursors support

This package is very interesting, although not so ergonomic IMHO. Here are some of the most relevant commands:

  • C-n make and go to next match
  • C-p make and go to previous match
  • g r q stop all cursors
(use-package evil-mc
  :straight t
  :config
  (global-evil-mc-mode 1))

Tabs-bar-mode

I could not manage to use this package without side effects in Evil-mode. Here is a simple usage:

  • C-x t f "filename" opens file in new tab
  • C-x t 0 closes current tab
(use-package tabbar
  :straight t
  :bind (("C-<right>" . tabbar-forward)))

Fancy HTML exports

This package ensures that some org-mode babel exports have proper syntax highlighting.

(use-package htmlize
  :straight t)

IDE features and help

Complete everything (but code) in Emacs with ivy

The usage of ivy and their autocompletion army (swiper, counsel) has greatly increased my productivity. It is also very well integrated with the Doom Emacs theme I use. The swiper search feature (C-s) is far more powerful than the default Emacs counterpart.

(use-package ivy
  :straight t
  :diminish
  :bind
  (("C-s" . swiper))
  :config
  (setq ivy-use-virtual-buffers t)
  (ivy-mode 1))

(use-package swiper
  :straight t)

(use-package counsel
  :straight t
  :after ivy
  :hook
  (after-init . counsel-mode)
  :config (counsel-mode)
  :bind
  ("M-x" . counsel-M-x)
  ("C-x b" . counsel-switch-buffer)
  ("C-c f" . counsel-recentf)
  ("C-M-l" . counsel-imenu)
  ("C-x C-f" . counsel-find-file)
  ("<f1> v" . counsel-describe-variable)
  ("<f1> f" . counsel-descbinds-function))

(use-package ivy-prescient
  :straight t
  :after counsel
  :config
  (ivy-prescient-mode 1))

(use-package ivy-rich
  :straight t
  :init
  (ivy-rich-mode 1)
  :after counsel
  :config
  (setq ivy-format-function #'ivy-format-function-line)
  (setq ivy-rich-display-transformers-list
        (plist-put ivy-rich-display-transformers-list
                   'ivy-switch-buffer
                   '(:columns
                     ((ivy-rich-candidate (:width 40))
                      (ivy-rich-switch-buffer-indicators
                       (:width 4 :face error :align right))
                      (ivy-rich-switch-buffer-major-mode
                       (:width 12 :face warning))
                      (ivy-rich-switch-buffer-project
                       (:width 15 :face success))
                      (ivy-rich-switch-buffer-path
                       (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path
                                            x (ivy-rich-minibuffer-width 0.3))))))))))

(use-package all-the-icons-ivy
  :straight t
  :demand t)

Jump anywhere in Emacs with avy

This powerful package comes with tons of features for jumping to visible text using a char-based decision tree. I have a minimal configuration, as avy-goto-char-timer achieves most of the functionality I need:

(use-package avy
  :straight t
  :bind (("M-j" . avy-goto-char-timer)))

Create awesome command combos and menus with hydra

This packages makes it very easy to create keybindings combinations using a common prefix. It also generates a menu showing the possible commands:

(use-package hydra
  :straight t)

(use-package use-package-hydra
  :straight t)

Here is a nice example for straight.el (the :color keyword symbol has a special purpose here):

(defhydra hydra-straight-helper (:hint nil :color pink)
  "
_c_: check all       _f_: fetch all     _m_: merge all       _n_: normalize all      _u_: push all
_C_: check package   _F_: fetch package _M_: merge package   _N_: normalize package  _U_: push package
_r_: rebuild all     _p_: pull all      _w_: watcher start   _g_: get recipe         _v_: versions freeze
_R_: rebuild package _P_: pull package  _W_: watcher quit    _e_: prune build        _q_: quit
"
  ("c" straight-check-all)
  ("C" straight-check-package)
  ("r" straight-rebuild-all)
  ("R" straight-rebuild-package)
  ("f" straight-fetch-all)
  ("F" straight-fetch-package)
  ("p" straight-pull-all)
  ("P" straight-pull-package)
  ("m" straight-merge-all)
  ("M" straight-merge-package)
  ("n" straight-normalize-all)
  ("N" straight-normalize-package)
  ("u" straight-push-all)
  ("U" straight-push-package)
  ("w" straight-watcher-start)
  ("W" straight-watcher-quit)
  ("g" straight-get-recipe)
  ("e" straight-prune-build)
  ("v" straight-freeze-versions)
  ("q" nil :color blue))

Command's information with which-key

Which-key is a must-have to sail the raging seas of Emacs' packages keybindings. These suggestions come very handy when exploring a new package.

(use-package which-key
  :straight t
  :diminish
  :custom
  (which-key-idle-secondary-delay 0.01)
  (which-key-dont-use-unicode t)
  :config
  (which-key-mode t))

Improved help system with Helpful

Emacs default help system is very good. Helpful makes it awesome by adding colors and source code exploration.

(use-package helpful
  :straight t
  :custom
  (counsel-describe-variable-function #'helpful-variable)
  :bind
  ("C-h f" . helpful-function)
  ([remap describe-symbol] . helpful-symbol)
  ([remap describe-variable] . helpful-variable)
  ([remap describe-command] . helpful-command)
  ([remap describe-key] . helpful-key))

Code tags

Using code tags when developing in large scale codes (like electronic structure packages) can save your fingers from grep induced damage. I use the minimalistic gtags-mode that relies as much as possible on already existing Emacs tools. I also have configured the counsel-etags package, though its approach of parsing the TAGS file makes it very slow when used with TRAMP.

(use-package gtags-mode
  :straight t
  :hook ((emacs-startup . gtags-mode)))

(use-package counsel-etags
  :straight t
  :disabled
  :bind (("C-]" . counsel-etags-find-tag-at-point))
  :init
  (add-hook 'prog-mode-hook
            (lambda ()
              (add-hook 'after-save-hook
                        'counsel-etags-virtual-update-tags 'append 'local)))
  :config
  (setq counsel-etags-update-interval 60)
  (push "build" counsel-etags-ignore-directories))

Completion with company

Source code completions. An all-time classic.

(use-package company
  :straight t
  :init
  (add-hook 'after-init-hook 'global-company-mode)
  (setq company-minimum-prefix-length 3
        company-selection-wrap-around t
        company-tooltip-limit 20
        company-tooltip-minimum-width 15
        company-tooltip-align-annotations t))

Language server protocol

Microsoft's Language Server Protocol (LSP) is a great tool to provide IDE features to Emacs. I use two major-modes for managing the lsp servers, namely lsp-mode and eglot. The latter maximizes the use of already existing Emacs infrastructure.

(use-package lsp-mode
  :straight t
  :init
  (defun my-lsp-hook ()                      
    "Do not use lsp-mode with tramp"         
    (unless (file-remote-p default-directory)
      (lsp)))                                
  :config
  (setq lsp-idle-delay 0.5
        lsp-enable-symbol-highlighting t
        lsp-enable-snippet nil
        lsp-pyls-plugins-flake8-enabled t)
  (lsp-register-custom-settings
   '(("pyls.plugins.pyls_mypy.enabled" nil nil)
     ("pyls.plugins.pyls_mypy.live_mode" nil t)
     ("pyls.plugins.pyls_black.enabled" t t)
     ("pyls.plugins.pyls_isort.enabled" t t)
     ;; Disable duplicated by flake8
     ("pyls.plugins.pycodestyle.enabled" nil t)
     ("pyls.plugins.mccabe.enabled" nil t)
     ("pyls.plugins.pyflakes.enabled" nil t)))
  :hook
  ((python-mode . my-lsp-hook)
   (f90-mode . my-lsp-hook)
   (lsp-mode . lsp-enable-which-key-integration)))

(use-package lsp-ui
  :straight t
  :hook (lsp-mode . lsp-ui-mode)
  :config
  (setq lsp-ui-sideline-show-hover t
        lsp-ui-sideline-delay 0.5
        lsp-ui-doc-delay 5
        lsp-ui-sideline-ignore-duplicates t
        lsp-ui-doc-position 'bottom
        lsp-ui-doc-alignment 'frame
        lsp-ui-doc-header nil
        lsp-ui-doc-include-signature t
        lsp-ui-doc-use-childframe t)
  :commands lsp-ui-mode)

(use-package lsp-treemacs
  :after treemacs lsp
  :straight t)

It is possible to create lsp-mode clients that should work over ssh. An example for Fortran and Python:

(lsp-register-client
 (make-lsp-client :new-connection (lsp-tramp-connection "<fortls>")
                  :major-modes '(f90-mode)
                  :remote? t
                  :server-id 'fortls-remote))
(lsp-register-client
 (make-lsp-client :new-connection (lsp-tramp-connection "<pyls>")
                  :major-modes '(python-mode)
                  :remote? t
                  :server-id 'pyls-remote))

On my experience however, eglot has better support for editing with TRAMP. As you can see, there is little configuration here, as most of the defaults are optimal for my workflow.

(use-package eglot
  :straight t
  :defer t)

Spell checking with FlySpell

FlySpell does a lot out of the box, therefore I have a very basic config.

(use-package flyspell
  :straight nil
  :hook
  ((prog-mode . flyspell-prog-mode)
   (text-mode . turn-on-flyspell))
  :config
  (flyspell-mode +1))

Grammar checking with LanguageTool

This is a free and open-source multilingual grammar checker that supports more than thirty languages. A must-have if you write papers in LaTeX or org-mode. For the Emacs package to work one needs to install the LanguageTool package, and start the HTTP server for example as a systemd unit.

(use-package langtool
  :straight t
  :init
  (setq langtool-http-server-host "localhost"
        langtool-http-server-port 8081))

Translator

This is a very powerful translator integrated with Emacs. It supports multiple translation engines such as Google, Bing, and DeepL. I prefer to use pop-ups for the translation, so I need to install the posframe package in addition. The translation directions can be changed in the prompt with C-n and C-p.

(use-package go-translate
  :straight t
  :bind
  (("C-c C-t" . gts-do-translate))
  :config
  (setq gts-translate-list '(("de" "en")
                             ("fr" "en")
                             ("en" "es")))
  (setq gts-default-translator
        (gts-translator
         :picker
         (gts-prompt-picker)
         :engines
         (list (gts-bing-engine) (gts-google-engine))
         :render
         (gts-posframe-pop-render))))

(use-package posframe
  :straight t)

Syntax checking with Flycheck

Flycheck is another helpful tool. It has some limitations though. For instance, the continuous checking can harm performance, and some checkers can introduce huge penalties (I had to disable mypy). With this config I only check when saving.

(use-package flycheck
  :init
  (setq-default flycheck-disabled-checkers '(python-mypy))
  :config
  (setq flycheck-check-syntax-automatically '(save mode-enable))
  (setq flycheck-scheme-chicken-executable "csc")
  :hook
  (after-init . global-flycheck-mode))

Snippets

Yasnippet is a fairly complete collection of snippets with a very intuitive syntax. Painful is the absence of Scheme snippets. I have written some custom snippets for the IRPF90 derived mode, which I also include here.

(use-package yasnippet
  :straight t
  :config
  (yas-global-mode 1))

(use-package yasnippet-snippets
  :straight t
  :after yasnippet)
# -*- mode: snippet -*-
# name: ASSERT 
# key: ass
# --
ASSERT ($0)%
# -*- mode: snippet -*-
# name: BEGIN_DOC ... END_DOC
# key: bc
# --
BEGIN_DOC
! $0
END_DOC%   
# -*- mode: snippet -*-
# name: BEGIN_PROVIDER ... END_PROVIDER 
# key: bp
# --
BEGIN_PROVIDER [${1:integer}, ${2:var}]
    $0
END_PROVIDER%  
# -*- mode: snippet -*-
# -*- mode: snippet -*-
# name: BEGIN_SHELL ... END_SHELL 
# key: bsh
# --
BEGIN_SHELL [ ${1:/bin/bash} ]
    $0
END_SHELL%  

Better ediff configuration

Emacs built-in ediff is more powerful than vimdiff IMHO. However, the default configuration can be improved a bit:

(use-package ediff
  :straight nil
  :preface
  (defvar my-ediff-original-windows nil)
  (defun my-store-pre-ediff-winconfig ()
    "Stores the window arrangement before opening ediff."
    (setq my-ediff-original-windows (current-window-configuration)))
  (defun my-restore-pre-ediff-winconfig ()
    "Resets original window arrangement"
    (set-window-configuration my-ediff-original-windows))
  :hook
  ((ediff-before-setup . my-store-pre-ediff-winconfig)
   (ediff-quit . my-restore-pre-ediff-winconfig))
  :config
  (setq ediff-window-setup-function 'ediff-setup-windows-plain
        ediff-split-window-function 'split-window-horizontally))

File browsing, terminal emulators, and web browsing

Terminal support with Vterm

This package uses a C library instead of Lisp to emulate the terminals. It is my favorite emulator currently.

(use-package vterm
  :straight t
  :preface
  (defun my/vterm-mode-hook ()
    (hl-line-mode -1)
    (display-line-numbers-mode -1)
    (display-fill-column-indicator-mode -1)
    (auto-fill-mode -1))
  :hook
  ((vterm-mode . my/vterm-mode-hook))
  :custom
  (vterm-kill-buffer-on-exit t)
  (vterm-max-scrollback 10000)
  (vterm-tramp-shells '(("ssh" "/bin/bash")))
  :init
  (which-key-add-key-based-replacements "C-c t" "term")
  :config
  ;; Add find-file-other-window to accepted commands
  (add-to-list 'vterm-eval-cmds
               '("find-file-other-window" find-file-other-window)))

(use-package vterm-toggle
  :straight t
  :bind (("C-`" . vterm-toggle-cd)
         :map vterm-mode-map
         (("<C-return>" . vterm-toggle-insert-cd)
          ("C-M-n" . vterm-toggle-forward)
          ("C-M-p" . vterm-toggle-backward)))
  :custom
  (vterm-toggle-scope 'project)
  (vterm-toggle-project-root t)
  (vterm-toggle-fullscreen-p nil)
  :config
  ;; Show at bottom
  (add-to-list 'display-buffer-alist
               '((lambda(bufname _)
                   (with-current-buffer bufname
                     (equal major-mode 'vterm-mode)))
                 ;; (display-buffer-reuse-window display-buffer-at-bottom)
                 (display-buffer-reuse-window display-buffer-in-direction)
                 ;;display-buffer-in-direction/direction/dedicated is added in emacs27
                 (direction . bottom)
                 (dedicated . t) ;dedicated is supported in emacs27
                 (reusable-frames . visible)
                 (window-height . 0.3))))

A very nice vterm feature is the possibility of message passing, allowing for executing Emacs commands from within a vterm buffer, even remote ones. It requires some configuration on the .bashrc. For example, for opening remote files, one could do the following:

vterm_printf(){
    if [ -n "$TMUX" ] && ([ "${TERM%%-*}" = "tmux" ] || [ "${TERM%%-*}" = "screen" ] ); then
        # Tell tmux to pass the escape sequences through
        printf "\ePtmux;\e\e]%s\007\e\\" "$1"
    elif [ "${TERM%%-*}" = "screen" ]; then
        # GNU screen (screen, screen-256color, screen-256color-bce)
        printf "\eP\e]%s\007\e\\" "$1"
    else
        printf "\e]%s\e\\" "$1"
    fi
}

vterm_cmd() {
    local vterm_elisp
    vterm_elisp=""
    while [ $# -gt 0 ]; do
        vterm_elisp="$vterm_elisp""$(printf '"%s" ' "$(printf "%s" "$1" | sed -e 's|\\|\\\\|g' -e 's|"|\\"|g')")"
        shift
    done
    vterm_printf "51;E$vterm_elisp"
}

find_file() {
    echo "/ssh:$(whoami)@$(hostname):$(realpath "${@:-.}")"
    vterm_cmd find-file "/ssh:remotehost:$(realpath "${@:-.}")"
}

Terminal support with Eshell

This is a command shell written purely in Emacs Lisp. While not being POSIX compliant, it is still a powerful tool for remote editing due to its native TRAMP support. Here I define a custom prompt that resembles my Zsh one. Use clear 1 to have the same behaviour of bash's clear function.

(use-package eshell
  :straight nil
  :preface
  (defun my-exit ()
    "Remove window when exiting"
    (when (not (one-window-p))
      (delete-window)))
  (defun my-eshell-prompt ()
    "A nice prompt"
    (concat
     "(" (user-login-name) " . "
     (car (reverse (split-string (eshell/pwd) "/"))) ") "))
  :hook
  ((eshell-mode . (lambda ()
                    (add-to-list 'eshell-visual-commands "ssh")
                    (add-to-list 'eshell-visual-commands "tail")
                    (add-to-list 'eshell-visual-commands "top")
                    (eshell/alias "e" "find-file-other-window $1")
                    (eshell/alias "d" "dired-other-window $1") 
                    (eshell/alias "mgu" "magit-diff-unstaged")
                    (eshell/alias "mg" "magit-diff-staged"))))
  :custom
  (eshell-banner-message
   (propertize
    (let ((default-directory "~"))
      (shell-command-to-string "figlet Eshell"))
    'face `(:foreground "#cc5492")))
  (eshell-history-size 10000)
  (eshell-hist-ignore-dups t)
  (eshell-buffer-maximum-lines 10000)
  (eshell-scroll-to-bottom-on-input t)
  (eshell-destroy-buffer-when-process-dies t)
  (eshell-prompt-regexp "^[^\)]*[\)] ")
  (eshell-prompt-function #'my-eshell-prompt)
  :config
  (advice-add 'eshell-life-is-too-much :after 'my-exit)
  (setenv "PAGER" "cat"))

(use-package eshell-toggle
  :straight t
  :custom
  (eshell-toggle-size-fraction 3)
  (eshell-toggle-use-projectile-root nil)
  (eshell-toggle-run-command nil)
  (eshell-toggle-init-function #'eshell-toggle-init-eshell)
  :bind
  ("C-c p" . eshell-toggle))

(use-package eshell-syntax-highlighting
  :straight t
  :config
  (eshell-syntax-highlighting-global-mode +1))

(use-package fish-completion
  :straight t
  :hook
  (eshell-mode . fish-completion-mode))

TRAMP

Another must have of Emacs. This package together with dired saves me from configuring my editor all the time in obscure supercomputers around the world.

(use-package tramp                                        
  :straight nil                                             
  :custom                                                 
  (tramp-default-method "ssh")                           
  :config
  (setq remote-file-name-inhibit-cache nil)
  (setq vc-ignore-dir-regexp               
        (format "%s\\|%s"                  
                vc-ignore-dir-regexp       
                tramp-file-name-regexp))   
  (setq tramp-verbose 1))                  

(use-package tramp-sh                                     
  :straight nil                                             
  :config                                                 
  (add-to-list 'tramp-remote-path 'tramp-own-remote-path))

File browsers

After trying a couple of file browsers I understood that all I needed was already there: dired.

(use-package ranger
  :straight t
  :disabled
  :config
  (setq ranger-preview-file t))

(use-package neotree
  :straight t
  :disabled
  :bind ("<f9>" . 'neotree-toggle)
  :init
  ;; slow rendering
  (setq inhibit-compacting-font-caches t)
  ;; set icons theme
  (setq neo-theme (if (display-graphic-p) 'icons 'arrow))
  (setq neo-smart-open t))

(use-package treemacs
  :straight t
  :defer t
  :bind
  (:map global-map
        ("<f8>" . treemacs)))

(use-package treemacs-evil
  :after (treemacs evil)
  :straight t)

(use-package treemacs-magit
  :after (treemacs magit)
  :straight t)

(use-package all-the-icons-dired
  :straight t)

(use-package dired
  ;; TIP: use ( to hide file information
  :straight nil
  :custom ((dired-listing-switches "-agho --group-directories-first"))
  :commands (dired dired-jump)
  :bind (("C-x C-j" . dired-jump))
  :hook
  (dired-mode . all-the-icons-dired-mode)
  :config
  (evil-define-key 'normal 'dired-mode-map
    "h" 'dired-single-up-directory
    "l" 'dired-single-buffer))

(use-package dired-single
  :straight t)

Web browsers

And because in the Emacs operating system we can do just about anything, I have added the shr-tag-pre-highlight and shrface packages, so eww gets an org-mode look and feel. This comes handy when comparing programming languages in Rosetta Code.

(use-package shr-tag-pre-highlight
  :straight t
  :init
  (defun shrface-shr-tag-pre-highlight (pre)
    "Highlighting code in PRE."
    (let* ((shr-folding-mode 'none)
           (shr-current-font 'default)
           (code (with-temp-buffer
                   (shr-generic pre)
                   (buffer-string)))
           (lang (or (shr-tag-pre-highlight-guess-language-attr pre)
                     (let ((sym (language-detection-string code)))
                       (and sym (symbol-name sym)))))
           (mode (and lang
                      (shr-tag-pre-highlight--get-lang-mode lang))))
      (shr-ensure-newline)
      (shr-ensure-newline)
      (setq start (point))
      (insert
       (propertize (concat "#+BEGIN_SRC " lang "\n") 'face 'org-block-begin-line)
       (or (and (fboundp mode)
                (with-demoted-errors "Error while fontifying: %S"
                  (shr-tag-pre-highlight-fontify code mode)))
           code)
       (propertize "#+END_SRC" 'face 'org-block-end-line ))
      (shr-ensure-newline)
      (setq end (point))
      (add-face-text-property start end '(:background "#1f2329" :extend t))
      (shr-ensure-newline)
      (insert "\n")))
  :config
  (add-to-list 'shr-external-rendering-functions
               '(pre . shrface-shr-tag-pre-highlight)))

(use-package shrface
  :straight t
  :config
  (shrface-basic)
  (shrface-trial)
  (shrface-default-keybindings)
  (setq shrface-href-versatile t))

(use-package eww
  :straight nil
  :hook
  ((eww-after-render . shrface-mode))
  :config
  (require 'shrface))

And now that we are here, why don't we surf YouTube with this amazing package:

(use-package elfeed
  :straight t)

(use-package elfeed-tube
  :straight t
  :after elfeed
  :config
  (elfeed-tube-setup)
  :bind
  (:map elfeed-show-mode-map
        ("F" . elfeed-tube-fetch)
        ([remap save-buffer] . elfeed-tube-save)
        :map elfeed-search-mode-map
        ("F" . elfeed-tube-fetch)
        ([remap save-buffer] . elfeed-tube-save)))

(use-package elfeed-tube-mpv
  :straight t
  :bind
  (:map elfeed-show-mode-map
        ("C-c C-f" . elfeed-tube-mpv-follow-mode)
        ("C-c C-w" . elfeed-tube-mpv-where)))

IRC and Matrix

Software tinkerers tend to hang out on IRCs a lot, so this configuration is very useful to get help on those channels, and keeping in touch with friends.

(use-package ement
  :straight (:type git
             :host github
             :repo "alphapapa/ement.el"))

PDF support

Another C Library, it works better than the default Emacs PDF viewer, for instance it allows hyperlinks.

(use-package pdf-tools
  :straight t
  :bind
  (:map pdf-view-mode-map
        ("C-s" . isearch-forward-regexp))
  :hook
  ((pdf-view-mode . (lambda ()
                      (display-line-numbers-mode -1))))
  :config
  (setq-default pdf-view-display-size 'fit-page)
  (setq pdf-annot-activate-created-annotations t)
  (pdf-tools-install))

Multimedia player

For multimedia content I use emms, a lightweight player written in Emacs Lisp, that integrates perfectly with mpv.

(use-package emms
  :straight t
  :config
  (require 'emms-setup)
  (emms-all)
  (add-to-list 'emms-player-list 'emms-player-mpv t)
  (emms-player-set emms-player-mpv
                   'regex
                   (rx (or (: "https://" (* nonl) "youtube.com" (* nonl))
                           (+ (? (or "https://" "http://"))
                              (* nonl)
                              (regexp (eval (emms-player-simple-regexp
                                             "mp4" "mov" "wmv" "webm" "flv" "avi" "mkv"))))))))

Alternatively, I use the empv package, which provides a more elaborated interface to the mpv player.

(use-package empv
  :straight (:type git
             :host github
             :repo "isamert/empv.el")
  :config
  (setq empv-invidious-instance "https://invidious.tiekoetter.com/api/v1"))

Programming

Here you can find my configuration for different programming languages. I will introduce it in a laconic manner, because this section is rather opinionated.

Lisp

The language that gave birth to the "Maxwell equations of software". Lisp dialects are my favorite programming languages. Below I add a vague description of some of the things I like from them.

General

Enhance the overall lisp experience in Emacs with Lispy, and its evil friend Lispyville.

(use-package lispy
  :straight t
  :hook ((emacs-lisp-mode . lispy-mode)
         (scheme-mode . lispy-mode)))

(use-package lispyville
  :straight t
  :hook (lispy-mode . lispyville-mode))

Common Lisp

Powerful compiler, great libraries and OOP features.

(use-package slime
  :straight t
  :mode ("\\.lisp\\'" . lisp-mode)
  :init
  (setq slime-net-coding-system 'utf-8-unix
        inferior-lisp-program "sbcl")
  (setq slime-contribs '(slime-fancy
                         slime-repl
                         slime-autodoc)))

Scheme and Racket

Minimalism and elegant syntax, the purest. Hygienic macros. Racket has a great documentation.

(use-package geiser
  :straight t
  :config
  (setq geiser-active-implementations '(chez)))

(use-package geiser-chez
  :straight t
  :init
  (setq geiser-chez-binary "scheme"))

(use-package racket-mode
  :straight t)

Clojure

Largest library ecosystem thanks to JVM, vectors and maps. Strong focus on functional programming.

(use-package clojure-mode
  :straight t)

(use-package cider
  :straight t
  :config
  (setq nrepl-log-messages t
        cider-font-lock-dynamically '(macro core function var)
        cider-overlays-use-font-lock t))

Hy

The youngest in this list, similar in spirit to Clojure, but translates to Python's AST.

(use-package hy-mode
  :straight t
  :mode "\\.hy\\'"
  :commands (hy-mode org-babel-execute:hy)
  :interpreter "hy"
  :hook
  (hy-mode . company-mode)
  (hy-mode . (lambda () (lispy-mode 1)))
  :config
  (add-hook 'hy-mode-hook #'paredit-mode)
  (add-hook 'hy-mode-hook #'rainbow-delimiters-mode))

Sometimes it is very useful to expand Emacs Lisp macros to better understand their functionality. For this, the macrostep package comes very handy.

(use-package macrostep
  :straight t
  :bind (:map emacs-lisp-mode-map
              ("C-c e" . macrostep-expand)))

Fortran

Fortran is a major component of my job as a physicist, this package provides all I need.

(use-package f90-mode
  :straight nil
  :mode ("\\.f90\\'")
  :hook
  (f90-mode . (lambda () (setq flycheck-gfortran-args "-ffree-form"))))

IRPF90

IRPF90 is an environment that allows for efficient functional programming in FORTRAN using the Implicit Reference to Parameters (IRP) method. I have developed a derived mode for IRPF90:

;;; irp-mode.el --- A major mode for dealing with IRPF90 files

;;; Commentary:
;; An attempt to support Scemama's IRPF90 in Emacs

;;; Code:

;; Define IRPF90 extended FORTRAN syntax

(defvar irp-font-lock-keywords)

(setq irp-font-lock-keywords
      (let* (
             ;; Define different keywords
             (x-keywords '("BEGIN_PROVIDER" "END_PROVIDER" "ASSERT"
                           "FREE" "PROVIDE" "BEGIN_TEMPLATE"
                           "END_TEMPLATE" "BEGIN_SHELL"
                           "END_SHELL" "IRP_IF" "IRP_ELSE" "TOUCH"
                           "SOFT_TOUCH"))
             (x-types '("double precision" "integer"))
             (x-comments '("BEGIN_DOC" "END_DOC"))

             ;; Generate regex
             (x-keywords-regexp (regexp-opt x-keywords 'words))
             (x-types-regexp (regexp-opt x-types 'words))
             (x-comments-regexp (regexp-opt x-comments 'words)))

        `(
          (,x-types-regexp . font-lock-type-face)
          (,x-keywords-regexp . font-lock-preprocessor-face)
          (,x-comments-regexp . font-lock-comment-face)
          )))

;;;###autoload
(define-derived-mode irp-mode f90-mode "irp mode"
  "Major mode for editing IRPF90 files."
  :syntax-table nil
  :abbrev-table nil
  (font-lock-add-keywords nil irp-font-lock-keywords))

(provide 'irp-mode)
;;; irp-mode.el ends here

This mode can be also configured with use-package:

(use-package irp-mode
  :straight nil
  :mode ("\\.irp.f\\'")
  :load-path "~/.emacs.d/lib")

C and C++

(use-package ccls
  :straight t
  :hook ((c-mode c++-mode objc-mode cuda-mode) .
         (lambda () (require 'ccls) (lsp)))
  :config
  (setq c-basic-offset 6)
  (setq c-default-style '((java-mode . "java")
                          (awk-mode . "awk")
                          (other . "linux"))))

Rust

(use-package rust-mode
  :straight t)

Makefile

Here it is important to mention that the following use-package declaration exists solely for aesthetic and organizational purposes. If we macrostep-expand it, we can convince ourselves:

(use-package make-mode
  :straight nil)

which expands to

(progn
  (use-package-ensure-elpa 'make-mode
                           '(nil)
                           'nil)
  (defvar use-package--warning79
    (function
     (lambda
       (keyword err)
       (let
           ((msg
             (format "%s/%s: %s" 'make-mode keyword
                     (error-message-string err))))
         (display-warning 'use-package msg :error)))))
  (condition-case-unless-debug err
      (if
          (not
           (require 'make-mode nil t))
          (display-warning 'use-package
                           (format "Cannot load %s" 'make-mode)
                           :error))
    (error
     (funcall use-package--warning79 :catch err))))

Cmake

(use-package cmake-mode
  :mode ("CMakeLists\\.txt\\'" "\\.cmake\(.in\)?\\'")
  :config
  (add-to-list 'company-backends 'company-cmake))

(use-package eldoc-cmake
  :after company
  :hook (cmake-mode . eldoc-cmake-enable))

(use-package cmake-font-lock
  :straight t
  :preface
  (defun my-cmake-font-lock ()
    (let ((auto-refresh-defaults (boundp 'font-lock-keywords)))
      (cmake-font-lock-activate)
      (when auto-refresh-defaults
        (font-lock-refresh-defaults))))
  :init
  (add-hook 'cmake-mode-hook #'my-cmake-font-lock))

Cuda

(use-package cuda-mode
  :mode "\\.cu\\'")

Python

(use-package python
  :straight nil
  :config
  (setq python-shell-interpreter "jupyter"
        python-shell-interpreter-args "console --simple-prompt"
        python-shell-prompt-detect-failure-warning nil)
  (add-to-list 'python-shell-completion-native-disabled-interpreters "jupyter"))

(use-package ein
  :straight t)

(use-package pyvenv
  :straight t
  :demand t
  :config
  (setq pyvenv-workon "emacs")
  (pyvenv-tracking-mode 1))

(use-package py-autopep8
  :straight t
  :hook (python-mode . py-autopep8-enable-on-save))

Additional support for Jupyter notebooks:

(use-package jupyter
  :straight t)

Julia

(use-package julia-mode
  :mode "\\.jl\\'")

(use-package flycheck-julia
  :hook (julia-mode . flycheck-julia-setup))

Haskell

(use-package haskell-mode
  :straight t
  :custom
  (haskell-process-load-or-reload-prompt t)
  (haskell-process-auto-import-loaded-modules t)
  (haskell-process-log t)
  (haskell-tags-on-save t))

(use-package flycheck-haskell
  :straight t
  :config
  (setq-default flycheck-disabled-checkers '(haskell-stack-ghc))
  (add-hook 'haskell-mode-hook #'flycheck-haskell-setup))

Perl

(use-package cperl-mode
  :straight nil
  :mode ("\\.\\([pP][Llm]\\|al\\)\\'" . cperl-mode))

Raku

(use-package raku-mode
  :straight t
  :defer t
  :mode "\\.raku\\'")

Lua

(use-package lua-mode
  :straight t)

Org-mode

Org-mode is the package allowing me to have this literate configuration. Org-superstar makes org-mode files look gorgeous.

(use-package org
  :straight t
  :init
  (defun display-ansi-colors ()
    "Fixes kernel output in emacs-jupyter"
    (ansi-color-apply-on-region (point-min) (point-max)))
  :hook
  (org-mode . (lambda () (progn
                           (add-hook 'after-save-hook #'org-babel-tangle :append :local)
                           (add-hook 'org-babel-after-execute-hook #'display-ansi-colors))))
  :config
  (require 'ox-beamer)
  (require 'ol-bibtex)
  (add-to-list 'org-modules 'org-tempo)
  (org-babel-do-load-languages
   'org-babel-load-languages '((fortran . t)
                               (julia . t)
                               (python . t)
                               (jupyter . t)
                               (scheme . t)
                               (haskell . t)
                               (lisp . t)
                               (emacs-lisp . t)
                               (clojure . t)
                               (C . t)
                               (org . t)
                               (gnuplot . t)
                               (awk . t)
                               (latex . t)
                               (shell . t)))
  (setq org-babel-clojure-backend 'cider)
  (setq org-latex-pdf-process '("latexmk -shell-escape -pdf -outdir=%o %f"))
  (setq org-preview-latex-default-process 'imagemagick)
  (setq org-file-apps '(("\\.pdf\\'" . "evince %s")))
  (setq org-startup-indented t)
  (setq org-latex-listings 'minted
        org-latex-packages-alist '(("" "minted")))
  (setq org-startup-with-inline-images t)
  (setq org-todo-keywords
        '((sequence "TODO(t)" "BROKEN(b)" "RUNNING(r)" "VERIFY(v)" "URGENT(u)" "PARTIAL(p)"
                    "|" "DONE(d)" "OPTIONAL(o)" "DELEGATED(e)" "IRRELEVANT(i)")))
  (setq org-todo-keyword-faces
        '(("BROKEN" . "red") ("RUNNING" . "yellow")
          ("VERIFY" . "light goldenrod") ("URGENT" . "orange") ("PARTIAL" . "burlywood")
          ("OPTIONAL" . "green") ("IRRELEVANT" . "LightBlue1")
          ("DELEGATED" . "aquamarine3")))
  (plist-put org-format-latex-options :scale 3.0)
  (add-hook 'org-babel-after-execute-hook 'org-redisplay-inline-images))

(use-package org-superstar
  :straight t
  :config
  (add-hook 'org-mode-hook (lambda () (org-superstar-mode 1))))

Some extra exporting formats:

(use-package ox-reveal
  :straight t
  :after org
  :custom
  (org-reveal-root "file:///home/loren/bin/reveal.js")
  :config
  (setq org-reveal-mathjax t))

(use-package ox-hugo
  :straight t
  :after org)

(use-package ox-ipynb
  :straight (:host github :repo "jkitchin/ox-ipynb")
  :after org)

And by the way, convert everything else to org-mode please:

(use-package org-pandoc-import
  :straight (:host github
             :repo "tecosaur/org-pandoc-import"
             :files ("*.el" "filters" "preprocessors")))

The current Geiser implementation does not play well with org-babel, so I have decided to use ob-racket for evaluating the Scheme code blocks:

(use-package ob-racket
  :after org
  :config
  (add-hook 'ob-racket-pre-runtime-library-load-hook
            #'ob-racket-raco-make-runtime-library)
  :straight (ob-racket
             :type git :host github :repo "hasu/emacs-ob-racket"
             :files ("*.el" "*.rkt")))

Finally, I set up my personal knowledge management system with org-roam. This package leverages the power of org-mode to create knowledge networks using Niklas Luhmann's Zettelkasten method.

(use-package org-roam
  :straight t
  :custom
  (org-roam-directory (file-truename "~/Documents/Arbeit/WIKI"))
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n g" . org-roam-graph)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n c" . org-roam-capture)
         ("C-c n j" . org-roam-dailies-capture-today))
  :config
  (setq org-roam-node-display-template
        (concat "${title:*} "
                (propertize "${tags:10}" 'face 'org-tag)))
  (org-roam-db-autosync-mode)
  (require 'org-roam-protocol))

(use-package org-roam-ui
  :straight (:host github
             :repo "org-roam/org-roam-ui"
             :branch "main"
             :files ("*.el" "out"))
    :after org-roam
    :config
    (setq org-roam-ui-sync-theme t
          org-roam-ui-follow t
          org-roam-ui-update-on-save t
          org-roam-ui-open-on-start t))

Latex

Auctex provides all you will ever need to work with Latex, and more.

(use-package auctex
  :straight t
  :mode ("\\.tex\\'" . latex-mode)
  :commands (latex-mode LaTeX-mode plain-tex-mode)
  :init
  (progn
    (add-hook 'LaTeX-mode-hook #'LaTeX-preview-setup)
    (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)
    (add-hook 'LaTeX-mode-hook #'flyspell-mode)
    (add-hook 'LaTeX-mode-hook #'turn-on-reftex)
    (setq TeX-auto-save t
          TeX-parse-self t
          TeX-save-query nil
          TeX-PDF-mode t)))

Web

(use-package php-mode
  :mode ("\\.php\\'"))

(use-package web-mode
  :straight t
  :defer t
  :mode ("\\.html\\'" "\\.htm\\'" "\\.css\\'")
  :bind (("C-c C-v" . browse-url-of-buffer))
  :init
  (defun webmd-hooks-mine ()
    "Some hooks for web mode."
    (setq web-mode-markup-indent-offset 2)
    (setq web-mode-css-indent-offset 2)
    (setq web-mode-code-indent-offset 2)
    (setq web-mode-enable-current-column-highlight t)
    (setq web-mode-enable-current-element-highlight t)
    (set-face-attribute 'web-mode-html-tag-bracket-face nil :foreground "red"))

  (defun web-mode-flyspell-verify ()
    "Make flyspell behave correctly in web mode."
    (let ((f (get-text-property (- (point) 1) 'face)))
      (not (memq f '(web-mode-html-attr-value-face
                     web-mode-html-tag-face
                     web-mode-html-attr-name-face
                     web-mode-doctype-face
                     web-mode-keyword-face
                     web-mode-function-name-face
                     web-mode-variable-name-face
                     web-mode-css-property-name-face
                     web-mode-css-selector-face
                     web-mode-css-color-face
                     web-mode-type-face)
                 ))))
  (put 'web-mode 'flyspell-mode-predicate 'web-mode-flyspell-verify)
  :hook
  ((web-mode . company-mode)
   (web-mode . emmet-mode)
   (web-mode . (lambda () (flyspell-mode 1)))
   (web-mode . webmd-hooks-mine)))

(use-package emmet-mode
  :straight t)

(use-package js-mode :straight nil
  :mode ("\\.js\\'"))

(use-package simple-httpd
  :straight t)

YAML

(use-package yaml-mode
  :straight t
  :mode "\.ya?ml\'")

Markdown

(use-package markdown-mode
  :straight t
  :init
  (setq-default markdown-hide-markup t))

reStructuredText

(use-package rst
  :straight nil
  :mode ("\\.rst\\'" . rst-mode)
  :bind (:map rst-mode-map
              ("M-a" . rst-backward-section)
              ("M-e" . rst-forward-section))
  :init
  (setq rst-indent-width 2))

AsciiDoc

(use-package adoc-mode
  :straight t)

JSON

(use-package json-mode
  :mode "\\.json\\'")

(use-package flymake-json
  :hook (json-mode . flymake-json-load))

Git

A porcelain as glorious as the one from Mei├čen:

(use-package magit
  :straight t)

(use-package git-modes
  :straight t
  :mode (("\\.gitattributes\\'" . gitattributes-mode)
         ("\\.gitconfig\\'" . gitconfig-mode)
         ("\\.gitignore\\'" . gitignore-mode)))

GitLab CI

This package comes very handy when writing GitLab pipelines:

(use-package gitlab-ci-mode
  :straight t
  :mode
  ("\\.gitlab-ci.yaml\\'" "\\.gitlab-ci.yml\\'"))

(use-package gitlab-ci-mode-flycheck
  :straight t
  :after flycheck gitlab-ci-mode
  :init
  (gitlab-ci-mode-flycheck-enable))

The heretic Vimscript

(use-package vimrc-mode
  :straight t)

Nix expressions language

Syntax highlighting for Nix expression's language files

(use-package nix-mode
  :straight t
  :mode "\\.nix\\'")

Footer of the Emacs package

Thank you for reading 'till the end or for being interested on how to end an Emacs package. If you would like to try my configuration, feel free to fork it on GitHub. So that's it, let's gracefully finish tangling everything:

(provide 'init.el)
;;; init.el ends here

Author: Panadestein