1
0
Fork
You've already forked me
0
Muupi Emacs - a light Emacs configuration for use on servers running Common Lisp systems.
2025年06月12日 03:54:09 -07:00
me.ef-dream.png add screenshot 2025年05月18日 19:42:40 -07:00
muupi1.jpg add pic of muupi 2025年05月24日 20:56:13 -07:00
README.org reword 2025年06月12日 03:54:09 -07:00

Muupi Emacs

/ggxx/me/media/branch/main/muupi1.jpg
Pirate Queen Muupi `M-x org-toggle-inline-images`

About

This is a light Emacs configuration for use on the dev server. The goal was to make Emacs a little more comfortable to use than vanilla Emacs, and have org-babel and sly setup for Common Lisp development.

Because this config is geared toward use in a server environment, there's no configuration for themes or fonts. If you want to use this in a graphical environment, I'd advise you to look at Prot's Basic Emacs Configuration that this config is based off of, and see how he does font and theme setup.

There is also an example at the end of how you can add your own customizations to this config for a graphical environment.

Installation

To install Muupi Emacs, open up a terminal and type the following.

# clone the git repository
git clone https://git.muupi.com/gg/me.git
# go into the cloned repository and view the README.org
cd me
emacs README.org

Then hit the following key combination inside Emacs.

  • M-x org-babel-tangle (or C-c C-v t).

Note that M-x means Alt-X and C-c means Ctrl-C in the notation that Emacs uses for keybindings.

To finish your installation, go back to your terminal and run me like this.

me
# If that didn't work, that means $HOME/.local/bin isn't in your PATH.
# Try this instead (and fix your PATH when you get a chance).
~/.local/bin/me

On the first run, Emacs will install some packages into ~/.config/me/. On subsequent runs, it should start up much more quickly.

Uninstallation

rm -rf ~/.config/me
rm ~/.local/bin/me

Usage

~/.local/bin/me

#!/bin/sh
emacs=${MUUPI_EMACS:-emacs}
exec $emacs --init-directory ~/.config/me $@

The me script is installed for your convenience. It's intended to be an easy way to load this configuration. Any additional arguments are passed on to emacs.

# Open ~/.bashrc
me ~/.bashrc
# Open the config
me ~/.config/me/init.el
# to save: Ctrl-X Ctrl-S
# to quit: Ctrl-X Ctrl-C

The e Alias

On the dev server, the following alias was added to ~/.bashrc.

alias e="emacsclient -nw"

If you've done M-x server-start in an Emacs instance, you can connect to it with e. This is Emacs' way of dealing with the slow start up problem.

# Open ~/.bashrc
e ~/.bashrc
# Open the config
e ~/.config/me/init.el
# when you're done: Ctrl-X #

Configuration

Prot's Basic Config

https://protesilaos.com/codelog/2024-11-28-basic-emacs-configuration/

I have helped several people set up a basic Emacs configuration. In the process, I have learnt about some of the problems they have with the out-of-the-box experience.

What follows is a rather simple, yet fully capable, setup to get started with Emacs. At the end of the file is the code block with the complete configuration (The complete configuration). The other sections explain the rationale for each component.

Use this as a starting point. If you are curious about my own setup, check this: https://protesilaos.com/emacs/dotemacs.

I made two changes to the base config.

  • display-buffer-alist got a few more entries to control the placment of help windows.
  • Everything font and theme related was commented out, because this config is geared toward use in terminals.
C-g get you out of trouble*
C-x C-j dired
F10 open menu bar on the terminal

[*] C-g can cancel a lot of things. If you get Emacs into an awkward spot, try C-g.

