Emacs literate configuration

Table of Contents

Prelude: Emacs configuration and the world of Lisp

Welcome to my literate Emacs configuration. This setup, like many of its lineage, validates Newton's famous aphorism about standing on the shoulders of giants. A heartful nod to all the unsung heroes whose Emacs Lisp snippets form the backbone of this file. Speaking of Lisp, I have recently tasted the enlightenment frequently prophesied in many a hacker's lore. Here is one of my favorites, penned by RMS himself:

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

Leveraging these options, I tangle (each time I save the file, refer to the org-mode hook) the source code blocks in this configuration to my Emacs init.el, while also setting up the ReadTheOrg theme utilized by the website. One nuance is the task of ensuring syntax highlighting when exporting to HTML with org-mode while using Emacs in batch mode. For a practical insight into how I accomplish this, feel free to examine my build-site.el script in the source repository.

Deployment with Nix

While it's perfectly feasible to configure Emacs with Nix, my preference leans towards the Elisp configuration. Nonetheless, I've incorporated the following overlay in conjunction with Cachix to access the latest commits from 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. Previously, 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 facilitate this without abandoning package.el, I opted for the approach ensuring reproducibility: straight.el. This purely functional package manager brings me into closer alignment with the lofty principles championed by 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))

Dirty hacks

The typical caveat of using bleeding-edge software is the occasional need for manual fixes. Here, I introduce (hopefully) temporary workarounds to address issues in broken packages.

;; All sorts of nasty things happen if wrong Org version
(straight-use-package 'org)

;; Nix-mode needs this package
(use-package reformatter
  :straight t)

;; A bunch of packages need it, including magit
(use-package compat
  :straight (:host github
             :repo "emacs-compat/compat"))

Other workarounds:

  • which-key's native compilation is currently not working
  • emacs-jupyter's native compilation is currently not working
  • cider is disabled
  • magit needs to be loaded after transient

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 . "Iosevka-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)))
  ;; Increase memory
  (setq gc-cons-threshold 100000000)
  (setq read-process-output-max (* 1024 1024))
  :bind
  (;; Make ESC close prompts
   ("<escape>" . keyboard-escape-quit)
   ("C-c C-r" . revert-buffer-no-confirm)))

Safeguarding configuration file from unwanted custom interference

This piece of code serves to reduce clutter and boost the reproducibility of the configuration.

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

Precision line highlighting

This code ensures line highlighting is applied selectively, focusing solely on 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

Discarding years of honed Vim muscle memory is not an option for me. Yet, I'm beginning to find that Emacs keybindings are starting to resonate, and in some major modes, employing Evil just doesn't make any sense.

(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))
        '(bqn-help--mode
        bqn-glyph-mode
        bqn-keymap-mode
        dired-mode
        eldoc-mode
          elfeed-mode
          eshell-mode
          eww-mode
          helpful-mode
          pdf-view-mode
          profiler-report-mode
          realgud-mode
          vterm-mode)))

Streamlining the undo system

Embracing Emacs comes with its quirks, and the default undo system stands out as one that puzzles me. Perhaps one day, I'll unlock its full potential. For the time being, I opt for a linear approach.

(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 emulates the minimap feature found in Sublime Text within the realm of Emacs. Although I'm moderately content with this configuration, I haven't fully embraced its regular usage just yet.

(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))

An elegant mode line

A precious contribution from the Doom Emacs community, boasting the finest mode line I've encountered for Emacs. The supplementary package all-the-icons augments this experience, offering an array of splendid icons for the mode line, file managers, and numerous 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 intriguing package offers functionality for handling multiple cursors, although its ergonomics could, in my opinion, use some improvement. Here are some of the most significant 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

Tabs are not traditionally a part of the standard Emacs workflow, preferring buffer-switching instead. The following is a simple configuration for incorporating tabs, included merely for the sake of completeness:

  • 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)))

Enhanced HTML exports

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

(use-package htmlize
  :straight t)

IDE features and documentation

Incremental search and completion with Ivy

The integration of Ivy and its auxiliary tools, Swiper and Counsel, greatly enhances my Emacs experience by providing an advanced incremental completion system. They fit seamlessly with my Doom Emacs setup, facilitating a smooth workflow. Swiper's incremental buffer search functionality, invoked with C-s, outperforms the standard Emacs search.

(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)))

Crafting command combos and menus with Hydra

Hydra is a package that simplifies the creation of keybinding combinations using a shared prefix. As a bonus, it also generates a menu displaying all potential 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))

