Emacs literate configuration
Table of Contents
- Prelude: Emacs configuration and the world of Lisp
- General configuration
- Setting up a package manager
- Dirty hacks
- One editor to rule them all, one editor to find them
- Safeguarding configuration file from unwanted
custom
interference - Precision line highlighting
- Old habits die hard
- Streamlining the undo system
- Themes
- Handle very long lines
- Improved parentheses
- Highlight parentheses with different color
- Minimap
- An elegant mode line
- Multiple cursors support
- Tabs-bar-mode
- Enhanced HTML exports
- IDE features and documentation
- Incremental search and completion with Ivy
- Jump anywhere in Emacs with avy
- Crafting command combos and menus with Hydra
- Exploring command options with which-key
- Enhanced help systems with Helpful
- Code tags
- Tree-sitter
- Completion with company
- Language server protocol
- Debuggers
- Spell checking with FlySpell
- Grammar checking with LanguageTool
- Translator
- Syntax checking with Flycheck
- Snippets
- Better ediff configuration
- ChatGPT
- File browsing, terminal emulators, and web browsing
- Programming
- Lisp
- Iversonian array languages
- Theorem provers
- My Emacs Lisp packages
- Fortran
- IRPF90
- C and C++
- Rust
- Makefile
- Cmake
- Cuda
- Python
- Julia
- Computer algebra systems
- Graph visualization
- Alternative shells
- Haskell
- Perl
- Raku
- Lua
- Org-mode
- LaTeX
- Web
- YAML
- Markdown
- reStructuredText
- AsciiDoc
- JSON
- Git
- GitLab CI
- The heretic Vimscript
- Nix expressions language
- GNU Guix package manager
- Footer of the Emacs package
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 workingemacs-jupyter
's native compilation is currently not workingcider
is disabledmagit
needs to be loaded aftertransient
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 matchC-p
make and go to previous matchg 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 tabC-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")) :disabled t :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\\'")
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 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 (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\\'")
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