;;; init.el --- muupi emacs -*- lexical-binding: t; -*-
;; Author: gg
;; Keywords: emacs
;;; Commentary:
;; - Start with Prot's basic Emacs configuration as a base.
;; - Add a few creature comforts.
;; - Add A little more config for Sly and Common Lisp.
;;; Code:
(setq custom-file (locate-user-emacs-file "custom.el"))
(load custom-file :no-error-if-file-is-missing)
;;; Set up the package manager
(require 'package)
(package-initialize)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(when (< emacs-major-version 29)
 (unless (package-installed-p 'use-package)
 (unless package-archive-contents
 (package-refresh-contents))
 (package-install 'use-package)))
(defun my/frame-is-wide (buffer-or-name)
 "Return non-nil if the frame is wide.
BUFFER-OR-NAME is ignored."
 (>= (frame-width) 160))
(defun my/frame-is-narrow (buffer-or-name)
 "Return non-nil if the frame is NOT wide.
BUFFER-OR-NAME is ignored."
 (not (my/frame-is-wide buffer-or-name)))
;; [2025年05月06日 Tue 04:31] setq my default display-buffer-alist for easier debugging.
(setq display-buffer-alist
 '(("\\`\\*\\(Warnings\\|Compile-Log\\|Async Shell Command\\)\\*\\'"
 (display-buffer-no-window)
 (allow-no-window . t))
 ;; ((derived-mode . pdf-outline-buffer-mode)
 ;; (display-buffer-in-side-window)
 ;; (side . left)
 ;; (slot . 0)
 ;; (window-width . 77))
 ((derived-mode . compilation-mode)
 (display-buffer-in-side-window)
 (side . bottom)
 (slot . 0)
 (window-height . 7))
 ((and "\\*YASnippet Tables\\*" my/frame-is-wide) ; [2025年05月08日 Thu 13:14] This is a help-mode
 (display-buffer-in-side-window) ; window that gets a special case to make sure
 (side . right) ; it's wide enough.
 (slot . 0)
 (window-width . 102))
 ((and (or (derived-mode . Info-mode)
 (derived-mode . help-mode))
 my/frame-is-wide)
 (display-buffer-in-side-window)
 (side . right)
 (slot . 0)
 (window-width . 88))
 ((and (or (derived-mode . Info-mode)
 (derived-mode . help-mode))
 my/frame-is-narrow)
 (display-buffer-in-side-window)
 (side . bottom)
 (slot . 0)
 (window-height . 20))
 ((and "\\*Man"
 my/frame-is-wide)
 (display-buffer-in-side-window)
 (side . right)
 (slot . 0)
 (window-width . 88))
 ((and "\\*Man"
 my/frame-is-narrow)
 (display-buffer-in-side-window)
 (side . bottom)
 (slot . 0)
 (window-height . 20))))
;;; Basic behaviour
(use-package delsel
 :ensure nil
 :hook (after-init . delete-selection-mode))
(defun prot/keyboard-quit-dwim ()
 "Do-What-I-Mean behaviour for a general `keyboard-quit'.
The generic `keyboard-quit' does not do the expected thing when
the minibuffer is open. Whereas we want it to close the
minibuffer, even without explicitly focusing it.
The DWIM behaviour of this command is as follows:
- When the region is active, disable it.
- When a minibuffer is open, but not focused, close the minibuffer.
- When the Completions buffer is selected, close it.
- In every other case use the regular `keyboard-quit'."
 (interactive)
 (cond
 ((region-active-p)
 (keyboard-quit))
 ((derived-mode-p 'completion-list-mode)
 (delete-completion-window))
 ((> (minibuffer-depth) 0)
 (abort-recursive-edit))
 (t
 (keyboard-quit))))
(define-key global-map (kbd "C-g") #'prot/keyboard-quit-dwim)
;;; Tweak the looks of Emacs
;; Those three belong in the early-init.el, but I am putting them here
;; for convenience. If the early-init.el exists in the same directory
;; as the init.el, then Emacs will read+evaluate it before moving to
;; the init.el.
;;(menu-bar-mode 1)
;;(scroll-bar-mode 1)
(tool-bar-mode -1)
(let ((mono-spaced-font "Monospace")
 (proportionately-spaced-font "Sans"))
 (set-face-attribute 'default nil :family mono-spaced-font :height 100)
 (set-face-attribute 'fixed-pitch nil :family mono-spaced-font :height 1.0)
 (set-face-attribute 'variable-pitch nil :family proportionately-spaced-font :height 1.0))
;; (use-package modus-themes
;; :ensure t
;; :config
;; (load-theme 'modus-vivendi-tinted :no-confirm-loading))
;; Remember to do M-x and run `nerd-icons-install-fonts' to get the
;; font files. Then restart Emacs to see the effect.
(use-package nerd-icons
 :ensure t)
(use-package nerd-icons-completion
 :ensure t
 :after marginalia
 :config
 (add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup))
(use-package nerd-icons-corfu
 :ensure t
 :after corfu
 :config
 (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
(use-package nerd-icons-dired
 :ensure t
 :hook
 (dired-mode . nerd-icons-dired-mode))
;;; Configure the minibuffer and completions
(use-package vertico
 :ensure t
 :hook (after-init . vertico-mode))
(use-package marginalia
 :ensure t
 :hook (after-init . marginalia-mode))
(use-package orderless
 :ensure t
 :config
 (setq completion-styles '(orderless basic))
 (setq completion-category-defaults nil)
 (setq completion-category-overrides nil))
(use-package savehist
 :ensure nil ; it is built-in
 :hook (after-init . savehist-mode))
(use-package corfu
 :ensure t
 :hook (after-init . global-corfu-mode)
 :bind (:map corfu-map ("<tab>" . corfu-complete))
 :config
 (setq tab-always-indent 'complete)
 (setq corfu-preview-current nil)
 (setq corfu-min-width 20)
 (setq corfu-popupinfo-delay '(1.25 . 0.5))
 (corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'
 ;; Sort by input history (no need to modify `corfu-sort-function').
 (with-eval-after-load 'savehist
 (corfu-history-mode 1)
 (add-to-list 'savehist-additional-variables 'corfu-history)))
;;; The file manager (Dired)
(use-package dired
 :ensure nil
 :commands (dired)
 :hook
 ((dired-mode . dired-hide-details-mode)
 (dired-mode . hl-line-mode))
 :config
 (setq dired-recursive-copies 'always)
 (setq dired-recursive-deletes 'always)
 (setq delete-by-moving-to-trash t)
 (setq dired-dwim-target t))
(use-package dired-subtree
 :ensure t
 :after dired
 :bind
 ( :map dired-mode-map
 ("<tab>" . dired-subtree-toggle)
 ("TAB" . dired-subtree-toggle)
 ("<backtab>" . dired-subtree-remove)
 ("S-TAB" . dired-subtree-remove))
 :config
 (setq dired-subtree-use-backgrounds nil))
(use-package trashed
 :ensure t
 :commands (trashed)
 :config
 (setq trashed-action-confirmer 'y-or-n-p)
 (setq trashed-use-header-line t)
 (setq trashed-sort-key '("Date deleted" . t))
 (setq trashed-date-format "%Y-%m-%d %H:%M:%S"))

After Prot

A few more settings

;;; After Prot is Me
;; More settings
(setopt auto-save-default nil)
(setopt column-number-mode t)
(setopt create-lockfiles nil) ;; http://xahlee.info/emacs/emacs/emacs_auto-save_backup.html
(setopt indent-tabs-mode nil)
(setopt inhibit-startup-message t)
(setopt make-backup-files nil)
(setopt mouse-autoselect-window t)
(setopt scroll-conservatively 101)
(setopt tab-bar-show 1) ; [2025年05月11日 Sun 03:37] Only show tab bar when more than 1 tab exists.
(electric-pair-mode 1)
(global-auto-revert-mode 1)
(recentf-mode 1)
(save-place-mode 1)
(winner-mode 1)
;; .common-lisp files
(add-to-list 'auto-mode-alist '("\\.common-lisp\\'" . common-lisp-mode))

ace-window

An ergonomic way to move between windows

M-o ace-window
(use-package ace-window
 :ensure t
 :config
 (global-set-key (kbd "M-o") 'ace-window)
 (setopt ace-window-display-mode nil))

consult

User-friendly minibuffer completion functions

There are so many useful bindings here. I recommend scanning through the list, and trying the ones that catch your attention. Also, if you can't remember that consult-imenu is bound to M-g i, it's perfectly fine to say M-x consult-imenu also.

C-x b consult-buffer
M-g i consult-imenu
M-s l consult-line
(use-package consult
 :ensure t
 ;; Replace bindings. Lazily loaded by `use-package'.
 :bind (;; C-c bindings in `mode-specific-map'
 ("C-c M-x" . consult-mode-command)
 ("C-c h" . consult-history)
 ("C-c k" . consult-kmacro)
 ("C-c m" . consult-man)
 ("C-c i" . consult-info)
 ([remap Info-search] . consult-info)
 ;; C-x bindings in `ctl-x-map'
 ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
 ("C-x b" . consult-buffer) ;; orig. switch-to-buffer
 ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
 ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
 ("C-x t b" . consult-buffer-other-tab) ;; orig. switch-to-buffer-other-tab
 ("C-x r b" . consult-bookmark) ;; orig. bookmark-jump
 ("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
 ;; Custom M-# bindings for fast register access
 ("M-#" . consult-register-load)
 ("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
 ("C-M-#" . consult-register)
 ;; Other custom bindings
 ("M-y" . consult-yank-pop) ;; orig. yank-pop
 ;; M-g bindings in `goto-map'
 ("M-g e" . consult-compile-error)
 ("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
 ("M-g g" . consult-goto-line) ;; orig. goto-line
 ("M-g M-g" . consult-goto-line) ;; orig. goto-line
 ("M-g o" . consult-outline) ;; Alternative: consult-org-heading
 ("M-g m" . consult-mark)
 ("M-g k" . consult-global-mark)
 ("M-g i" . consult-imenu)
 ("M-g I" . consult-imenu-multi)
 ;; M-s bindings in `search-map'
 ("M-s d" . consult-find) ;; Alternative: consult-fd
 ("M-s c" . consult-locate)
 ("M-s g" . consult-grep)
 ("M-s G" . consult-git-grep)
 ("M-s r" . consult-ripgrep)
 ("M-s l" . consult-line)
 ("M-s L" . consult-line-multi)
 ("M-s k" . consult-keep-lines)
 ("M-s u" . consult-focus-lines)
 ;; Isearch integration
 ("M-s e" . consult-isearch-history)
 :map isearch-mode-map
 ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
 ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
 ("M-s l" . consult-line) ;; needed by consult-line to detect isearch
 ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
 ;; Minibuffer history
 :map minibuffer-local-map
 ("M-s" . consult-history) ;; orig. next-matching-history-element
 ("M-r" . consult-history)) ;; orig. previous-matching-history-element
 ;; Enable automatic preview at point in the *Completions* buffer. This is
 ;; relevant when you use the default completion UI.
 :hook (completion-list-mode . consult-preview-at-point-mode)
 ;; The :init configuration is always executed (Not lazy)
 :init
 ;; Tweak the register preview for `consult-register-load',
 ;; `consult-register-store' and the built-in commands. This improves the
 ;; register formatting, adds thin separator lines, register sorting and hides
 ;; the window mode line.
 (advice-add #'register-preview :override #'consult-register-window)
 (setq register-preview-delay 0.5)
 ;; Use Consult to select xref locations with preview
 (setq xref-show-xrefs-function #'consult-xref
 xref-show-definitions-function #'consult-xref)
 ;; Configure other variables and modes in the :config section,
 ;; after lazily loading the package.
 :config
 ;; Optionally configure preview. The default value
 ;; is 'any, such that any key triggers the preview.
 ;; (setq consult-preview-key 'any)
 ;; (setq consult-preview-key "M-.")
 ;; (setq consult-preview-key '("S-<down>" "S-<up>"))
 ;; For some commands and buffer sources it is useful to configure the
 ;; :preview-key on a per-command basis using the `consult-customize' macro.
 (consult-customize
 consult-theme :preview-key '(:debounce 0.2 any)
 consult-ripgrep consult-git-grep consult-grep consult-man
 consult-bookmark consult-recent-file consult-xref
 consult--source-bookmark consult--source-file-register
 consult--source-recent-file consult--source-project-recent-file
 ;; :preview-key "M-."
 :preview-key '(:debounce 0.4 any))
 ;; Optionally configure the narrowing key.
 ;; Both < and C-+ work reasonably well.
 (setq consult-narrow-key "<") ;; "C-+"
 ;; Optionally make narrowing help available in the minibuffer.
 ;; You may want to use `embark-prefix-help-command' or which-key instead.
 ;; (keymap-set consult-narrow-map (concat consult-narrow-key " ?") #'consult-narrow-help)
 )

corfu-terminal

This makes corfu (in-buffer completion) work in non-graphical Emacs.

This is what lets you do in-buffer completion of function and variable names with TAB.

(use-package corfu-terminal
 :ensure t
 :config
 (unless (display-graphic-p)
 (corfu-terminal-mode 1)))

editorconfig

Load .editorconfig files automatically and apply project-wide formatting standards.

(use-package editorconfig
 :ensure t
 :config
 (editorconfig-mode 1))

jq-mode

jq is a wonderful JSON processing tool.

People love to use it on the command line, but I believe one of the best ways to use it is in org-babel.

(use-package jq-mode
 :ensure t
 :defer t)

magit

An interface to git

C-x g Open magit
(use-package magit
 :ensure t
 :defer t)

markdown-mode

Markdown support

Markdown is the most common mark up format for README files these days, and GitHub Flavored Markdown (GFM) is the most popular variant. This configuration assumes GFM, and uses pandoc if available for converting markdown to HTML.

;; https://blog.bitsandbobs.net/blog/emacs-markdown-live-preview/
(use-package markdown-mode
 :ensure t
 :mode ("\\.md\\'" . gfm-mode)
 :commands (markdown-mode gfm-mode)
 :config
 (if (executable-find "pandoc")
 (setq markdown-command "pandoc -t html5")))

org

Emacs' org-mode is a text mark up format that is used heavily in the Muupi code base. A few config tweaks were made to help it work well and look good.

Important keybindings to remember

M-RET start a heading
M-<right> indent a heading
M-<left> outdent a heading
TAB cycle the visibility of a heading
S-TAB close all headings
C-c l org-store-link
C-c C-l org-insert-link

Sometimes, when you go into a big org document, you might want to S-TAB (Shift-TAB at the same time) in order to get a big picture view of the document. Then you can open the headings one-by-one with TAB.

(use-package olivetti
 :ensure t
 :config
 (setopt olivetti-body-width 0.75))
(defun greentext ()
 "Highlight >greentext in current buffer."
 (interactive)
 (highlight-lines-matching-regexp "^\s*>" 'hi-green-b))
(use-package org
 :ensure nil
 :hook ((org-mode . olivetti-mode)
 (org-mode . greentext))
 :config
 (setopt
 org-indent-mode t
 org-auto-align-tags nil
 org-tags-column 0
 org-edit-src-content-indentation 0 ; INFO: This is a must-have for src blocks.
 org-imenu-depth 4)
 ;; https://www.reddit.com/r/emacs/comments/1es353x/comment/li5ufbo/
 ;; - removed /italic/.
 ;; - I might remove _underline_ too.
 (setq-default org-emphasis-alist '(("*" bold)
 ("_" underline)
 ("=" org-verbatim verbatim)
 ("~" org-code verbatim)
 ("+" (:strike-through t))))
 ;; keybinding
 (global-set-key (kbd "C-c l") 'org-store-link)
 ;; org-babel
 (setq org-confirm-babel-evaluate nil)
 (org-babel-do-load-languages
 'org-babel-load-languages '(
 (emacs-lisp . t)
 (jq . t)
 (lisp . t) ; I needed this.
 (perl . t)
 (shell . t)
 (sql . t)
 ))
 (setq org-babel-lisp-eval-fn #'sly-eval)
 )

paredit

This provides structural editing for lisps.

If you want to use it, M-x paredit-mode while viewing lisp source code. It's not automatically enabled, because it could be surprising if you're not used to it.

For more details on usage, visit https://paredit.org/.

(use-package paredit
 :ensure t
 :defer t)

prog-mode

These are general settings for all programming modes.

(use-package prog-mode
 :ensure nil
 :hook ((prog-mode . hs-minor-mode) ; This makes things foldable without installing extras.
 (prog-mode . hl-line-mode))
 )

sly

Sly provides Common Lisp REPL integration.

This one has been setup to use /usr/bin/sbcl and display-buffer-alist has been customized to keep the windows that sly creates from getting too chaotic.

  • The REPL and the exception handler stick to the bottom.
  • Help windows and data inspectors stick to the right.
M-x sly Start a sly session
M-o Move between windows with ace-window
C-c C-c compile current function
C-c C-k compile current file
M-g i jump to symbol with imenu

Typical Workflow

  • Open muupi-renderer.org.common-lisp.
  • M-x sly
  • M-o to switch from the REPL back to the source code.
  • M-x sly-eval-buffer to send the whole buffer to the REPL.
  • M-o to switch back to the REPL
  • (hunchentoot:start *muupi*)
  • notice a bug
  • try to fix the bug by editing the function you think is broken
  • C-c C-c to recompile the function

The REPL in sly is a lot nicer than the command-line sbcl REPL.

M-p Previous lisp expression in history
M-n Next lisp expression in history
;; If you already have your own Emacs config but are keen to give
;; Common Lisp a try, just copy this sly config into your own.
;; It will make sly's window placement more predictable.
(use-package sly
 :ensure t
 :config
 (let ((patterns '(("\\*sly-mrepl"
 (display-buffer-in-side-window)
 (side . bottom)
 (slot . 0)
 (window-height . 18))
 ("\\*sly-db"
 (display-buffer-in-side-window)
 (side . bottom)
 (slot . 1))
 ("\\*sly-inspector"
 (display-buffer-in-side-window)
 (side . right)
 (slot . 1)
 (window-width . 80))
 ("\\*sly-description"
 (display-buffer-in-side-window)
 (side . right)
 (slot . 0)
 (window-width . 80)))))
 (cl-loop for p in patterns
 do (add-to-list 'display-buffer-alist p)))
 (setopt inferior-lisp-program "/usr/bin/sbcl"))

undo-fu

This brings a sane linear undo and redo to Emacs.

C-z undo
C-S-z redo
C-. redo
<f7> undo
<f9> redo

Note that redo is bound to both Ctrl-Shift-Z and Ctrl-.. The reason for this is that many terminals can't tell the difference between Ctrl-Z and Ctrl-Shift-Z, so I needed an alternative binding for redo that works on the terminal.

Later, <f7> and <f9> were added for undo/redo, because tmux can't handle Ctrl-.. Note that F7 and F9 are usually used for the media keys for previous track (⏮) and next track (⏭), so hopefully this makes it easier to remember for undo and redo.

(use-package undo-fu
 :ensure t
 :config
 (global-unset-key (kbd "C-z"))
 (global-set-key (kbd "C-z") 'undo-fu-only-undo)
 (global-set-key (kbd "C-S-z") 'undo-fu-only-redo)
 (global-set-key (kbd "C-.") 'undo-fu-only-redo)
 (global-set-key (kbd "<f7>") 'undo-fu-only-undo)
 (global-set-key (kbd "<f9>") 'undo-fu-only-redo))

which-key

Explore key bindings interactively.

Example

  • Hit C-x and then wait a second.
  • You should see the minibuffer filled with possibilities for your next key press.
(use-package which-key
 :ensure t
 :config
 (which-key-mode 1))

yasnippet

This provides a library of mini text templates.

Typical Workflow

  • Open a source file.
  • M-x yas-describe-tables to see a list of templates.
  • M-o to switch back and forth between source and template list.
  • Type a short code from the rightmost column.
  • Then hit TAB.
(use-package yasnippet
 :ensure t
 :config
 (yas-global-mode 1))
(use-package yasnippet-snippets
 :ensure t)

Additional Functions

;;; fun
(defun insert-setq (variable)
 "Insert a setq expression for a VARIABLE into the current buffer.
This is useful when hacking on variables that usually get their values
via repeated calls to `add-to-list'. (That's OK for configuration, but
annoying during development.)
Example:
 \\[insert-setq] RET display-buffer-alist RET "
 ;; [2025年05月08日 Thu 22:47] The interactive expression was shamelessly copied from describe-variable.
 (interactive
 (let ((v (variable-at-point))
 (enable-recursive-minibuffers t)
 (orig-buffer (current-buffer))
 val)
 (setq val (completing-read
 (format-prompt "Insert variable" (and (symbolp v) v))
 #'help--symbol-completion-table
 (lambda (vv)
 (or (get vv 'variable-documentation)
 (and (not (keywordp vv))
 ;; Since the variable may only exist in the
 ;; original buffer, we have to look for it
 ;; there.
 (buffer-local-boundp vv orig-buffer))))
 t nil nil
 (if (symbolp v) (symbol-name v))))
 (list (if (equal val "")
 v (intern val)))))
 (let* ((data (eval variable))
 (need-quote (if (eq (type-of data) 'cons) "'" ""))
 (point-a (point)))
 (insert (format "(setq %s %s%s)"
 (symbol-name variable)
 need-quote
 (pp-to-string data)))
 (let ((point-b (point)))
 (indent-region point-a point-b))))

Load extra.el If It Exists

If you want to add your own customizations, you can put them in ~/.config/me/extra.el. This config will load that file if it exists, and it will never be overwritten on updates.

Everything after here is optional (but fun).

(setq extra-file (locate-user-emacs-file "extra.el"))
(load extra-file :no-error-if-file-is-missing)
(provide 'init)
;;; init.el ends here

Extra Examples

Basic GUI Use

If you wanted to use this config with a GUI Emacs, you could try putting something like this in ~/.config/me/extra.el. This file will never be overwritten during upgrades, so you can feel free to keep your customizations there.

M-x i/set-frame-font select font and size
M-x my/org-faces adjust org heading sizes
M-x consult-theme choose a new theme
C-S-t open new tab
C-<next>, C-<PgDn> next tab
C-<prev>, C-<PgUp> prev tab

~/.config/me/extra.el (extra configuration for you)

;;; Functions
;; M-x i/set-frame-font :: change fonts with ease
(defun i/set-frame-font (&optional font size)
 "Set the FONT and SIZE for the current frame."
 (interactive (list
 (completing-read
 "font: " (process-lines "fc-list" "--format=%{family[0]}\n"))
 (completing-read
 "size: " (mapcar (lambda (n) (format "%d" n)) (number-sequence 12 22 1)))))
 (set-frame-font (concat font " " size)))
;; Make org headings a little bigger for extra emphasis.
(defun my/org-faces ()
 "Customize faces for `org-mode`."
 (interactive)
 (set-face-attribute 'org-document-title nil :height 2.0)
 (set-face-attribute 'org-level-1 nil :height 1.4)
 (set-face-attribute 'org-level-2 nil :height 1.2))
;;; Settings
;; modes
(context-menu-mode 1) ; right-click menu
(menu-bar-mode -1) ; many think emacs looks better without the menu bar
(scroll-bar-mode -1) ; same for the scroll bar
(blink-cursor-mode -1)
;; Ctrl-Shift-T to open a new tab
;; Ctrl-PgDn to go to the next tab
;; Ctrl-PgUp to go to the prev tab
;; just like a web browser
(global-set-key (kbd "C-<prior>") 'tab-previous)
(global-set-key (kbd "C-<next>") 'tab-next)
(global-set-key (kbd "C-S-t") 'tab-new)
;; This is how to temporarily use another default font.
;; (i/set-frame-font "VictorMono Nerd Font Propo" "14")
;; Fonts set this way will become the default for when new Emacs frames are created.
(let ((mono-spaced-font "Monospace")
 (proportionately-spaced-font "Sans"))
 (set-face-attribute 'default nil :family mono-spaced-font :height 100)
 (set-face-attribute 'fixed-pitch nil :family mono-spaced-font :height 1.0)
 (set-face-attribute 'variable-pitch nil :family proportionately-spaced-font :height 1.0))
(use-package ef-themes
 :ensure t
 :config
 (load-theme 'ef-dream t nil))
(use-package org
 :ensure nil
 :config
 (my/org-faces))
/ggxx/me/media/branch/main/me.ef-dream.png
Here's the result of the extra.el above.

Fonts

Where To Get Free Fonts

Nerd Fonts - This is one of the best collection of programming fonts available.

Aporetic by Prot

Google Fonts

Using set-face-attribute Versus set-frame-font

Configuring fonts using set-face-attribute should be preferred, because when fonts are set this way, all newly created Emacs frames will use these font settings as their default.

(let ((mono-spaced-font "Monospace")
 (proportionately-spaced-font "Sans"))
 (set-face-attribute 'default nil :family mono-spaced-font :height 100)
 (set-face-attribute 'fixed-pitch nil :family mono-spaced-font :height 1.0)
 (set-face-attribute 'variable-pitch nil :family proportionately-spaced-font :height 1.0))

The other way to set fonts in Emacs is set-frame-font. This lets you set fonts on a frame-by-frame basis, and some people may want this behavior from time to time. Unfortunately, the default behavior of set-frame-font is very anitquated and user-unfriendly, so I've written a wrapper around it called i/set-frame-font that makes font and size selection more intuitive. It requires the fc-list executable to be in the path, and it should work for most non-Apple Unixes.

SQL Client

Some Aseembly Is Required

Emacs comes with an SQL client built in, but there isn't a lot of documentation out there on how to configure it. It has a really unique and useful feature that is off by default, and it's not obvious how to turn it on.

The Best Hidden Feature

That feature is sql-placeholder-filter. It lets you put placeholders in your SQL so that when you execute them, Emacs will ask you for values for those placeholders before sending the SQL to the REPL. In other words, you get parameterized SQL statements.

To enable it, we override entries in sql-product-alist with new entries that are exactly the same except for the :input-filter field where we add sql-placeholders-filter. See the SQLite entry below for an example.

If the original entry already had a fitler, use the -compose function to combine the original filter with sql-placeholders-filter. See the MariaDB entry below for an example.

;; This has many useful functions, but today, we just want `-compose'.
;; https://github.com/magnars/dash.el
(use-package dash
 :ensure t)
(use-package sql
 :ensure nil
 :config
 ;; [2025年05月13日 Tue 06:27] MariaDB prompts were getting accidentally deleted without this.
 ;; https://emacs.stackexchange.com/a/14158/37580
 (setopt comint-prompt-read-only t)
 (add-to-list
 'sql-product-alist
 '(sqlite :name "SQLite"
 :free-software t
 :font-lock sql-mode-sqlite-font-lock-keywords
 :sqli-program sql-sqlite-program
 :sqli-options sql-sqlite-options
 :sqli-login sql-sqlite-login-params
 :sqli-comint-func sql-comint-sqlite
 :list-all ".tables"
 :list-table ".schema %s"
 :completion-object sql-sqlite-completion-object
 :prompt-regexp "^sqlite> "
 :prompt-length 8
 :prompt-cont-regexp "^ \\.\\.\\.> "
 :input-filter sql-placeholders-filter))
 (add-to-list
 'sql-product-alist
 '(mariadb :name "MariaDB"
 :free-software t
 :font-lock sql-mode-mariadb-font-lock-keywords
 :sqli-program sql-mariadb-program
 :sqli-options sql-mariadb-options
 :sqli-login sql-mariadb-login-params
 :sqli-comint-func sql-comint-mariadb
 :list-all "SHOW TABLES;"
 :list-table "DESCRIBE %s;"
 :prompt-regexp "^MariaDB \\[.*]> "
 :prompt-cont-regexp "^ [\"'`-]> "
 :syntax-alist ((35 . "< b"))
 :input-filter (-compose sql-remove-tabs-filter sql-placeholders-filter))))
;; This provides perfect and aesthetic SQL indentation
;; especially for multiline statements. Hit TAB and
;; it'll line things up beautifully.
;; Highly recommended.
(use-package sql-indent
 :ensure t
 :hook ((sql-mode . sqlind-minor-mode)))
;; This automatically capitalizes SQL keywords and functions as you type.
;; This is nice when it works, but it is imperfect.
;; Try it out.
;; If you don't like it, remove it.
(use-package sqlup-mode
 :ensure t
 :hook ((sql-mode . sqlup-mode)
 (sql-interactive-mode . sqlup-mode))
 :config
 (setq sqlup-blacklist '("name" "user" "binary" "changes"
 "check" "connection" "open" "output"
 "parameter" "read" "schema" "session"
 "system")))

To use sql-placeholders-filter, write SQL with placeholders like this.

-- list users with names shorter than `n`
SELECTid,username,LENGTH(username)ASlenFROMusersWHERELENGTH(username)<=&n-- placeholders start with &
ORDERBYlenDESC;

When you evaluate that with C-c C-c, Emacs will ask you for a value for n. You can have more than one placeholder in an expression, and they may be inside strings too.

Setting Up Connections

For convenience, it's nice to have connection info setup early so that you can M-x sql-connect and start using a database right away. However, you DO NOT want to store passwords in your Emacs configuration files. Here's an example of how to avoid that using backquotes and environment variables.

;; Setting up named connections.
(setq sql-connection-alist `(("muupi"
 (sql-product 'mariadb)
 (sql-user ,(getenv "MUUPI_UNAME"))
 (sql-password ,(getenv "MUUPI_PASSWD"))
 (sql-server "localhost")
 (sql-database "muupi"))))

Evil Mode

If you are a refugee from vim, I will say that evil provides a very good approximation of vim to the extent that's possible in Emacs. There are some things that are missing like :set, but that's because there's already an Emacs way of setting variables so :set isn't necessary. However, : in evil-mode is arguably more powerful than in vim, because you get access to interactive Elisp functions instead.

It may sound crazy to a vim lover, but to me, Emacs feels like a better vim than vim itself, because you get the majority of what's good about vim combined with access to the vast Elisp ecosystem.

(use-package evil
 :ensure t
 :init
 (setq evil-want-integration t)
 (setq evil-want-keybinding nil)
 (setq evil-undo-system 'undo-fu)
 :config
 (evil-mode 1))
(use-package evil-collection
 :after evil
 :ensure t
 :config
 (evil-collection-init))
;; https://github.com/edkolev/evil-lion
;; glip= :: align inner paragraph on =
(use-package evil-lion
 :ensure t
 :config
 (evil-define-key 'normal prog-mode-map
 (kbd "g l") 'evil-lion-left
 (kbd "g L") 'evil-lion-right)
 (evil-define-key 'visual prog-mode-map
 (kbd "g l") 'evil-lion-left
 (kbd "g L") 'evil-lion-right))
(use-package evil-surround
 :ensure t
 :config
 (global-evil-surround-mode 1))