My primary goal with Emacs is to consolidate the software I use in my free time. At work, I rely completely on Microsoft’s ecosystem and have done what I can to get away from that ecosystem at home. Originally, the intention for my Emacs configuration was to create one environment for all of the programming languages I use. As I have familiarized myself with Emacs and Org mode, I have begun using Emacs for more things.
My init.el
file installs straight
if it doesn’t already exist and loads the configuration generated from tangling this file. This file isn’t a great example of literate configuration because I don’t explain anything particularly well and leave out a lot of details, but it is an example.
I’m on Arch and use the emacs-pgtk-native-comp-git
package in the AUR.
Systemd services get placed in a couple different places, you can check where Emacs is by running systemctl status --user emacs
. I created my own systemd entry in ~/.config/systemd/user/emacs.service
as described here. If the service file exists, just make sure it looks like this:
[Unit] Description=Emacs texteditor Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/ [Service] Type=forking ExecStart=/usr/local/bin/emacs --daemon ExecStop=/usr/local/bin/emacsclient --eval "(kill-emacs)" Environment=SSH_AUTH_SOCK=%t/keyring/ssh Restart=on-failure [Install] WantedBy=default.target
To run an Emacs client, either run:
emacsclient -create-frame --alternate-editor=""
Or, what I do is create a Desktop entry in ~/.local/share/applications/emacsclient.desktop
:
[Desktop Entry] Name = Emacs Client GenericName=Text Editor Comment=Emacs Editor MimeType=text/english;text/plain Exec=emacsclient -create-frame --alternate-editor="" Icon=emacs Type=Application Terminal=false Categories=Development;TextEditor;Utility; StartupWMClass=Emacs
As I use Org more and more, I find myself wishing Pop!_os knew to open Org files with Emacs. Thanks to Tecosaur, I have learned that this is possible! Just copy this to /usr/share/mime/packages/org.xml
:
<?xml version="1.0"
encoding="utf-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="text/org">
<comment>Emacs Org-mode File</comment>
<glob pattern="*.org"/>
<alias type="text/org"/>
</mime-type>
</mime-info>
On top of that, Papirus’s icon pack has an icon for it. Papirus is on the AUR.
sudo update-mime-database /usr/share/mime
xdg-mime default emacsclient.desktop text/org
Apparently your mime database can be in different places. For Arch, it was in /usr/share/mime. If you're not running Emacs as a daemon, then replace =emacsclient.desktop
with emacs.desktop
. Also, the update command will only work if the XML file has been created.
While I like the idea of organizing everything alphabetically, this README
more closely follows a hierarchy determined by package dependencies. In an effort to keep the tangled configuration file in line with Emacs standards, I’ve added a code block containing front matter:
;;; config.el --- My configuration tangled from README.org
;;; Commentary:
;;; This file is generated by org-babel-tangle. For persistent changes, edit 'README.org' instead!
;;; Code:
Some packages have external dependencies that must be installed, and I get tired of installing packages one by one every time I work with a new OS install. To alleviate that burden, my configuration also tangles a script to install all dependencies readily available to pacman
and its default repositories. A good first step in this script is to update:
sudo pacman -Syu \
All packages are installed up front to allow more flexibility with configuration organization.
(straight-use-package 'auctex)
(straight-use-package 'latex-preview-pane)
(straight-use-package 'mw-thesaurus)
(straight-use-package 'org-contrib)
(straight-use-package 'rainbow-mode)
I’ve started making themes and putting them in the eponymous themes
directory.
(add-to-list 'custom-theme-load-path (expand-file-name "themes" user-emacs-directory))
(if (daemonp)
(load-theme 'light t)
(load-theme 'light t))
(defun apply-post-frame-config ()
(set-fontset-font "fontset-default" '(#xF6C3 . #xF6C3) "Font Awesome 5 Free"))
In the past when using Word, I have run into trouble with backups. I don’t typically keep documents synced with source control, so I decided to use Emacs to create per-session and per-save backups, as described in the documentation here:
(setq backup-directory-alist `(("." . ,(concat user-emacs-directory "backups/per-save"))))
(setq-default tab-width 4)
(defun force-backup-of-buffer ()
"Make a special per-session backup at the first save of each
emacs session."
(when (not buffer-backed-up)
(let ((backup-directory-alist `(("" . ,(concat user-emacs-directory "backup/per-session"))))
(kept-new-versions 3))
(backup-buffer)))
(let ((buffer-backed-up nil))
(backup-buffer)))
(add-hook 'before-save-hook 'force-backup-of-buffer)
I don’t like that Custom clutters my init file, so I have it write to a separate file:
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
I want Emacs to open the Scratch buffer on startup and I want that buffer’s default major mode to be Org:
(setq inhibit-startup-message t
initial-major-mode 'org-mode
initial-scratch-message nil)
I have a couple yank-related settings to make Emacs play a little more nicely with external programs:
(setq mouse-yank-at-point t
save-interprogram-paste-before-kill t
x-select-enable-clipboard t
x-select-enable-primary t)
Since I’m on Wayland now, I’m not exactly sure if the x-select
variables have any effect.
By default, Emacs forces the end of a sentence to have two spaces, but this isn’t the nineties anymore:
(setq sentence-end "[\\.\\?\\!] +")
I use Recentf to keep track of Emacs history. Consult uses the history file to inform completions:
(setq recentf-save-file (concat user-emacs-directory ".recentf")
recentf-max-menu-items 40)
Places keeps track of where my cursor was at last time I visited a file:
(setq save-place-file (concat user-emacs-directory "places"))
(setq-default save-place t)
I want words to wrap to succeeding lines like in most word processors (which, admittedly, Emacs is not):
(setq-default display-line-numbers-width-start t
truncate-lines nil
word-wrap t)
I don’t structure my Emacs sessions enough for lockfiles to make sense:
(setq create-lockfiles nil)
I set my cursor to blink so it’s easier to find:
(blink-cursor-mode 1)
Use ‘y’ or ‘n’ instead of ‘yes’ or ‘no’ in prompts:
(fset 'yes-or-no-p 'y-or-n-p)
I have trouble tracking parentheses, so highlight matches when possible:
(show-paren-mode 1)
I like saving minibuffer history so completions later on are more relevant:
(savehist-mode)
Set completions to be case-insensitive by default. A cool side-effect of this is that if I include capitalization, the search becomes case-sensitive to match:
(setq read-file-name-completion-ignore-case t)
I don’t like Emacs to make sounds:
(setq ring-bell-function 'ignore)
I use Auctex to modify LaTeX files, and have found TeX-PDF mode to be a bit of a hindrance:
(setq TeX-PDF-mode nil)
Disable showing function arguments in echo area:
(global-eldoc-mode -1)
I jumped on the minimal UI bandwagon and disabled menu-
, tool-
, and scroll-bar-mode
:
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
After switching to Arch, I’ve hooked SSH and GPG into Gnome-Keyring, so my default password store is unlocked on login. Emacs can also take advantage of this, as long as I point it to the properly unlocked SSH auth socket:
(setenv "SSH_AUTH_SOCK" (concat "/run/user/" (int-to-string (user-real-uid)) "/keyring/ssh"))
I don’t have many keybindings set up; I mostly unset C-x C-r
to make register functions easier to get to in Meow mode:
(global-set-key (kbd "C-x K") 'kill-buffer-and-window)
(global-unset-key (kbd "C-x C-r"))
(global-unset-key (kbd "C-x C-p"))
(global-set-key (kbd "M-g f") 'rg)
(global-set-key (kbd "M-g F") 'consult-find)
(global-set-key (kbd "M-F") 'forward-to-word)
(global-set-key (kbd "M-B") 'backward-to-word)
This section contains all of the configuration related to built-in packages. Some of this configuration is used elsewhere, I unset C-x C-r
in part because I never open files in read-only mode, and because removing it makes it easier to set marks when using Meow.
I’m a strong advocate for Org at this point, especially for its ability to use both variable- and fixed-pitch fonts in the same buffer. Since I’m running Emacs as a daemon, this configuration needs to be loaded each time a new window instance is created. There are some times when I start Emacs outside of the daemon, so my config only adds the hook if it detects daemon mode:
(defun apply-post-frame-config ()
(set-face-attribute 'fixed-pitch nil :font "Roboto Mono 14")
(set-face-attribute 'variable-pitch nil :font "Roboto 14")
(set-face-attribute 'default nil :font "Roboto Mono 14")
(set-fontset-font "fontset-default" '(#xF6C3 . #xF6C3) "Font Awesome 5 Free"))
(if (daemonp)
(add-hook 'server-after-make-frame-hook 'apply-post-frame-config)
(apply-post-frame-config))
When I code, I want to be able to easily toggle a line to be commented. This is the implementation that I understood:
(defun custom-toggle-comment ()
"Toggle comment on region if region is active else toggle comment on line."
(interactive)
(if (use-region-p)
(comment-or-uncomment-region (region-beginning) (region-end))
(toggle-comment-on-line)))
(defun toggle-comment-on-line ()
"Comment or uncomment current line."
(interactive)
(comment-or-uncomment-region (line-beginning-position) (line-end-position)))
I’m using Meow mode, so the above code is bound in that section.
One thing I missed from Visual Studio was the automatic highlighting of TODO items. I like the idea of being able to do that in any document, and decided for code, the regex i wanted to use was comment characters at the start of a line, followed by an arbitrary amount of whitespace and the text “TODO”. I’m only building expressions around the languages I use (Elisp, Python, and Rust):
(defun highlight-todo-semicolon ()
(font-lock-add-keywords nil '(("^;;+\s*?\\(TODO:.*$\\)" 1 'font-lock-warning-face prepend))))
(defun highlight-todo-slash ()
(font-lock-add-keywords nil '(("^//+\s*?\\(TODO:.*$\\)" 1 'font-lock-warning-face))))
(add-hook 'emacs-lisp-mode-hook 'highlight-todo-semicolon)
I also thought this would be handy to have in a text mode, but since text mode doesn’t really understand comments, I decided to remove the constraint of a TODO being on its own line, and instead just be wrapped in square brackets:
(defun highlight-todo-bracket ()
(font-lock-add-keywords nil '(("\\[TODO:.*\\]" 0 'font-lock-warning-face prepend))))
(add-hook 'text-mode-hook 'highlight-todo-bracket)
I like my modeline pretty clean, and dislike having all of the minor modes listed for a given buffer, especially because there always seem to be quite a few:
(setq mode-line-modes (mapcar (lambda (elem)
(pcase elem
(`(:propertize (,_ minor-mode-alist . ,_) . ,_)
"")
(t elem)))
mode-line-modes))
I also like certain things to be on the left side of the frame, and certain things to be on the right side. This code provides that functionality:
(defun mode-line-render (left right)
"Render mode-line with filled white space between LEFT and RIGHT."
(let ((available-width (- (window-total-width)
(+ (length (format-mode-line left))
(length (format-mode-line right)))
2)))
(append left
(list (format (format "%%%ds" available-width) ""))
right)))
(setq-default mode-line-format '((:eval
(mode-line-render (list "%e %b %* [%l,%c] "
mode-line-modes)
(list '(vc-mode vc-mode)
mode-line-misc-info
mode-line-end-spaces)))))
I often find myself wanting to remove a bunch of whitespace in one stroke:
(defun whack-whitespace (arg)
"Deletes all white space from point to the next word. With prefix ARG delete across newlines as well. The only danger in this is that you don't have to actually be at the end of a word to make it work. It skips over to the next whitespace and then whacks it all to the next word."
(interactive "P")
(let ((regexp (if arg "[ \t\n]+" "[ \t]+")))
(re-search-forward regexp nil t)
(replace-match "" nil nil)))
I want a little bit of a border between the window border and the text:
(add-to-list 'default-frame-alist '(internal-border-width . 20))
When you use fonts installed from the Linux repositories, they don’t have the metadata that allows Emacs to recognize italic and bold versions of a font, so they don’t render. Instead, download the bold, italic, regular, and bolditalic versions of Alegreya and Hack and place the .ttf
files in /usr/local/share/fonts/
or just keep the fonts in source control.
I know there are packages to automatically insert parentheses, but if I can avoid another dependency, I will:
(defun autopair-insert (arg)
(interactive "P")
(let (pair)
(cond
((assq last-command-event skeleton-pair-alist)
(autopair-open arg))
(t
(autopair-close arg)))))
(defun autopair-open (arg)
(interactive "P")
(let ((pair (assq last-command-event
skeleton-pair-alist)))
(cond
((and (not mark-active)
(eq (car pair) (car (last pair)))
(eq (car pair) (char-after)))
(autopair-close arg))
(t
(skeleton-pair-insert-maybe arg)))))
(defun autopair-close (arg)
(interactive "P")
(cond
(mark-active
(let (pair open)
(dolist (pair skeleton-pair-alist)
(when (eq last-command-event (car (last pair)))
(setq open (car pair))))
(setq last-command-event open)
(skeleton-pair-insert-maybe arg)))
((looking-at
(concat "[ \t\n]*"
(regexp-quote (string last-command-event))))
(replace-match (string last-command-event))
(indent-according-to-mode))
(t
(self-insert-command (prefix-numeric-value arg))
(indent-according-to-mode))))
It also helps to define which characters get autopaired and how:
(setq skeleton-pair t
skeleton-pair-alist '((?\( _ ?\))
(?\[ _ ?\])
(?{ _ ?})
(?\" _ ?\")))
Auto-pairing is great, but I don’t usually find it helpful in the minibuffer so I disable that: [TODO: Figure out what this does]
(define-key minibuffer-inactive-mode-map (kbd ")") nil)
(straight-use-package 'all-the-icons)
(require 'all-the-icons)
(straight-use-package 'dashboard)
(require 'dashboard)
(require 'project)
(setq dashboard-banner-logo-title "So you think you'll get something done today.")
(setq dashboard-center-content t)
(setq dashboard-footer-icon (all-the-icons-octicon "dashboard"
:height 1.1
:v-adjust -0.05
:face 'font-lock-keyword-face))
(setq dashboard-items '((bookmarks . 3)
(agenda . 3)
(projects . 3)
(recents . 5)))
(setq dashboard-projects-backend 'project-el)
(setq dashboard-set-file-icons t)
(setq dashboard-set-init-info nil)
(setq dashboard-set-heading-icons t)
(setq dashboard-startup-banner 'logo)
(setq dashboard-footer-messages '("Maximum overdrive!"))
(setq initial-buffer-choice (lambda () (get-buffer "*dashboard*")))
(dashboard-setup-startup-hook)
Dired
(require 'dired)
(defun dired-open-file ()
"In dired, open the selected file on this line."
(interactive)
(let* ((file (dired-get-filename nil t)))
(message "Opening %s..." file)
(call-process "xdg-open" nil 0 nil file)))
(define-key dired-mode-map (kbd "<RET>") 'dired-find-alternate-file)
(define-key dired-mode-map (kbd "M-<RET>") 'dired-find-file)
(setq dired-dwim-target t
dired-listing-switches "-al --group-directories-first")
IBuffer-VC organizes the list of open buffers by project, as defined by project.el
. Pretty handy:
(straight-use-package 'ibuffer-vc)
(require 'ibuffer-vc)
(add-hook 'ibuffer-mode-hook 'ibuffer-vc-set-filter-groups-by-vc-root)
Peep-Dired provides file previews:
(straight-use-package 'peep-dired)
(require 'peep-dired)
(define-key dired-mode-map (kbd "M-k") 'peep-dired-kill-buffers-without-window)
(define-key dired-mode-map (kbd "M-n") 'peep-dired-next-file)
(define-key dired-mode-map (kbd "M-p") 'peep-dired-prev-file)
Orderless provides a nice completion function option that I was missing from Helm. This and Consult have been a great replacement for Helm.
(straight-use-package 'orderless)
(require 'orderless)
(setq completion-category-defaults nil
completion-styles '(orderless)
completion-category-overrides '((file (styles basic partial-completion))))
It took a while, but I finally got Corfu where I wanted it once I realized I needed to install Cape to get the same buffer completions provided by Company in text mode and others. I moved to Corfu as part of my desire to move to packages that leverage built-in Emacs utilities. Also, Corfu supports orderless completions which are amazing in-buffer.
Note that Corfu requires Orderless
(straight-use-package 'corfu)
(require 'corfu)
(setq corfu-quit-no-match t
corfu-cycle t
corfu-auto t
tab-indent-always 'complete)
(corfu-global-mode 1)
Cape provides completion-at-point functions that aren’t available by default in Corfu. Below are the functions I use and why:
Function | Reason |
---|---|
cape-dabbrev | Completions based on words in current buffer |
(straight-use-package 'cape)
(require 'cape)
(add-to-list 'completion-at-point-functions #'cape-dabbrev)
Consult is my preferred completion engine. I’m overriding some global keybindings which works well with meow
’s leader function; for quicker access, I have a couple keybindings set up in Meow’s configuration as well.
(straight-use-package 'consult)
(require 'consult)
(setq consult-project-root-function (lambda () (cdr (project-current))))
(global-set-key (kbd "C-s") 'consult-line)
(global-set-key (kbd "C-M-s") 'consult-imenu)
(global-set-key (kbd "C-x b") 'consult-buffer-other-window)
(global-set-key (kbd "C-x C-b") 'consult-buffer)
My preferred search utility. With Emacs 28, there’s fido-vertical-mode
built-in, but it doesn’t support orderless filtering so I’m still using Vertico!
(straight-use-package 'vertico)
(require 'vertico)
(vertico-mode)
Marginalia provides helpful context to completions in the minibuffer:
(straight-use-package 'marginalia)
(require 'marginalia)
(marginalia-mode)
Obligatory change irc.freenode.net
to irc.libera.chat
.
(require 'erc)
(setq erc-default-server "irc.libera.chat")
Flyspell has done a decent job with word corrections. For now, I’m using Aspell as my checker program in text-mode
, prog-mode
, and their derived modes:
(require 'flyspell)
(setq ispell-program-name "/usr/bin/aspell")
(add-hook 'org-mode-hook 'flyspell-mode)
(add-hook 'prog-mode-hook 'flyspell-prog-mode)
This function isn’t useful since I added Flyspell-Correct to my configuration, but I’m not ready to delete the function yet:
(defun flyspell-goto-previous-error (arg)
"Go to arg previous spelling error."
(interactive "p")
(while (not (= 0 arg))
(let ((pos (point))
(min (point-min)))
(if (and (eq (current-buffer) flyspell-old-buffer-error)
(eq pos flyspell-old-pos-error))
(progn
(if (= flyspell-old-pos-error min)
(progn
(message "Restarting from end of buffer")
(goto-char (point-max)))
(backward-word 1))
(setq pos (point))))
(while (and (> pos min)
(let ((ovs (overlays-at pos))
(r '()))
(while (and (not r) (consp ovs))
(if (flyspell-overlay-p (car ovs))
(setq r t)
(setq ovs (cdr ovs))))
(not r)))
(backward-word 1)
(setq pos (point)))
(setq arg (1- arg))
(setq flyspell-old-pos-error pos)
(setq flyspell-old-buffer-error (current-buffer))
(goto-char pos)
(if (= pos min)
(progn
(message "No more miss-spelled word!")
(setq arg 0))
(forward-word)))))
Flyspell requires aspell
:
aspell aspell-en \
This package really just provides integration with completion-read and hence consult!
(straight-use-package 'flyspell-correct)
(require 'flyspell-correct)
(define-key text-mode-map (kbd "M-g n") 'flyspell-correct-next)
(define-key text-mode-map (kbd "M-g p") 'flyspell-correct-previous)
Htmlize comes into play when I’m exporting Org documents to HTML, especially if there are code blocks involved:
(straight-use-package 'htmlize)
(require 'htmlize)
Magit or git command line. Those are the only options.
(straight-use-package 'magit)
(require 'magit)
After moving to Arch, I’ve decided to use Emacs as my file navigator/manager. Treemacs works great for this:
(straight-use-package 'treemacs)
(require 'treemacs)
(global-set-key (kbd "C-x F") 'treemacs)
To make the look and feel between Dired and Treemacs more consistent, I use treemacs-icons-dired
to add Treemacs icons to Dired:
(straight-use-package 'treemacs-icons-dired)
(require 'treemacs-icons-dired)
(add-hook 'dired-mode-hook 'treemacs-icons-dired-mode)
I haven’t had the opportunity to use this yet, but I’m looking forward to experimenting with Treemacs visualizations in Magit:
(straight-use-package 'treemacs-magit)
(require 'treemacs-magit)
(straight-use-package 'eglot)
(straight-use-package 'consult-eglot)
So far, I use Python and Rust in Emacs, both of which have good LSP options. Because of this, I have a section for general, LSP-oriented configuration and separate sections for each language that is supported by the LSP mode umbrella.
(straight-use-package 'lsp-mode)
(require 'lsp-mode)
(setq lsp-modeline-diagnostics-scope :project
lsp-signature-doc-lines 1)
(define-key lsp-mode-map (kbd "C-c `") 'lsp-restart-workspace)
(define-key lsp-mode-map (kbd "C-c a") 'lsp-execute-code-action)
(define-key lsp-mode-map (kbd "C-c d") 'lsp-describe-thing-at-point)
(define-key lsp-mode-map (kbd "C-c s") 'rg)
(define-key lsp-mode-map (kbd "C-c e") 'lsp-rename)
(define-key lsp-mode-map (kbd "C-c S") 'lsp-treemacs-symbols)
LSP doesn’t integrate well with Corfu, so we need to add some configuration to make things work:
(setq lsp-completion-provider :none)
(defun corfu-lsp-setup ()
(setq-local completion-styles '(orderless)
completion-category-defaults nil))
(add-hook 'lsp-completion-mode-hook 'corfu-lsp-setup)
I have found LSP-Treemacs pretty useful. I like being able to see all of the symbols in a project, similar to the Object Explorer in Visual Studio.
(straight-use-package 'lsp-treemacs)
(require 'lsp-treemacs)
I really like both Consult and LSP, so I figured this would be a good package to have. So far, I only really use consult-lsp-diagnostics
, but I’m still figuring things out:
(straight-use-package 'consult-lsp)
(require 'consult-lsp)
(consult-lsp-marginalia-mode)
I’m using LSP-Pyright for Python development, in spite of my tendency to steer clear of Microsoft packages:
(straight-use-package 'lsp-pyright)
(require 'lsp-pyright)
I have seen issues with opencv where Pyright will not only not provide completions for cv2 functions, but flymake will show errors where these functions are used. I have found a way to address this, however:
cd <projectdir>
python -m venv venv
source venv/bin/activate
pip install mypy
cd venv/lib/python3.10/site-packages/cv2
stubgen -m cv2 -o .
mv cv2.pyi __init__.pyi
Most languages I use are hooked up to LSP:
(require 'python)
(add-to-list 'exec-path "~/.local/bin")
(add-hook 'python-mode-hook 'lsp)
My autopair settings for Python:
(define-key python-mode-map (kbd "(") 'autopair-insert)
(define-key python-mode-map (kbd ")") 'autopair-insert)
(define-key python-mode-map (kbd "[") 'autopair-insert)
(define-key python-mode-map (kbd "]") 'autopair-insert)
(define-key python-mode-map (kbd "{") 'autopair-insert)
(define-key python-mode-map (kbd "}") 'autopair-insert)
(define-key python-mode-map (kbd "\"") 'autopair-insert)
(define-key python-mode-map (kbd "'") 'autopair-insert)
(setq lsp-pylsp-server-command "~/.local/bin/pylsp")
For LSP-Pyright to work, it has to be installed through NPM:
npm \
I only have a few programming languages I use regularly, Rust is one of them.
(straight-use-package 'rust-mode)
(require 'rust-mode)
(add-to-list 'exec-path "~/.cargo/bin")
(setenv "PATH" (concat "~/.cargo/bin:" (getenv "PATH")))
(setq lsp-rust-analyzer-server-display-inlay-hints t
lsp-rust-analyzer-server-command '("~/.local/bin/rust-analyzer")
lsp-rust-server 'rust-analyzer)
(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode))
(add-hook 'rust-mode-hook 'lsp)
(add-hook 'rust-mode-hook 'highlight-todo-slash)
Rustic mode is a good package, but it adds a lot that I don’t really use and doesn’t interface with the rust compiler how i would like. In the spirit of keeping my package footprint minimal, i decided to write my own build commands:
(defun cargo-build (arg)
"Build with input ARG."
(interactive "MCargo Build arguments: ")
(compile (concat "cargo build " arg)))
(define-key rust-mode-map (kbd "C-c b") 'cargo-build)
(define-key rust-mode-map (kbd "C-c f") 'rust-format-buffer)
(define-key rust-mode-map (kbd "C-c r")
(lambda ()
(interactive)
(compile "cargo run")))
(define-key rust-mode-map (kbd "C-c k")
(lambda ()
(interactive)
(compile "cargo check")))
(define-key rust-mode-map (kbd "C-c t")
(lambda ()
(interactive)
(compile "cargo test -- --nocapture")))
(define-key rust-mode-map (kbd "C-c C-f") nil)
Rust and Rust-Analyzer are available in Arch’s default repositories:
rust rust-analyzer \
For me, Rainbow Delimiters has saved a lot of time tracking down parentheses and brackets in Rust and what Elisp I am willing to commit to:
(straight-use-package 'rainbow-delimiters)
(require 'rainbow-delimiters)
(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
I used Evil mode for a long time, but eventually decided to switch to Meow because it makes a good effort at synergizing with Emacs’ built-in keybindings. Sure, Evil doesn’t change keybindings it doesn’t know about, but it’s jarring to switch between modal editing and using control to execute commands, and I’m somewhat resistant to going into my config and remapping keys. Meow, on the other hand, has an agnostic way of accommodating keybindings from any mode (that I have used), provided said mode has configured its keybindings using typical conventions.
Note that Meow has dependencies on Consult:
(straight-use-package 'meow)
(require 'meow)
(defun meow-setup ()
(setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)
(meow-motion-overwrite-define-key
'("n" . meow-next)
'("p" . meow-prev)
'("<escape>" . ignore))
(meow-leader-define-key
;; '("n" . "H-n")
;; '("p" . "H-p")
'("1" . meow-digit-argument)
'("2" . meow-digit-argument)
'("3" . meow-digit-argument)
'("4" . meow-digit-argument)
'("5" . meow-digit-argument)
'("6" . meow-digit-argument)
'("7" . meow-digit-argument)
'("8" . meow-digit-argument)
'("9" . meow-digit-argument)
'("0" . meow-digit-argument)
'("/" . meow-keypad-describe-key)
'(";" . custom-toggle-comment)
'("?" . meow-cheatsheet)
'("d" . windmove-down)
'("i" . mu4e)
'("k" . kill-sentence)
'("l" . windmove-left)
'("n" . display-line-numbers-mode)
'("r" . windmove-right)
'("s" . flyspell-correct-previous)
'("t" . org-capture)
'("u" . windmove-up)
'("w" . whack-whitespace))
(meow-normal-define-key
'("0" . meow-expand-0)
'("9" . meow-expand-9)
'("8" . meow-expand-8)
'("7" . meow-expand-7)
'("6" . meow-expand-6)
'("5" . meow-expand-5)
'("4" . meow-expand-4)
'("3" . meow-expand-3)
'("2" . meow-expand-2)
'("1" . meow-expand-1)
'("-" . negative-argument)
'(";" . custom-toggle-comment)
'("," . meow-inner-of-thing)
'("." . meow-bounds-of-thing)
'("[" . meow-beginning-of-thing)
'("]" . meow-end-of-thing)
'("a" . move-beginning-of-line)
'("b" . backward-char)
'("d" . delete-char)
'("e" . move-end-of-line)
'("f" . forward-char)
'("g" . meow-cancel-selection)
'("i" . meow-insert)
'("k" . kill-line)
'("K" . kill-sexp)
'("l" . recenter-top-bottom)
'("m" . meow-join)
'("n" . next-line)
'("o" . meow-open-above)
'("O" . meow-open-below)
'("p" . previous-line)
'("r" . query-replace)
'("s" . consult-line)
'("S" . consult-imenu)
'("t" . meow-till)
'("u" . meow-undo)
'("v" . meow-visit)
'("W" . meow-next-symbol)
'("x" . meow-line)
'("X" . meow-goto-line)
'("y" . consult-yank-from-kill-ring)
'("Y" . meow-sync-grab)
'("z" . zap-up-to-char)
'("Z" . zap-to-char)
'("'" . repeat)
'("<escape>" . ignore)))
(meow-setup)
(meow-global-mode 1)
One of the main drivers for me to use Mu4e (or another Emacs package) for email management is to provide access to email in Org mode. This really shines when you need to make a TODO
item from an email. You simply use a capture template, insert a link to the email, flesh out the TODO
tasks, and save. If you leave and have to come back, there is no need to go to your inbox and find the email, everything is in your TODO
.
With everything installed we need to perform an initial sync using the mbsync
command. Before that, a mail directory must be created: mkdir ~/Mail
My .mbsyncrc
is set up to use Gnus Authinfo, so we need to set that up as well. It’s not too bad, simply create a file named ~/.authinfo
and add this line:
machine smtp.gmail.com login USERNAME password PASSWORD port 587
Now, encrypt the file with the following command:
gpg2 --symmetric .authinfo
Emacs has support for reading these encrypted files built-in. Just open the file in a buffer. Should you need to decrypt, though, just enter the following:
gpg2 --decrypt .authinfo.gpg
I have Mu4e hooked up to my gmail account so that’s how the example is laid out. Of course, you will need to substitute your username and password for the capitalized words, but other than that you should be good.
As an aside, Gnus Authinfo can be used in a variety of ways in Emacs: many packages support it. I recommend looking into it for any packages interfacing with a service you log into like Slack or Gitlab.
Now, mail can be synced using the config file. First, create your mail directory at ~/Mail
. A different location will require configuration changes. Since the config is in an unconventional directory, it must be specified explicitly. First, navigate to ~/.config/emacs/mu4e
and run mbsync -c .mbsyncrc -a
The last step is to index the messages with mu:
mu init --maildir=~/mail=
mu index
I’ve defined a convenience function called search-for-sender
which I’ve never had occasion to use, but it seems like a basic function that any email client should have.
I have a lot of customization for Mu4e. Admittedly, most of it was taken from other peoples’ configuration I found online. An interesting aspect of Mu4e contexts, which can be associated with an email address. This provides separation between work and home, for example.
NOTE: Mu4e has dependencies.
Since Mu4e isn’t on MELPA or anything, I install it with Pacman and add the directories to Emacs’ load path:
(add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e/")
(add-to-list 'load-path "/usr/share/emacs/site-lisp/ox-rss/")
(require 'mu4e)
I want to be able to search for emails by sender, so I wrote a function for it:
(defun search-for-sender (msg)
"Search for MSG messages sent by the sender of the message at point."
(mu4e-headers-search
(concat "from:" (cdar (mu4e-message-field msg :from)))))
(add-to-list 'mu4e-view-actions '("xsearch for sender" . search-for-sender) t)
Mu4e operates using contexts associated with different identities.
(require 'smtpmail)
(setq smtpmail-queue-mail nil)
(setq mu4e-contexts
(list
(make-mu4e-context
:name "general"
:enter-func (lambda () (mu4e-message "Entering general context"))
:leave-func (lambda () (mu4e-message "Leaving general context"))
:match-func (lambda (msg)
(when msg
(mu4e-message-contact-field-matches
msg '(:from :to :cc :bcc) "[email protected]")))
:vars '((user-mail-address . "[email protected]")
(user-full-name . "Andrew Burch")
(mu4e-sent-folder . "/sent")
(mu4e-refile-folder . "/all")
(mu4e-drafts-folder . "/drafts")
(mu4e-trash-folder . "/trash")
(mu4e-compose-signature . (concat "Cheers,\n Andrew"))
(mu4e-compose-format-flowed . t)
(smtpmail-queue-dir . "~/mail/gmail/queue/cur")
(message-send-mail-function . smtpmail-send-it)
(smtpmail-auth-credentials . (expand-file-name "~/.authinfo.gpg"))
(smtpmail-debug-info. t)
(smtpmail-default-smtp-server . "smtp.gmail.com")
(smtpmail-local-domain . "gmail.com")
(smtpmail-smtp-user . "andrewwburch")
(smtpmail-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-service . 587)
(smtpmail-debug-verbose . t)))))
Imagemagick can render images in plain text emails, but the configuration is a little obscure:
(when (fboundp 'imagemagick-register-types)
(imagemagick-register-types))
(setq mu4e-view-show-images t)
Currently, I have Mu4e set to pick the first available context when it needs one. This will probably change when I finally add in another context:
(setq mu4e-context-policy 'pick-first)
Mu4e adds its own newlines for formatting, I ask it to not do this:
(setq mu4e-compose-format-flowed t)
(add-hook 'message-mode-hook (lambda ()
(use-hard-newlines -1)))
Moving messages in Mbsync has caused syncing issues which can be fixed by making sure moved messages get renamed:
(setq mu4e-change-filenames-when-moving t)
I have Mu4e set up to always prefer plain text mail over HTML, mostly because HTML mail viewing isn’t great in Emacs yet:
(setq mu4e-view-html-plaintext-ratio-heuristic most-positive-fixnum)
(setq mu4e-view-prefer-html nil)
Hopefully, the rest of these settings are pretty self-explanatory:
(setq message-kill-buffer-on-exit t
mu4e-attachment-dir "~/downloads"
mu4e-compose-context-policy 'always-ask
mu4e-compose-dont-reply-to-self t
mu4e-compose-in-new-frame t
mu4e-compose-signature-auto-include nil
mu4e-confirm-quit t
mu4e-headers-auto-update t
mu4e-headers-date-format "%H:%M %d-%m-%Y"
mu4e-get-mail-command "mbsync -a"
mu4e-maildir (expand-file-name "~/mail")
mu4e-sent-messages-behavior 'delete ;; Gmail puts messages in Sent so Mu4e doesn't have to.
mu4e-update-interval 300
mu4e-view-show-addresses t)
(add-to-list 'mu4e-view-actions '("ViewInBrowser" . mu4e-action-view-in-browser) t)
(define-key mu4e-view-mode-map (kbd "M-n") 'mu4e-view-headers-next)
(define-key mu4e-view-mode-map (kbd "M-p") 'mu4e-view-headers-prev)
(add-hook 'mu4e-headers-mode-hook
(defun mu4e-change-head()
(interactive)
(setq mu4e-headers-fields `((:date . 22)
(:flags . 6)
(:from . 22)
(:thread-subject . ,(- (window-body-width) 70))
(:size . 7)))))
(add-hook 'mu4e-view-mode-hook
(lambda()
(local-set-key (kbd "<RET>") 'mu4e-view-browse-url-from-binding)
(local-set-key (kbd "<tab>") 'shr-next-link)
(local-set-key (kbd "<backtab>") 'shr-previous-link)))
Mu4e itself needs to be installed from AUR. Its other dependencies can be installed with pacman
:
isync html2text gnupg \
I’ve set a bunch of face attributes to get Org documents looking “better”. I’ve set org-edit-src-content-indentiation
to 0
so everything stays left-aligned. I find the indentation more distracting than anything. The last thing here that future me might need a reminder of is that I set org-log-into-drawer
to logbook
, mostly for compatibility with Orgzly, which I use to interact with org files on mobile.
(require 'org)
(require 'org-protocol)
(defun generate-post ()
(setq post-title (read-string "Title: "))
(setq post-file-name (replace-regexp-in-string ":" "" (replace-regexp-in-string " " "-" (downcase post-title))))
(expand-file-name (format "%s.org" post-file-name) "~/git/nothingissimple/org/posts"))
(defun generate-reference (title url body))
I wrote this function in an attempt to clean up exported LaTeX files, but I quickly realized that it broke links. Maybe someday I will get around to fixing it:
(defun org-export-latex-remove-labels (s backend info)
(when (org-export-derived-backend-p org-export-current-backend 'latex)
(replace-regexp-in-string "\\\\label{sec:org[a-z0-9]+}\n" "" s)))
(setq org-export-filter-final-output-functions '(org-export-latex-remove-labels))
I try to hide as much markup as possible to keep the document looking clean:
(setq org-edit-src-content-indentation 0
org-hide-emphasis-markers t
org-hide-leading-stars t)
Since I’ve started using Variable-Pitch mode, the indentation scheme Org uses doesn’t quite line up headings with their text and it bugs me so I just disabled Org’s indentation altogether:
(setq org-adapt-indentation nil)
I keep emphasis markers hidden to make documents look nicer, but every once in a while I get confused and need to toggle them on to fix the markup:
(defun org-toggle-emphasis-markers ()
"Toggle hiding/showing of org emphasis markers."
(interactive)
(if org-hide-emphasis-markers
(set-variable 'org-hide-emphasis-markers nil)
(set-variable 'org-hide-emphasis-markers t))
(org-mode-restart))
[TODO: This is broken]
If all of a task’s subtasks are marked as DONE
, the parent task should be as well:
(defun org-summary-todo (n-done n-not-done)
"Switch entry to DONE when all subentries are done, to TODO otherwise."
(let (org-log-done org-log-states) ; turn off logging
(org-todo (if (= n-not-done 0) "DONE" "TODO"))))
(add-hook 'org-after-todo-statistics-hook 'org-summary-todo)
[TODO: Figure out why I added this]
(define-prefix-command 'ring-map)
I’ve modified faces quite a bit to make Org mode documents look what I would describe as “better”, or at least a little closer to a WYSIWYG processor. At some point I will try to merge these changes into my theme:
(add-to-list 'font-lock-extra-managed-props 'invisible)
(font-lock-add-keywords 'org-mode '(("^\\*+ " (0 '(face nil invisible t)))))
(defcustom org-ellipsis nil
:group 'org-startup
:type '(choice (const :tag "Default" nil)
(face :tag "Face" :value org-warning)
(string :tag "String" :value " ")))
I’ve added a few templates:
(setq org-capture-templates
'(("e" "event" plain (function (lambda ()
(let ((path (read-file-name "Select file:")))
(find-file path)
(goto-char 0)
(if (search-forward "* Reference" nil t)
(progn
(org-end-of-subtree)
(newline))
(progn
(goto-char (point-max))
(newline)
(insert "* Reference")
(newline))
))))
"\n** %^{Title}\nSCHEDULED: %(org-insert-timestamp (org-read-date nil t \"+1y\"))\n:PROPERTIES:\n:REF: %l\n:STYLE: habit\n:END:\n\n%(unless (string= (string-trim \"%i\") \"\")(format \"#+begin_quote\n%s\n#+end_quote\" \"%i\"))\n")
z ("j" "journal" plain (file+datetree "~/org/journal.org")
"")
("l" "link" entry (file+headline "~/org/tasks/Todo.org" "Tasks")
"* %a\n")
("p" "post" plain (file generate-post)
"%(format \"#+title: %s\n#+date:\n#+filetags:\n#+slug: %s\n#+category: draft\n#+options: toc:nil num:nil\n#+description:\n\n\" post-title post-file-name)")
("r" "recipe" entry (file+headline "~/org/recipes.org" "Recipes")
"%(format \"* %s\nSCHEDULED: %s\n\n|Quantity|Unit|Ingredient|Notes|\n|----%?\n\n\" (read-string \"Recipe name:\") (org-insert-timestamp (org-read-date nil t \"+1y\")))")
("s" "skill" plain (function (lambda ()
(let ((path (read-file-name "Select file:")))
(find-file path)
(goto-char 0)
(if (search-forward "* Reference" nil t)
(progn
(org-end-of-subtree)
(newline))
(progn
(goto-char (point-max))
(newline)
(insert "* Reference")
(newline))))))
"\n** %^{Title}\n:PROPERTIES:\n:REF: %l\n:STYLE: habit\n:END:\n\n%(unless (string= (string-trim \"%i\") \"\")(format \"#+begin_quote\n%s\n#+end_quote\" \"%i\"))\n")
("t" "todo" entry (file+headline "~/org/tasks/Todo.org" "Tasks")
"* TODO %?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n:PROPERTIES:\n:CATEGORY: Todo\n:END:\n")))
I have Org log into a logbook
drawer. At first it was for compatibility with the Orgzly app, but now I just like it better:
(setq org-log-into-drawer "logbook")
Noticed interactive org-export wasn’t working properly unless org was reloaded in my config:
(org-reload)
Figuring out if I need this..
(define-key org-mode-map (kbd "<M-return>") nil)
Hopefully, these changes are self-explanatory:
(setq org-directory "~/org"
org-export-allow-bind-keywords t
org-highest-priority ?A
org-lowest-priority ?E)
(org-load-modules-maybe t)
Auto-pairing isn’t useful in all text-oriented modes, but it sure has come in handy in Org-mode:
(define-key org-mode-map (kbd "(") 'autopair-insert)
(define-key org-mode-map (kbd ")") 'autopair-insert)
(define-key org-mode-map (kbd "[") 'autopair-insert)
(define-key org-mode-map (kbd "]") 'autopair-insert)
(define-key org-mode-map (kbd "{") 'autopair-insert)
(define-key org-mode-map (kbd "}") 'autopair-insert)
(define-key org-mode-map (kbd "\"") 'autopair-insert)
Variable-Pitch mode does a lot for making Org files more pleasant to work with:
(add-hook 'org-mode-hook (lambda ()
(electric-indent-local-mode -1)
(variable-pitch-mode)
(setq truncate-lines nil)))
I have a hook to clean up artifacts from org-export
:
(defun clean-artifacts (infile outfile)
(message "Infile: %s" infile)
(message "Outfile: %s" outfile)
(let ((ext (file-name-extension outfile))
(filename (file-name-sans-extension)))
(cond ((string-equal ext "pdf")
(delete-file (concat filename ".tex"))))))
(setq org-publish-after-publishing-hook 'clean-artifacts)
Last but not least, my agenda configuration:
(setq org-agenda-files '("~/org/tasks/"))
To export to LaTeX, texlive
is required:
texlive-most \
I’m still trying to figure out how to integrate Org-Roam into my workflow. It seems like it could be so helpful!
(straight-use-package 'org-roam)
(setq org-roam-v2-ack t)
(require 'org)
(require 'org-roam)
(require 'org-roam-protocol)
(setq org-roam-capture--file-name-default "%<%Y%m%d>"
org-roam-capture-templates '(("d" "default" plain "%?"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+title: ${title}\n")
:unnarrowed t))
org-roam-completion-system 'ido
org-roam-graph-edge-extra-config '(
("color" . "green")
("fillcolor" . "green"))
org-roam-graph-extra-config '(
("bgcolor" . "lightgray"))
org-roam-graph-node-extra-config '(
("color" . "skyblue")
("fillcolor" . "skyblue")
("fontname" . "Arial")
("style" . "filled")))
(setq org-roam-directory "/home/andy/nothingissimple")
(org-roam-setup)
Org-Roam manages nodes in a SQLite database, so that needs to be installed:
sudo pacman -S sqlite3
Additionally, Org-Roam has nifty protocol support to enable external applications to send information to Emacs. Org has this support as well, but I wasn’t able to get it working properly. Org-Roam seems to have gotten this down-pat because it worked straight away and was simpler to set up than Org based on the information I found. First, I created an application for other applications to use to send data to Emacs:
[Desktop Entry] Name=Org-Protocol Exec=emacsclient %u Icon=emacs-icon Type=Application Terminal=false Categories=System; MimeType=x-scheme-handler/org-protocol;
Now, other applications just need to be told to use this application. In a browser, for example, creating a bookmarklet lets me send information to Emacs using Roam-Ref:
javascript:location.href='org-protocol://roam-ref?template=f&ref='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)+'&body='+encodeURIComponent(window.getSelection())
I mostly use a derived Ox-Slimhtml backend to generate my website, but this configuration also comes in handy for one-off documents.
(straight-use-package '(ox-slimhtml :host github :repo "ablatedsprocket/ox-slimhtml"))
(require 'ox-slimhtml)
(defun ab/org-html-table (table contents info)
"Transcodes a TABLE from Org to HTML.
CONTENTS is the contents of the table.
INFO is a plist used as a communication channel."
(let ((caption (car (car (car (org-element-property :caption table))))))
(concat "<table>\n"
(when caption
(format "<caption>%s</caption>\n" caption))
contents
"</tbody>\n</table>")))
(defun ab/org-html-table-row (table-row contents info)
"Transcodes a TABLE-ROW from Org to HTML.
CONTENTS is the contents of the row.
INFO is a plist used as a communication channel."
(if contents
(concat (when (eq 1 (org-export-table-row-group table-row info))
"<thead>\n")
"<tr>\n"
contents
"</tr>"
(when (eq 1 (org-export-table-row-group table-row info))
"</thead>\n<tbody>\n"))))
(defun ab/export-as-html
(&optional async subtreep visible-only body-only ext-plist)
(interactive)
(org-export-to-buffer 'trimhtml "*Org TRIMHTML Export*"
async subtreep visible-only body-only ext-plist
(lambda () (set-auto-mode t))))
(defun ab/export-to-html (&optional async subtreep visible-only body-only ext-plist)
(interactive)
(let* ((extension (concat "." (or (plist-get ext-plist :html-extension)
org-html-extension
"html")))
(file (org-export-output-file-name extension subtreep))
(org-export-coding-system org-html-coding-system))
(org-export-to-file 'trimhtml file
async subtreep visible-only body-only ext-plist ())))
(defun ab/org-html-table-cell (table-cell contents info)
"Transcodes a TABLE-CELL from Org to HTML.
CONTENTS is the contents of the cell.
INFO is a plist used as a communication channel."
(if (eq 1 (org-export-table-row-group (org-element-property :parent table-cell) info))
(concat "<th>" contents "</th>")
(concat "<td>" contents "</td>\n")))
(org-export-define-derived-backend 'trimhtml
'slimhtml
:menu-entry '(?a "trimhtml"
((?H "As trimhtml buffer" ab/export-as-html)
(?h "As trimhtml file" ab/export-to-html)))
:translate-alist
'((template . ox-slimhtml-template)
(link . ox-slimhtml-link)
(code . ox-slimhtml-verbatim)
(headline . ox-slimhtml-headline)
(table . ab/org-html-table)
(table-cell . ab/org-html-table-cell)
(table-row . ab/org-html-table-row))
:options-alist
'((:page-type "PAGE-TYPE" nil nil nil)
(:html-use-infojs nil nil nil)
(:description nil nil nil)
(:category nil nil nil)))
I use Simple-HTTPd to host my website locally for debugging.
(straight-use-package 'simple-httpd)
(require 'simple-httpd)
(when (file-exists-p "~/git/nothingissimple/site")
(setq httpd-root "~/git/nothingissimple/site"))
I’m hoping to use Restclient as a stand-in for Postman. I found an integration with Org-Babel that has been great to use. For me, Org-Babel is a must for Restclient. Restclient has some nifty hooks that can be run, but in order for them to work properly, I had to go to the restclient.el
and add (require 'cl)
. Requiring in my config here before requiring restclient
didn’t work.
(straight-use-package 'restclient)
(require 'restclient)
(defun as-send-to-webdav ()
"Send a file to webDAV server"
(interactive)
(let ((buf (buffer-name))
(contents (buffer-string)))
(message "Buffer name: %s" buf)
(ignore-errors (restclient-http-do "PUT" (concat "https://webdav.labb.local/" buf) '(("Content-Type" . "text/plain")) contents))))
If it were up to me, I would never use Postman again. Incorporating literate programming into test suites is amazing, especially when you can mix Restclient with your programming langauge of choice. Of course, this isn’t viable in a setting where multiple people are involved in a project.
(straight-use-package 'ob-restclient)
(org-babel-do-load-languages 'org-babel-load-languages '((restclient . t) (shell . t) (python . t)))
I’m all about Rust implementations of things.
(straight-use-package 'rg)
(require 'rg)
ripgrep \
My configuration also provides some customization of Emacs’ SQL mode. My workflow for SQL usually consists of two buffers: one of a SQL file and the other is the SQL interactive buffer. The SQL file is helpful because I can save and track my queries easily without thinking about it and the keeping the SQLi buffer separate is nice because I can disable font-lock so query results don’t have silly distracting faces. The first function disables font-lock for SQL Interactive mode and the second sets up the SQL Interactive-mode buffer automatically when SQL mode is enabled (either by opening a SQL buffer or manually activating SQL mode). Here, I’ve set up a list of connections I use frequently. I was surprised by how much of a quality-of-life improvement this was. I made a couple of keybindings for sending region and the whole buffer to the SQL Interactive mode buffer. I believe there are existing bindings for this, but I wanted something more in keeping with the rest of my keybinding setup.
(require 'sql)
(defun my-sql-disable-font-lock (orig-fun &rest args)
"Disable syntax highlighting for SQL output."
(cl-letf (((symbol-function #'sql-product-font-lock) #'ignore))
(apply orig-fun args)))
(defun my-sql-login-hook ()
"Custom SQL log-in behaviors."
(when (eq sql-product 'postgres)
(let ((proc (get-buffer-process (current-buffer))))
(comint-send-string proc "\\set ECHO queries\n"))))
(setq sql-connection-alist
'(
(home (sql-product 'postgres)
(sql-port 5432)
(sql-server "localhost")
(sql-user "postgres")
(sql-database "savetheglobe"))
(savetheglobe_home (sql-product 'postgres)
(sql-port 5432)
(sql-server "localhost")
(sql-user "postgres")
(sql-database "savetheglobe"))
(savetheglobe_heroku (sql-product 'postgres)
(sql-port 5432)
(sql-server "ec2-52-87-22-151.compute-1.amazonaws.com")
(sql-user "nrsgquqvfevzbu")
(sql-database "ddpfocn81le95m"))))
(define-key sql-mode-map (kbd "C-c r") 'sql-send-region)
(define-key sql-mode-map (kbd "C-c R") 'sql-send-buffer)
(advice-add 'sql-interactive-mode :around 'my-sql-disable-font-lock)
(add-hook 'sql-mode-hook 'sql-set-sqli-buffer)
(add-hook 'sql-mode-hook '(lambda ()
(setq truncate-lines t
word-wrap nil)))
(add-hook 'sql-login-hook 'my-sql-login-hook)
SQLUp up-cases SQL keywords. I liked this in SSMS and enjoy having it in Emacs as well.
(straight-use-package 'sqlup-mode)
(require 'sql)
(add-hook 'sql-mode-hook 'sqlup-mode)
(add-hook 'sql-interactive-mode-hook 'sqlup-mode)
I’ve been tinkering in the command line and documenting things in Emacs lately, so I’ve been using shell
to make it easier to get command line output into documents. It works well enough, but I’ve wanted to try vterm
to see what difference it makes. It’s supposed to be a lot faster for commands with a lot of output, but I feel like I notice a difference even with simple commands. It also has better support for things like fish
and themes.
NOTE: Vterm has dependencies.
(straight-use-package 'vterm)
(require 'vterm)
(setq vterm-timer-delay 0.01)
cmake vterm
(straight-use-package 'emacs-w3m)
(setq w3m-home-page "https://www.duckduckgo.com/")
I am slowly using YASnippet more, I’m considering adding an integration with Company for snippet completion, but part of me thinks that at that point I have a bigger problem.
(straight-use-package 'yasnippet)
(yas-global-mode 1)
Trying out EMMS as a music player.
(straight-use-package 'emms)
(require 'emms)
(require 'emms-player-mpd)
(require 'emms-setup)
(setq emms-player-list '(emms-player-mpd))
(setq emms-player-mpd-music-directory "~/music/")
(setq emms-player-mpd-server-name "localhost")
(setq emms-player-mpd-server-port "6600")
(setq emms-info-functions '(emms-info-mpd))
(setq emms-source-file-default-directory "~/music/")
(setq emms-source-file-directory-tree-function 'emms-source-file-directory-tree-find)
(emms-standard)
(emms-default-players)
mpd mpv \
(straight-use-package 'markdown-mode)
(straight-use-package 'csharp-mode)
Nothing to see here, just finishing touches on the config file.
(provide 'config)
;;; config.el ends here