|
|
||
|---|---|---|
| me.ef-dream.png | add screenshot | |
| muupi1.jpg | add pic of muupi | |
| README.org | reword | |
Muupi Emacs
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(orC-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
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 slyM-oto switch from the REPL back to the source code.M-x sly-eval-bufferto send the whole buffer to the REPL.M-oto 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-cto 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-xand 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-tablesto see a list of templates.M-oto 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))
Fonts
Where To Get Free Fonts
Nerd Fonts - This is one of the best collection of programming fonts available.
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))