Exploring command options with which-key

Which-key is an indispensable tool for charting the turbulent waters of Emacs' packages keybindings. These suggestions prove incredibly useful when venturing into the realm of a new package.

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

Enhanced help systems with Helpful

While the default Emacs help system is commendable, Helpful elevates it to another level, introducing features such as syntax highlighting and source code exploration for a more intuitive and enriching experience.

(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

When navigating through large codebases, such as electronic structure packages, employing code tags can save your fingers from the wear and tear induced by continuous grep commands. I leverage the minimalist gtags-mode that maximizes the use of existing Emacs tools. Additionally, I've set up the counsel-etags package, although its method of parsing the TAGS file tends to be sluggish when used in conjunction 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))

Tree-sitter

Emacs package to use the incremental parsing library tree-sitter.

(use-package tree-sitter
  :straight t)

(use-package tree-sitter-langs
  :straight t
  :after tree-sitter)

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-headerline-breadcrumb-enable nil
        lsp-enable-snippet nil)
  :hook ((python-mode . my-lsp-hook)
         (f90-mode . my-lsp-hook)
         (haskell-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-enable nil
        lsp-ui-doc-header nil
        lsp-ui-doc-delay 0.5
        lsp-ui-doc-position 'bottom
        lsp-ui-doc-alignment 'frame
        lsp-ui-doc-include-signature t
        lsp-ui-doc-use-childframe t)
  :commands lsp-ui-mode)

(use-package lsp-treemacs
  :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)

Debuggers

The realgut debugger is a complete rewriting of the Emacs GUD. It has support for several debuggers and has seamless integration with GDB and PDB.

(use-package realgud
  :straight t)

For an experience closer to the mainstream IDEs one can use dap-mode, which uses the Debug Adapter Protocol:

(use-package dap-mode
  :straight 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))

Also, available as a language server:

