Emacs literate configuration
Prelude
Welcome to my literate Emacs configuration. Like many of its kind, it illustrates Newton's famous aphorism about standing on the shoulders of giants. A heartfelt nod goes to the unsung heroes whose Emacs Lisp snippets form the backbone of this file. Speaking of Lisp, I have also had a taste of the enlightenment promised in hacker lore. Here is one of my favorite examples, 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.
Literate programming with Org mode
The beauty of a literate configuration is that it lets me maintain an Emacs configuration, a website, and an Org document at the same time. For example, the header of this file contains the following code:
#+TITLE: Emacs literate configuration #+PROPERTY: header-args :tangle ../init.el #+OPTIONS: toc:2 num:nil #+HTML_HEAD_EXTRA: <link rel="stylesheet" href="site.css">
With these options, I tangle the source blocks in this configuration to my Emacs init.el
whenever I save the file (see the Org hook), while also styling
the exported page with a small stylesheet stored alongside the Org source. The
build-site.el script in the source repository uses the
Org exporter bundled with Emacs and a pinned local copy of htmlize for syntax
highlighting, keeping local and automated builds reproducible.
Deployment with Nix
Although it is perfectly feasible to configure Emacs with Nix, I prefer a hand-written Emacs Lisp configuration. I use the following overlay to track 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 deploy my Emacs configuration automatically 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 its package manager. I was mostly happy using
use-package with the package.el backend, but sometimes needed newer versions of packages such as
org-mode. Although tools such as Quelpa make this possible without abandoning package.el, I chose
the more reproducible approach offered by 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))
To use straight.el properly, I add the following code to early-init.el:
;;; early-init.el --- Emacs early init file -*- lexical-binding: t; -*- ;;; 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
It also integrates neatly 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, here is how to configure use-package with package.el: first add the GNU ELPA
and MELPA archives, 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))
Temporary workarounds
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 workingemacs-jupyter's native compilation is currently not workingcideris disabledmagitneeds to be loaded aftertransient
One editor to rule them all, one editor to find them
Here I use the use-package macro to keep Emacs' built-in settings organized in a 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 ";; The purpose of computing is insight, not numbers." 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)))
Protecting the configuration from unwanted custom changes
This code reduces clutter and makes the configuration more reproducible.
(let ((customization-file (expand-file-name "custom.el" user-emacs-directory))) (unless (file-exists-p customization-file) (with-temp-buffer (insert ";; -*- lexical-binding: t; -*-") (write-file customization-file))) (setq custom-file customization-file) (load custom-file 'noerror))
Precision line highlighting
This code applies line highlighting only in 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. Still, Emacs keybindings are beginning to feel natural, and in some major modes Evil simply does not make 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 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
Emacs has its quirks, and its default undo system still puzzles me. Perhaps one day I will unlock its full potential; for now, I prefer 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. That makes a visually pleasant editor important. These are my three favorite themes:
(use-package doom-themes :straight t :config ;; Fix fontlock bug (setcdr (assoc 'gnus-group-news-low-empty doom-themes-base-faces) '(:inherit 'gnus-group-mail-1-empty :weight 'normal)) ;; 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, highlighting numbers in source code:
(use-package highlight-numbers :straight t :hook (prog-mode . highlight-numbers-mode))
Handling very long lines
Very long lines are another well-known source of performance problems in Emacs, although they rarely affect me.
(use-package so-long :straight nil :hook (after-init-hook . global-so-long-mode))
Improved parentheses
This helps a lot when working with Lisp.
(use-package smartparens :straight t :hook (prog-mode . smartparens-mode))
Highlighting parentheses with different colors
This is mostly eye candy, but differently colored parentheses are genuinely helpful in Lisp.
(use-package rainbow-delimiters :straight t :hook (prog-mode . rainbow-delimiters-mode))
Minimap
This package brings Sublime Text's minimap feature to Emacs. I am reasonably happy with the configuration, although I do not use it regularly 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
This excellent contribution from the Doom Emacs community is the best Emacs mode line I have
encountered. The companion package all-the-icons provides icons for the mode line, 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 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-ncreates a cursor at the next match and moves to itC-pcreates a cursor at the previous match and moves to itg r qstops all cursors
(use-package evil-mc :straight t :config (global-evil-mc-mode 1))
Tab-bar mode
Tabs are not traditionally part of the standard Emacs workflow, which favors switching between buffers. For completeness, the following configuration adds a tab bar:
C-x t f "filename"opens a file in a new tabC-x t 0closes the current tab
(use-package tabbar :straight t :bind (("C-<right>" . tabbar-forward)))
Enhanced HTML exports
This package gives Org Babel exports proper syntax highlighting.
(use-package htmlize :straight t)
IDE features and documentation
Incremental search and completion with Ivy
Ivy and its companion tools, Swiper and Counsel, provide an effective incremental
completion system and fit smoothly into my 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 offers many ways to jump to visible text using a character-based
decision tree. My configuration is minimal because avy-goto-char-timer provides 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 navigating Emacs package keybindings. Its suggestions are especially useful when learning 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 large codebases such as electronic-structure packages, tags avoid repetitive
grep commands. I use the minimalist gtags-mode, which builds on existing Emacs tools.
I have also configured counsel-etags, 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
This package integrates the Tree-sitter incremental parsing library with Emacs.
(use-package tree-sitter :straight t) (use-package tree-sitter-langs :straight t :after tree-sitter)
Completion with company
Company is a classic completion framework for source code.
(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) brings IDE features to Emacs. I use two major
modes for managing LSP servers: lsp-mode and eglot. The latter makes greater use of
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 work over SSH. Here is 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))
In my experience, however, eglot better supports editing with TRAMP. There is little
configuration here because most of its defaults already suit my workflow.
(use-package eglot :straight t :defer t)
Debuggers
The realgud debugger is a complete reimplementation of Emacs' GUD. It supports several
debuggers and integrates seamlessly with GDB and PDB.
(use-package realgud :straight t :disabled)
For an experience closer to mainstream IDEs, you can use dap-mode and the Debug Adapter Protocol:
(use-package dap-mode :straight t)
Spell checking with Flyspell
Flyspell does a lot out of the box, so my configuration is minimal.
(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. It is indispensable when writing papers in LaTeX or Org. The Emacs package requires LanguageTool and its HTTP server, which can run as a systemd unit.
(use-package langtool :straight t :init (setq langtool-http-server-host "localhost" langtool-http-server-port 8081))
It is 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 integrates a powerful translation tool with Emacs and supports engines including
Google, Bing, and DeepL. I prefer displaying translations in pop-ups, which requires the
posframe package.
The translation directions can be easily adjusted in the prompt using C-n and C-p.
(use-package gt :straight t :disabled) (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.
To balance feedback and performance, my current configuration checks files only when they are saved.
(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, in my opinion.
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))
LLMs
The times they are a-changin'. Large language models are here to stay and, clichéd as it may sound, are likely to shape the future. It is therefore natural to have a convenient Emacs package for querying different models.
(use-package chatgpt-shell :straight (:host github :repo "xenodium/chatgpt-shell" :files ("shell-maker.el" "chatgpt-shell.el" "ob-chatgpt-shell.el")) :disabled t :config (require 'shell-maker) (require 'ob-chatgpt-shell) :custom (chatgpt-shell-openai-key (auth-source-pick-first-password :host "api.openai.com")))
I also add Copilot support, which is effective at generating boilerplate in some languages:
(use-package copilot :straight ( :host github :repo "copilot-emacs/copilot.el" :files ("*.el")) :bind (:map copilot-completion-map ("<tab>" . 'copilot-accept-completion) ("TAB" . 'copilot-accept-completion) ("C-TAB" . 'copilot-accept-completion-by-word) ("C-<tab>" . 'copilot-accept-completion-by-word)))
File browsing, terminal emulators, and web browsing
Terminal support with Vterm
This package uses a C library rather than Lisp to emulate a terminal. It is currently my favorite terminal emulator.
(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 particularly useful vterm feature is message passing, which makes it possible to execute
Emacs commands from a vterm buffer, including a remote one. It requires some configuration
in .bashrc. For example, the following opens remote files:
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 command shell is written entirely in Emacs Lisp. Although it is not POSIX-compliant,
its native TRAMP support makes it a powerful tool for remote editing. Here I define a custom
prompt resembling my Zsh prompt. Use clear 1 to get the same behavior as Bash's clear command.
(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 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 several file browsers, I realized that everything I needed was already available in 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, giving EWW an Org-like appearance. This is useful when comparing programming languages on 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 often spend time on IRC, so this configuration is useful both for getting help and for keeping in touch with friends.
(use-package ement :straight (:type git :host github :repo "alphapapa/ement.el"))
PDF support
This package uses another C library and works better than Emacs' default PDF viewer; for example, it supports 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 empv, which provides a more elaborate 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 describe it briefly because this section is rather opinionated.
Lisp
The language that gave birth to the "Maxwell's equations of software." Lisp dialects are my favorite programming languages; below is a brief description of what I value in them.
General
Lispy and its Evil companion, Lispyville, improve the overall Lisp experience in Emacs.
(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
A powerful compiler, excellent libraries, and strong object-oriented programming 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 in their purest form, plus hygienic macros. Racket also has excellent 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
The JVM's enormous library ecosystem, vectors and maps, and a 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 language on this list is similar in spirit to Clojure but compiles 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.
The macrostep package is very useful for this.
(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 led me to the beauty of Lisp and Haskell. Despite some criticisms of these languages, the core features I value in them have roots in APL (A Programming Language). Programming in Iversonian array languages is a delightful experience.
Dyalog APL
The tool of thought for software solutions.
(use-package dyalog-mode :straight t :init (defun disable-rainbow-delimiters-apl () (rainbow-delimiters-mode -1)) :hook (dyalog-mode . disable-rainbow-delimiters-bqn))
BQN
An APL for your flying saucer, and the language that interests me most at the moment. I am managing the package myself locally for now:
(use-package bqn-mode :straight (:type nil :local-repo "bqn-mode") :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)))) ;; Fix: disable quail only for BQN (defadvice quail-completion (around disable-for-bqn activate) (if (string= "BQN-Z" current-input-method) nil ad-do-it)) (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")))))) :bind (:map bqn-mode-map ("M-i" . bqn-help-symbol-info-at-point) ("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 . 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)))))))
Uiua
Point-free or die. A stack-based, array-oriented, general-purpose programming language.
(use-package uiua-ts-mode :straight t :mode "\\.ua\\'")
Theorem provers
For now, this is only Lean:
(use-package lean4-mode :straight (lean4-mode :type git :host github :repo "leanprover/lean4-mode" :files ("*.el" "data")) :commands (lean4-mode))
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 encounter 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 work as a physicist; this package provides everything 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 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 also be 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 jupyter-org-auto-connect nil))
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 :disabled :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 makes this literate configuration possible, while Org Superstar makes Org 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))) (add-to-list 'org-src-lang-modes '("jupyter-apl" . dyalog)) (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 additional export 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") :disabled t :config (advice-remove 'org-display-inline-images 'font-lock-fontify-buffer) :after org)
Add support for encrypted sections:
(use-package org-crypt :straight nil :after org :config (org-crypt-use-before-save-magic) (setq org-tags-exclude-from-inheritance (quote ("crypt"))) :custom (org-crypt-key "rpana92@gmail.com"))
And, while we are at it, convert everything else to Org:
(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 (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 everything you could need for working 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 is very useful 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 heretical Vim script
(use-package vimrc-mode :straight t)
Nix expression language
Syntax highlighting for Nix expression language files:
(use-package nix-mode :straight t :mode "\\.nix\\'")
Postlude
At last, the closing parenthesis. An Emacs configuration is never finished (only briefly free of obvious defects) so fork it, borrow whatever proves useful, and send back any improvement clever enough to survive your own init file. For now, however, Emacs expects a proper ending:
(provide 'init.el) ;;; init.el ends here