(use-package lsp-ltex
  :straight t
  :commands lsp-ltex-enable
  :init
  (setq lsp-ltex-version "15.2.0")
  (defun lsp-ltex-enable ()
    "Enable lsp-ltex."
    (require 'lsp-ltex)
    (lsp)))

Translator

This package provides a potent translation tool integrated seamlessly with Emacs. It extends support to numerous translation engines including Google, Bing, and DeepL. My preference leans towards using pop-ups for translations, necessitating the additional installation of the posframe package. The translation directions can be easily adjusted in the prompt using 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 indeed a valuable asset in the toolkit, despite certain constraints. For instance, the persistent checking might negatively affect performance, and certain checkers can impose severe overheads - this led me to disable mypy. In my current configuration, checks are performed exclusively during saves for a balanced performance.

(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 offers a comprehensive collection of snippets complemented by an incredibly intuitive syntax. Its notable shortcoming, however, is the conspicuous absence of Scheme snippets. I've crafted custom snippets for the IRPF90 derived mode, which are also included here.

(use-package yasnippet
  :straight t
  :config
  ;; Enable everywhere
  (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))

ChatGPT

For the times they are a-changin'. Large language models are here to stay, and as cliche as it might sound, they are the future. It comes then natural to have a nice Emacs package to query ChatGPT.

(use-package chatgpt-shell
  :straight (:host github
             :repo "xenodium/chatgpt-shell"
             :files ("shell-maker.el" "chatgpt-shell.el" "ob-chatgpt-shell.el"))
  :config
  (require 'shell-maker)
  (require 'ob-chatgpt-shell)
  :custom
  (chatgpt-shell-openai-key (auth-source-pick-first-password :host "api.openai.com")))

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: Remote file editing made easy

TRAMP (Transparent Remote Access, Multiple Protocols) is an essential package in Emacs that simplifies remote file editing and management. Paired with Dired, TRAMP enables seamless editing of files on remote machines, saving you from the hassle of configuring your editor on obscure supercomputers around the world. With TRAMP, you can open, save, and edit files on remote machines as if they were local files. TRAMP supports various protocols, such as SSH, FTP, and SFTP, allowing you to work with remote files using familiar Emacs commands and keybindings.

(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
  :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
  (setq dired-kill-when-opening-new-dired-buffer t)
  (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)
         (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, and scribble!

(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
  :mode ("\\.rkt\\'" "\\.scrbl\\'")
  :hook ((racket-mode . racket-xp-mode)))

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
  :disabled 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)))

Iversonian array languages

I began programming with Fortran, a common initiation for many physicists. The rise of NumPy and SciPy eventually drew me to the Python world. As my interest in Python waned, I turned to C++, but was quickly dissuaded by its aesthetic limitations, which lead me to the beauty of Lisp and Haskell. Despite some criticisms of these languages, the core features I value in them have their roots in APL (A Programmin Language). Programming in Iversonian array languages is a delightful experience.

BQN

An APL for your flying saucer, and the language I am more interested in at the moment:

(use-package bqn-mode
  :straight t
  :init
  ;; Rudimentary org-babel support
  (add-to-list 'org-babel-tangle-lang-exts '("bqn" . "bqn"))
  (defun org-babel-expand-body:bqn (body params &optional processed-params)
    "Expand BODY according to PARAMS, return the expanded body."
    (require 'bqn-mode nil t)
    (let ((vars (org-babel--get-vars (or
                                    processed-params
                                    (org-babel-process-params params)))))
      (concat
       (mapconcat
      (lambda (pair)
        (format "%s=%S" (car pair) (org-babel-bqn-var-to-bqn (cdr pair))))
      vars "\n")
       "\n" body "\n")))
  (defun org-babel-execute:bqn (body params)
    "Execute a block of BQN code with org-babel."
    (message "Executing BQN source code block")
    (bqn-comint-evaluate-command body))
  (defun org-babel-bqn-var-to-bqn (var)
    "Convert an elisp var into a string of BQN."
    (format "%S" var))
  (defun org-babel-bqn-initiate-session (&optional session)
    "If there is not a current inferior-process-buffer in SESSION then create."
    (unless (string= session "none")
      (get-buffer-process (bqn-comint-buffer))))

  ;; Some utilities
  (defun toggle-bqn-keymap ()
    "Toggle the BQN keymap display."
    (interactive)
    (let ((buf (get-buffer "*BQN keymap*")))
      (if (and buf (get-buffer-window buf))
          (progn
            (delete-window (get-buffer-window buf))
            (kill-buffer buf))
      (bqn-keymap-mode-show-keyboard))))
  (defun toggle-bqn-glyphs ()
    "Toggle the BQN glyphs display."
    (interactive)
    (let ((buf (get-buffer "*BQN Glyphs*")))
      (if (and buf (get-buffer-window buf))
          (progn
            (delete-window (get-buffer-window buf))
            (kill-buffer buf))
      (bqn-glyph-mode-show-glyphs))))
  (defun bqn-explain-line ()
    "Explain the current line in BQN, displaying the explanation result in the minibuffer."
    (interactive)
    ;; Capture the current line's content
    (let* ((current-line (thing-at-point 'line t))
           (command-to-explain (concat ")explain " current-line))
         (proc (get-buffer-process (bqn-comint-buffer))))
      ;; Use bqn-comint-evaluate-command to send the command
      (message "%s" (bqn--comint-call-process-silently proc command-to-explain))))

  ;; Too much color in BQN is not good
  (defun disable-rainbow-delimiters-bqn ()
    (rainbow-delimiters-mode -1))

  ;; Modify the default fontlock
  (defface bqn-function
    '((t (:foreground "#3aa548")))
    "Face used for BQN functions."
    :group 'bqn)
  (defface bqn-one-modifier
    '((t (:foreground "#93428b")))
    "Face used for BQN 1-modifiers."
    :group 'bqn)
  (defface bqn-two-modifier
    '((t (:foreground "#b7a31e")))
    "Face used for BQN 2-modifiers."
    :group 'bqn)
  (defface bqn-primitive-function
    '((t (:foreground "#3aa548")))
    "Face used for primitive BQN functions."
    :group 'bqn)
  (defface bqn-primitive-one-modifier
    '((t (:foreground "#93428b")))
    "Face used for primitive BQN 1-modifiers."
    :group 'bqn)
  (defface bqn-primitive-two-modifier
    '((t (:foreground "#b7a31e")))
    "Face used for primitive BQN 2-modifiers."
    :group 'bqn)
  (defun my-bqn-mode-custom-faces ()
    "Customize string face for BQN mode."
    (set (make-local-variable 'font-lock-string-face)
       '(:foreground "#ABB2BF"))
    (font-lock-add-keywords nil
                            '(("\\({\\|}\\)"
                             0 'font-lock-type-face)))
    (font-lock-add-keywords nil
                            '(("\\(\\[\\|\\]\\|\\|\\)"
                               0 '(face (:foreground "cyan"))))))

  ;; Propagate fontlock to helper modes
  (defun my-bqn-keymap-mode-setup ()
    "Set up bqn-keymap-mode specific configurations."
    ;; Set up font locking to highlight BQN glyphs in bqn-keymap-mode using bqn-mode's rules
    (setq-local font-lock-defaults bqn--font-lock-defaults)
    (font-lock-add-keywords nil '(("\\(\"\\)" 1 'default t)))
    (font-lock-add-keywords nil '(("[-┌┬┐├┼┤└┴┘─│]+" . 'default)))
    (font-lock-flush (point-min) (point-max))
    ;; We don't need strings in this mode
    (modify-syntax-entry ?\" "." (syntax-table)))

  :bind (:map bqn-mode-map
            ("M-i" . bqn-help-symbol-info-at-point)
            ("C-c g" . toggle-bqn-glyphs)
            ("C-c k" . toggle-bqn-keymap)
            ("C-c C-e" . bqn-comint-eval-buffer)
            ("C-x e" . bqn-explain-line)
            ("C-x C-e" . bqn-comint-eval-dwim))
  :hook ((bqn-mode . bqn-comint-buffer)
       (bqn-mode . disable-rainbow-delimiters-bqn)
       (bqn-mode . my-bqn-mode-custom-faces)
       (bqn-mode . (lambda ()
                       (set (make-local-variable 'company-backends)
                            '((company-yasnippet company-capf company-keywords)))))
       (bqn-keymap-mode . my-bqn-keymap-mode-setup))
  :config
  ;; Glyphs and keyboard layout
  (require 'bqn-keymap-mode)
  (require 'bqn-glyph-mode)
  ;; Get rid of *Quail Completions*
  (defun quail-completion ()))

Uiua

Point free or die. Stack based, array oriented, general purpose programming language.

(use-package uiua-ts-mode
  :straight t
  :mode "\\.ua\\'")  

My Emacs Lisp packages

Here I plan to add small packages I write from time to time, mainly for non-trivial editing tasks that I stumble against at work.

;;; parse-greenx.el --- Utilities to refactor GreenX library

;; Copyright (C) Dr. Ramon L. Panades Barrueta

;; Author: Dr. Ramon L. Panades Barrueta
;; Keywords: parsing, refactoring

;;; Commentary:

;; This module provides some procedures to manipulate the GreenX
;; hard-coded arrays.

;;; Code:

(defun prune-chars-greenx (beginning end)
  "Remove undesired characters in active region from BEGINNING to END."
  (when (use-region-p)
    (mapcar (lambda (spurious-string)
              (replace-string-in-region spurious-string
                                        " "
                                        beginning
                                        end))
            '("," "&"))))

(defun insert-matrix-cols-greenx (matrix-as-list len col-count)
  "Print COL-COUNT columns of length LEN for MATRIX-AS-LIST."
  (when matrix-as-list
    (let ((col-as-string
           (vconcat
            (mapcar
             (lambda (the-numbers)
               (format "%s, " the-numbers))
             (butlast matrix-as-list
                      (- (length matrix-as-list) (- len 1))))
            (list (format "%s" (nth (- len 1) matrix-as-list))))))
      (setq col-count (1+ col-count))
      (insert "\n"
              (format "aw%%aw_erange_matrix(:, %d) = %s" col-count col-as-string)))
    (insert-matrix-cols-greenx (nthcdr len matrix-as-list) len col-count)))

;;;###autoload
(defun matrix-to-cols-greenx (start end len)
  "Print columns of array of shape (-1, LEN) in active region START END."
  (interactive "r\nP")
  (prune-chars-greenx start end)
  (unwind-protect
      (let ((mstart (make-marker))
            (mend (make-marker)))
        (set-marker mstart start)
        (set-marker mend end)
        (goto-char start)
        (let* ((text (buffer-substring start end))
               (elems-matrix
                (mapcar
                 (lambda (srow)
                   (car (read-from-string srow)))
                 (split-string text nil t))))
          (goto-char end)
          (insert-matrix-cols-greenx elems-matrix len 0)
          (delete-region mstart mend)))
    (widen)))

(provide 'parse-greenx)
;;; parse-greenx.el ends here

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
  :defer t
  :hook ((c-mode c++-mode objc-mode cuda-mode) . lsp)
  :commands lsp)

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
  :commands python-mode
  :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
  :init
  (setq pyvenv-workon "emacs")
  (pyvenv-tracking-mode 1))

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

(use-package lsp-pyright
  :straight t
  :hook (python-mode . (lambda ()
                         (require 'lsp-pyright)
                         (lsp)))
  :config
  (setq lsp-pyright-auto-import-completions t
        lsp-pyright-typechecking-mode "basic"))

I also heavily use the emacs-jupyter package to communicate with Jupyter kernels.

(use-package jupyter
  :straight (:build (:not native-compile))
  :config
    (setq jupyter-use-zmq t))

Julia

(use-package julia-mode
  :straight t
  :mode "\\.jl\\'"
  :hook (julia-mode . lsp-mode))

(use-package lsp-julia
  :straight t
  :custom
  (lsp-julia-default-environment "~/.julia/environments/v1.9"))

(use-package julia-vterm
  :straight t
  :hook (julia-mode . julia-vterm-mode)
  :custom
  (julia-vterm-repl-program "julia -t 4"))

Computer algebra systems

Maxima

Maxima is a CAS written in Common Lisp that has been around for quite some time. It provides capabilities for symbolic and numerical computations. The following package enhances the default Emacs experience with Maxima.

(use-package maxima
  :straight t
  :init
  (setq org-format-latex-options (plist-put org-format-latex-options :scale 2.0)
        maxima-display-maxima-buffer nil)
  (add-to-list 'auto-mode-alist
               (cons "\\.mac\\'" 'maxima-mode))
  (add-to-list 'interpreter-mode-alist
               (cons "maxima" 'maxima-mode)))

SageMath

SageMath is a comprehensive alternative to Mathematica, occasionally surpassing it in performance. However, it's written in Python, which lacks the advanced functional programming capabilities that I generally prefer in CASs.

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

(use-package ob-sagemath
  :straight t)

Graph visualization

This package provides support for Graphviz's dot command line tool.

(use-package graphviz-dot-mode
  :straight t
  :config
  (setq graphviz-dot-indent-width 4))

Alternative shells

Nushell

This shell draws a lot of inspiration from functional languages:

(use-package nushell-mode
  :straight t)

Xonsh

The best of Python and Bash, in a single shell:

(use-package xonsh-mode
  :straight t)

Haskell

(use-package haskell-mode
  :straight t
  :init
  (setq flymake-allowed-file-name-masks nil)
  :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 lsp-haskell
  :straight t)

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 '((awk . t)
                               (C . t)
                               (clojure . t)
                               (dot . t)
                               (emacs-lisp . t)
                               (eshell . t)
                               (fortran . t)
                               (gnuplot . t)
                               (haskell . t)
                               (julia . t)
                               (latex . t)
                               (lisp . t)
                               (maxima . t)
                               (org . t)
                               (python . t)
                               (scheme . t)
                               (shell . t)
                               (jupyter . 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-src-window-setup 'current-window)
  (setq org-startup-indented t)
  (setq org-highlight-latex-and-related '(native))
  (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")
  :config (advice-remove 'org-display-inline-images 'font-lock-fontify-buffer)
  :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
  :init
  (use-package emacsql-sqlite-builtin)
  (setq org-roam-database-connector 'sqlite-builtin)
  :custom
  (org-roam-directory (file-truename "~/Documents/Arbeit/WIKI/notes"))
  :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
  :hook ((LaTeX-mode . LaTeX-preview-setup)
         (LaTeX-mode . LaTeX-math-mode)
         (LaTeX-mode . flyspell-mode)
         (LaTeX-mode . turn-on-reftex))
  :mode ("\\.tex\\'" . latex-mode)
  :config
  (setq TeX-auto-save t
        TeX-parse-self t
        TeX-save-query nil
        TeX-PDF-mode t
        TeX-source-correlate-mode t
        TeX-source-correlate-method 'auto
        TeX-source-correlate-start-server 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 transient
  :straight t)

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

(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\\'")

GNU Guix package manager

Guix is a functional package manager inspired by Nix, but using Guile scheme instead of the Nix expression's language. This package offers an Emacs interface to it.

(use-package guix
  :straight t)

Footer of the Emacs package

You've journeyed to the finale or perhaps, you've taken a particular interest in the art of closing an Emacs package. If my configuration intrigues you and you'd like to experiment with it, I warmly invite you to fork it on GitHub. Your support and feedback will surely fuel its continuous development. Thank you for your time and interest! So, without further ado, let's elegantly wrap up the tangling process:

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

Author: Panadestein