This is an experiment to find out whether literate configurations in a single monolithic org-mode file actually have a perceivable benefit.
I’m using this configuration on a system running Arch Linux and Emacs
24.5, that’s why this configuration will be using the latest and
greatest features and not check for the system it’s running on.
While I do use emacsclient
for most of my editing needs, I
occasionally open emacs
instances for things like IRC or testing
purposes.
To debug it, I’ve added a properties drawer to the following subtree
that exports the code blocks to .emacs
with M-x org-babel-tangle
.
Please don’t use this Emacs configuration as is. I suggest you to
study it instead and inspect the many variables involved with the
built-in help system by hitting F1 v
.
Here’s a list of other literate Emacs configurations I’ve drawn inspiration from:
- http://writequit.org/org/
- http://doc.rix.si/org/fsem.html
- https://postmomentum.ch/steckemacs.html
- http://pages.sachachua.com/.emacs.d/Sacha.html
- https://github.com/larstvei/dot-emacs/blob/master/init.org
Emacs comes with a set of pretty wonky defaults, since the UI is what
we’ll see first, we’ll deal with it swiftly. Part of its
configuration can be set up in ~/.Xresources
and has the effect to
be used before any frame is displayed for the price of less
flexibility. For experimentation, reload such configuration with
xrdb ~/.Xresources
. Keep in mind that this hack might not do
anything for you on a sufficiently fast machine that loads themes
faster than you can notice.
I prefer not dealing with menu bars, tool bars and scroll bars and
deactivate them in ~/.Xresources
:
Emacs.menuBar: off
Emacs.toolBar: off
Emacs.verticalScrollBars: off
This used to give me nightmares, especially with the 24.4 addition that made the cursor blink ten times, then stops blinking until moving it again.
Emacs.cursorBlink: off
The default background color chosen is white, however I’m using a dark theme. Changing the background color after frame creation results in flashing, therefore I modify the background color to equal the one of the theme I’m going to load later.
Emacs.background: #002b36
The only other UI element left that stays visible throughout the
entire init time would be the mode line. It took me a bit of puzzling
to figure out the right format for making it look the same as in my
theme, but I eventually figured out from reading the last paragraph of
the (emacs) Table of Resources
info node that it does read in a list
from a string by studying the sources of faces.el
and xfaces.c
.
While this sounds kind of obvious, it means one can debug errors for
more complex values like the one for the box by simply invoking
read-from-string
on them.
Emacs.mode-line.attributeForeground: #93a1a1
Emacs.mode-line.attributeBackground: #002b36
Emacs.mode-line.attributeBox: (:line-width 1 :color "#073642")
My theme is a heavily customized Solarized, originally taken from Bozhidar Batsov, later rewritten to support additional modes, the 16-color palette in terminals and variant toggling. It’s stored in a separate file, to accomodate for that fact we need to customize a variable for user-made themes.
(setq custom-theme-directory "~/.emacs.d/theme")
(defun my-load-theme (&optional frame)
(with-selected-frame (or frame (selected-frame))
(load-theme 'my-solarized t)))
(my-load-theme)
(add-hook 'after-make-frame-functions 'my-load-theme)
Let’s disable questions about theme loading while we’re at it.
(setq custom-safe-themes t)
Tooltips can be themed as well.
(setq x-gtk-use-system-tooltips nil)
The mode line is essentially a huge nested list of mode line items you can customize to achieve your prefered look. The simplest way of getting a more useful mode line is using the smart-mode-line package which comes with more useful and color-coded items and a few interesting features like shortening file names and minor modes.
(setq sml/theme 'automatic
sml/mode-width 'full
sml/name-width '(0 . 20)
sml/replacer-regexp-list
'(("^~/org/" ":O:")
("^~/\\.emacs\\.d/" ":ED:")))
However I prefer hiding minor modes by default.
(setq rm-blacklist ".*")
The first obvious thing one notices upon launching an uncustomized Emacs is a rather fancy splash screen that informs you about the usage and advertises for the GNU project. I did eventually grow annoyed by it.
(setq inhibit-startup-screen t)
A less obvious one is the advertisement message displayed after
successful startup in the echo area. The culprit behind it is
display-startup-echo-area-message
and goes great lengths to make
sure it’s seen by first checking whether the
inhibit-startup-echo-area-message
has been set by the customize
system to your user name, then scanning your init file with a regular
expression for it. Considering I dislike using the customize
system, don’t have a conventional init file and find this pretty
silly, I disable this behaviour entirely by redefining the function to
display a bit more encouraging message instead.
(defun display-startup-echo-area-message ()
(message "Let the hacking begin!"))
After starting to use Emacs for IRC I’ve discovered that unlike
everything else on my system using the excellent fontconfig
software
it fails displaying Emoji such as the infamous PILE OF POO (💩) out of
the box. I’m afraid I will never find out the exact details of its
font fallback mechanism which might be for the better. To fix this
for both Emacs and Emacsclient for all font sizes I had to set up a
fontset consisting of my favourite monospaced and a suitable fallback
font in both X resources and my init file.
Emacs.Fontset-0: -*-DejaVu Sans Mono-*-*-*-*-14-*-*-*-*-*-fontset-dejavu14, symbol:-*-DejaVu Sans-*-*-*-*-14-*-*-*-*-*-*, symbol:-*-Symbola-*-*-*-*-14-*-*-*-*-*-*
Emacs.font: fontset-dejavu14
(setq default-frame-alist '((font . "DejaVu Sans Mono-10.5")))
(defun my-fix-emojis (&optional frame)
(set-fontset-font "fontset-default" nil "Symbola" frame 'append))
(my-fix-emojis)
(add-hook 'after-make-frame-functions 'my-fix-emojis)
This is a built-in feature I didn’t expect to be useful. If you type part of keybind, Emacs will display this part in the echo area after a timeout. One second is a bit too long though for my taste.
(setq echo-keystrokes 0.5)
Every file stating “This file is part of GNU Emacs.” is more often than not a source of code that may be crufty, nausea-inducing or just having weird defaults that I need to correct.
Let’s allow more than 800 KiB cache before starting garbage collection.
(setq gc-cons-threshold 50000000)
line-number-mode
displays the current line number in the mode line,
however it stops doing that in buffers when encountering at least one
overly long line and displays two question marks instead. This is
pretty unhelpful, the only workaround I’ve been able to find was to
increase line-number-display-width
to a substantially higher value.
(setq line-number-display-limit-width 10000)
See also this question on the Emacs SE.
I have no idea why, but apparently you get nasty warnings by the GnuTLS library when using https with the default settings. Increasing the minimum prime bits size to something safer alleviates that.
(setq gnutls-min-prime-bits 4096)
Since the *scratch*
buffer is pretty hard-wired into Emacs (see
buffer.c
), the least we could do is getting rid of its initial
message. No, it’s using its own mode instead of emacs-lisp-mode
for
the questionable benefit of having a function inserting evaluation
values after a newline.
(setq initial-scratch-message "")
(setq initial-major-mode 'emacs-lisp-mode)
However I don’t want to see the scratch buffer, let’s display our notes file instead as daily reminder what’s left to do.
(setq remember-notes-initial-major-mode 'org-mode)
(setq initial-buffer-choice 'remember-notes)
There is a bit of mismatch between the keybindings of
remember-notes-mode
and org-mode
, so let’s fix that:
(with-eval-after-load 'remember
(define-key remember-notes-mode-map (kbd "C-c C-c") nil))
There’s a fair number of Emacs functions that aren’t written in Emacs
Lisp (see these statistics). To be able to locate them, it’s
necessary to grab a tarball of the sources and put it into a specific
location. To recreate these, grab the latest tarball from
http://ftp.gnu.org/gnu/emacs/, extract its contents and put the src
directory into ~/.emacs.d
, then customize the following variable.
(setq find-function-C-source-directory "~/.emacs.d/src")
Per default you’re required to type out a full “yes” or “no” whenever
the function yes-or-no-p
is invoked, let’s substitute its function
definition to allow a “y” or “n” without even requiring confirmation.
(fset 'yes-or-no-p 'y-or-n-p)
I’ve set up xdg-open
to use my prefered browser for HTTP and HTTPS
URLs. Emacs claims to detect whether my system can use it, however
this fails because I don’t have a popular DE up and running (I kid you
not, look at browse-url-can-use-xdg
and how it replicates that part
from the xdg-open
script).
(setq browse-url-browser-function 'browse-url-xdg-open)
I have no idea how this actually works, but it seems to make Emacs prefer doing a horizontal split over a vertical split on wide screens.
(setq split-height-threshold 0
split-width-threshold 0)
This shouldn’t be necessary since I’m already using smart-mode-line
,
however it’s better to use a less confusing style than the default
that puts brackets around the buffer names shared in Emacs.
(setq uniquify-buffer-name-style 'forward)
For whatever reason the customization system will write into your init file which is especially annoying if you have it in version control like I do. It’s reasonably simple to deactivate this behaviour by customizing customize into using a dedicated file, however you’ll need to both delete the lines it wrote and load it afterwards to make it aware it has already been loaded successfully.
(setq custom-file "~/.emacs.d/etc/custom.el")
(load custom-file)
Since Emacs auto-detection of encodings is quite good, but not omniscient, we’ll give it a nudge to display these files the way they’re supposed to be.
(add-to-list 'auto-coding-alist '("\\.nfo\\'" . ibm437))
Half-page scrolling is great at reducing bandwidth, but is very jarring when done automatically. The following settings will make Emacs scroll line by line, without scrolloff and try to keep point at the same visual place when scrolling by page.
I used to have scrolloff enabled here with the scroll-margin
variable, but it introduced pretty nasty scrolling behaviour for large
files, so I no longer do.
(setq scroll-conservatively 10000
scroll-preserve-screen-position t)
Most programming languages I work with prefer spaces over tabs.
Note how this is not a mode, but a buffer-local variable.
(setq-default indent-tabs-mode nil)
Backup files are created on save in the same directory as the file and
end in ~
. They can be numbered which makes most sense combined with
a different save location and automatic pruning.
(setq backup-directory-alist '((".*" . "~/.emacs.d/backup")))
(setq version-control t)
(setq delete-old-versions t)
Autosave files are created between saves after a sufficient timeout in
the current directory for crash detection, they begin and end with
#
. Let’s change their save location as well.
(setq auto-save-list-file-prefix "~/.emacs.d/autosave/")
(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/autosave/" t)))
Keep in mind that there is nothing you can do regarding lock files
except deactivating them completely (which robs you of the ability to
detect session clashes). They are symlinks that are created upon
modification of the file in question in its directory and are prefixed
by .#
. Saving the file makes them disappear (unlike autosave
files).
Although I’m pretty sure I won’t make use of this, I prefer using
local TCP connections over socket files. Another benefit of this
setting is that it would allow me to make use of emacsclient
to access a remote Emacs daemon.
(setq server-use-tcp t)
Middle-clicking is nice to paste, however it should not adjust point and paste at the then adjusted point.
(setq mouse-yank-at-point t)
The default is to display the invocation name and host. Changing that
to use a different separator and the buffer name is trivial, however
there’s still an annoying space in front when using M-:
. Regular
expressions to the rescue!
(setq frame-title-format
'("" invocation-name ": " (:eval (replace-regexp-in-string
"^ +" "" (buffer-name)))))
This will be done by a different package anyways, therefore we don’t need it.
(setq blink-matching-paren nil)
It’s pretty nice to have the option to display words in a buffer as if they were hardwrapped around the word boundaries, however it’s confusing to not have any fringe indicators.
(setq visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow))
I don’t like the remappings done to operate on visual lines (for
C-a
, C-e
and C-k
), so I’m just undefining them.
(setcdr visual-line-mode-map nil)
The rationale for this default seems to be to avoid confusion for beginners, I personally find it kind of annoying that Emacs of all editors does this kind of thing and doesn’t offer a straightforward option to disable it even.
(setq disabled-command-function nil)
I hope the necessity of this will be gone once Wayland is a viable option for me.
(setq save-interprogram-paste-before-kill t)
Try it out yourself by hitting C-l
, it will start with the top
instead of the middle row.
(setq recenter-positions '(top middle bottom))
A lesser known fact is that sending the USR2
signal to an Emacs
process makes it proceed as soon as possible to a debug window.
USR1
is ignored however, so let’s bind it to an alternative
desirable function that can be used on an Emacs instance that has
locked up.
(defun my-quit-emacs-unconditionally ()
(interactive)
(my-quit-emacs '(4)))
(define-key special-event-map (kbd "<sigusr1>") 'my-quit-emacs-unconditionally)
Clicking on an install button for instance makes Emacs spawn dialog boxes from that point on.
(setq use-dialog-box nil)
This includes stuff that is bundled with Emacs and can be obtained
from a more recent source as well, such as org-mode
. I’m mostly
refering to smaller packages though.
recentf-mode
allows you to access the list of recent files which can
be used by ido
and helm
. Let’s save its file somewhere else and
change the size of its history while we’re at it.
(setq recentf-save-file "~/.emacs.d/etc/recentf"
recentf-max-saved-items 50)
The history of prompts like M-:
can be saved, but let’s change its
save file and history length first.
(setq savehist-file "~/.emacs.d/etc/savehist"
history-length 150)
I didn’t expect to like this functionality, but it’s pretty neat to start from the last place you were in a file the next time you visit it. Asides from putting the save file somewhere else, I have to enable this behaviour for every buffer since it’s buffer-local.
(setq-default save-place t)
(setq save-place-file "~/.emacs.d/etc/saveplace")
The windmove
provides useful commands for moving window focus by
direction, I prefer having wraparound instead of getting errors
though.
(setq windmove-wrap-around t)
Yet another file that I prefer being saved somewhere else.
(setq bookmark-default-file "~/.emacs.d/etc/bookmarks")
Anything else than emacsclient
spawning frames is pretty much
useless for me with i3
. I assume the vertical split is not done
because I’ve customized horizontal splits to be prefered. The name of
the alternative splitting function is not a mistake, what Emacs calls
“horizontal” in window.el
is called vertical in anything else.
(setq ediff-window-setup-function 'ediff-setup-windows-plain
ediff-split-window-function 'split-window-horizontally)
The debugger does display only the position of point when evaluating
buffers, the following rendition of debug-setup-buffer
displays a
line number as well.
(with-eval-after-load 'debug
(defun debugger-setup-buffer (debugger-args)
"Initialize the `*Backtrace*' buffer for entry to the debugger.
That buffer should be current already."
(setq buffer-read-only nil)
(erase-buffer)
(set-buffer-multibyte t) ;Why was it nil ? -stef
(setq buffer-undo-list t)
(let ((standard-output (current-buffer))
(print-escape-newlines t)
(print-level 8)
(print-length 50))
(backtrace))
(goto-char (point-min))
(delete-region (point)
(progn
(search-forward "\n debug(")
(forward-line (if (eq (car debugger-args) 'debug)
2 ; Remove implement-debug-on-entry frame.
1))
(point)))
(insert "Debugger entered")
;; lambda is for debug-on-call when a function call is next.
;; debug is for debug-on-entry function called.
(pcase (car debugger-args)
((or `lambda `debug)
(insert "--entering a function:\n"))
;; Exiting a function.
(`exit
(insert "--returning value: ")
(setq debugger-value (nth 1 debugger-args))
(prin1 debugger-value (current-buffer))
(insert ?\n)
(delete-char 1)
(insert ? )
(beginning-of-line))
;; Debugger entered for an error.
(`error
(insert "--Lisp error: ")
(prin1 (nth 1 debugger-args) (current-buffer))
(insert ?\n))
;; debug-on-call, when the next thing is an eval.
(`t
(insert "--beginning evaluation of function call form:\n"))
;; User calls debug directly.
(_
(insert ": ")
(prin1 (if (eq (car debugger-args) 'nil)
(cdr debugger-args) debugger-args)
(current-buffer))
(insert ?\n)))
;; After any frame that uses eval-buffer,
;; insert a line that states the buffer position it's reading at.
(save-excursion
(let ((tem eval-buffer-list))
(while (and tem
(re-search-forward "^ eval-\\(buffer\\|region\\)(" nil t))
(beginning-of-line)
(insert (format "Error at line %d in %s: "
(with-current-buffer (car tem)
(line-number-at-pos (point)))
(with-current-buffer (car tem)
(buffer-name))))
(pop tem))))
(debugger-make-xrefs)))
For the few times I’m using Dired, I prefer it not spawning an endless amount of buffers. In fact, I’d prefer it using one buffer unless another one is explicitly created, but you can’t have everything.
(with-eval-after-load 'dired
(define-key dired-mode-map (kbd "RET") 'dired-find-alternate-file))
If TRAMP makes backup files, they should better be kept locally than remote.
(setq tramp-backup-directory-alist backup-directory-alist)
As usual I want to fix up the file it’s storing its history in.
(with-eval-after-load 'tramp-cache
(setq tramp-persistency-file-name "~/.emacs.d/etc/tramp"))
But to be honest, I prefer it not automatically interfering with everything. Unloading it entirely causes packages to break that assume it’s enabled, therefore I’m going for its main entry point and dike it out.
(defun my-disable-tramp-file-handlers ()
(setq file-name-handler-alist
(--remove (string-match-p "^tramp" (symbol-name (cdr it)))
file-name-handler-alist)))
General functionality for calendars inside Emacs, split up in a lot of files. Customizing it will affect other packages, including calfw. The following customizations make it appear german (since I happen to live in Germany, d’uh).
(setq calendar-week-start-day 1
calendar-day-name-array ["Sonntag" "Montag" "Dienstag" "Mittwoch"
"Donnerstag" "Freitag" "Samstag"]
calendar-month-name-array ["Januar" "Februar" "März" "April" "Mai"
"Juni" "Juli" "August" "September"
"Oktober" "November" "Dezember"])
(setq solar-n-hemi-seasons
'("Frühlingsanfang" "Sommeranfang" "Herbstanfang" "Winteranfang"))
(setq holiday-general-holidays
'((holiday-fixed 1 1 "Neujahr")
(holiday-fixed 5 1 "1. Mai")
(holiday-fixed 10 3 "Tag der Deutschen Einheit")))
(setq holiday-christian-holidays
'((holiday-float 12 0 -4 "1. Advent" 24)
(holiday-float 12 0 -3 "2. Advent" 24)
(holiday-float 12 0 -2 "3. Advent" 24)
(holiday-float 12 0 -1 "4. Advent" 24)
(holiday-fixed 12 24 "Weihnachten")
(holiday-fixed 12 25 "1. Weihnachtstag")
(holiday-fixed 12 26 "2. Weihnachtstag")
(holiday-fixed 1 6 "Heilige Drei Könige")
(holiday-easter-etc -48 "Rosenmontag")
(holiday-easter-etc -3 "Gründonnerstag")
(holiday-easter-etc -2 "Karfreitag")
(holiday-easter-etc 0 "Ostersonntag")
(holiday-easter-etc +1 "Ostermontag")
(holiday-easter-etc +39 "Christi Himmelfahrt")
(holiday-easter-etc +49 "Pfingstsonntag")
(holiday-easter-etc +50 "Pfingstmontag")
(holiday-easter-etc +60 "Fronleichnam")
(holiday-fixed 8 15 "Mariae Himmelfahrt")
(holiday-fixed 11 1 "Allerheiligen")
(holiday-float 11 0 1 "Totensonntag" 20)))
(setq holiday-oriental-holidays nil
holiday-bahai-holidays nil
holiday-islamic-holidays nil
holiday-hebrew-holidays nil)
First some UI and editing tweaks.
(setq org-catch-invisible-edits 'error
org-startup-indented t
org-cycle-include-plain-lists 'integrate
org-ellipsis " […]"
org-return-follows-link t
org-M-RET-may-split-line nil
org-src-fontify-natively t
org-src-preserve-indentation t
org-enforce-todo-dependencies t
org-enforce-todo-checkbox-dependencies t
org-link-frame-setup '((file . find-file)))
I like taking notes and sometimes even take a look at the agenda.
(setq org-directory "~/org/"
org-agenda-files (list org-directory)
org-default-notes-file "~/org/inbox.org"
org-capture-templates
'(("n" "Note" entry (file+headline "~/org/inbox.org" "Inbox")
"* TODO %<%Y-%m-%d %H:%M:%S>\n\n%?" :empty-lines 1)
("p" "PW" entry (file+headline "~/org/pw.org" "PW")
"* TODO %<%Y-%m-%d %H:%M:%S>\n\n%?" :empty-lines 1)
("w" "Work" entry (file+datetree "~/org/work.org")
"* %<%H:%M>\n\n%?" :empty-lines 1)
("j" "Journal" entry (file+datetree "~/org/journal.org")
"* %<%H:%M>\n\n%?" :empty-lines 1)))
To keep track how much I wrote when taking a note, I enable a word counting minor mode. Upstream didn’t autoload its entry point for Reasons™ which is why I do that myself.
(autoload 'wc-mode "wc-mode" "Enable wc-mode" t)
(add-hook 'org-capture-mode-hook 'wc-mode)
The export functionality is very handy, but some of the stuff I like using is deactivated by default :<
(setq org-export-backends '(ascii beamer html latex md))
It’s a bit tricky to color code listings and permit more flexible tables:
(setq org-latex-listings 'minted
org-latex-packages-alist '(("" "tabu") ("" "minted"))
org-latex-pdf-process
'("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
Here comes another particularly interesting Emacs package. It allows
one to define major modes interacting with a REPL-style process. In
other words, it gives you all kinds of shell and interpreter
interaction with common keybindings, be it for SQL, your favourite
programming language or your shell. Even Emacs itself can be used,
try out M-x ielm
.
However there’s a couple things that could be improved. One of them is the fact that by default such buffers are editable. The prompt can be customized easily to be read-only, the remaining output needs a bit more work.
(setq comint-prompt-read-only t)
(defun my-comint-preoutput-read-only (text)
(propertize text 'read-only t))
(add-hook 'comint-preoutput-filter-functions
'my-comint-preoutput-read-only)
While it would be better to patch comint-previous-input
(which is
used by comint-next-input
with a negative argument, so don’t worry)
to take a customizable value that determines whether to wrap around or
not, I’ve hacked it into just wrapping around for simplicity’s sake.
(defun comint-previous-input (arg)
"Cycle backwards with wrap-around through input history, saving input."
(interactive "*p")
(unless (and (eq comint-input-ring-index nil)
(< arg 0))
(if (and (eq comint-input-ring-index 0)
(< arg 0)
comint-stored-incomplete-input)
(comint-restore-input)
(unless (and (eq comint-input-ring-index
(- (ring-length comint-input-ring) 1))
(> arg 0))
(comint-previous-matching-input "." arg)))))
It’s trivial to clear the entire comint
buffer by temporarily
binding comint-buffer-maximum-size
to zero and calling
comint-truncate-buffer
, however that’s not what I really want.
Usually it’s just the output of the last expression that’s been
faulty and needs to be cleared by replacing it with a comment. The
idea itself is taken from CIDER.
(defun my-comint-last-output-beg ()
(save-excursion
(comint-goto-process-mark)
(while (not (or (eq (get-char-property (point) 'field) 'boundary)
(= (point) (point-min))))
(goto-char (previous-char-property-change (point) (point-min))))
(if (= (point) (point-min))
(point)
(1+ (point)))))
(defun my-comint-last-output-end ()
(save-excursion
(comint-goto-process-mark)
(while (not (or (eq (get-char-property (point) 'font-lock-face)
'comint-highlight-prompt)
(= (point) (point-min))))
(goto-char (previous-char-property-change (point) (point-min))))
(let ((overlay (car (overlays-at (point)))))
(when (and overlay (eq (overlay-get overlay 'font-lock-face)
'comint-highlight-prompt))
(goto-char (overlay-start overlay))))
(1- (point))))
(defun my-comint-clear-last-output ()
(interactive)
(let ((start (my-comint-last-output-beg))
(end (my-comint-last-output-end)))
(let ((inhibit-read-only t))
(delete-region start end)
(save-excursion
(goto-char start)
(insert (propertize "output cleared"
'font-lock-face 'font-lock-comment-face))))))
Killed comint
processes tend to leave an useless buffer around.
Let’s kill it after noticing such an event with a process sentinel.
(defun my-shell-kill-buffer-sentinel (process event)
(when (and (memq (process-status process) '(exit signal))
(buffer-live-p (process-buffer process)))
(kill-buffer)))
(defun my-kill-process-buffer-on-exit ()
(set-process-sentinel (get-buffer-process (current-buffer))
#'my-shell-kill-buffer-sentinel))
(dolist (hook '(ielm-mode-hook term-exec-hook comint-exec-hook))
(add-hook hook 'my-kill-process-buffer-on-exit))
Recentering feels a bit unintuitive since it goes by the middle first. I only need top and bottom commands, for that I’ll define my own command and bind it later.
(defun my-recenter-top-bottom ()
(interactive)
(goto-char (point-max))
(let ((recenter-positions '(top bottom)))
(recenter-top-bottom)))
Another thing annoying me in comint buffers is that when text is read-only, both cursor movement and appending to kill ring still happen. This is less useful since if you keep holding the keys to delete words, you end up traversing the entire buffer instead of stopping at the read-only boundaries and pollute the kill ring. To remedy that I’ll write my own word killing commands in the typical Emacs user fashion, however I’ll not advise the built-ins since who knows what might possibly be relying on this default behaviour.
(defun my-kill-word (arg)
(interactive "p")
(unless buffer-read-only
(let ((beg (point))
(end (save-excursion (forward-word arg) (point)))
(point (save-excursion (goto-char
(if (> arg 0)
(next-single-char-property-change
(point) 'read-only)
(previous-single-char-property-change
(point) 'read-only)))
(point))))
(unless (get-char-property (point) 'read-only)
(if (if (> arg 0) (< point end) (> point end))
(kill-region beg point)
(kill-region beg end))))))
(defun my-backward-kill-word (arg)
(interactive "p")
(my-kill-word (- arg)))
The new functionality introduced has to be bound to keys for convenient use. Note the remapping of commands.
(with-eval-after-load 'comint
(define-key comint-mode-map (kbd "<remap> <kill-word>") 'my-kill-word)
(define-key comint-mode-map (kbd "<remap> <backward-kill-word>") 'my-backward-kill-word)
(define-key comint-mode-map (kbd "C-S-l") 'my-comint-clear-last-output)
(define-key comint-mode-map (kbd "C-l") 'my-recenter-top-bottom))
For unknown reasons I get my input echoed back to me. In other words,
sending ls
to shell
echoes my input twice, then the output.
comint
has a setting that can filter these echoes.
(defun my-shell-turn-echo-off ()
(setq comint-process-echoes t))
(add-hook 'shell-mode-hook 'my-shell-turn-echo-off)
I want C-d
to not unconditionally delete the character, but to quit
on an empty prompt, too.
(defun my-eshell-quit-or-delete-char (arg)
(interactive "p")
(if (and (eolp) (looking-back eshell-prompt-regexp))
(eshell-life-is-too-much) ;; http://emacshorrors.com/post/life-is-too-much
(delete-forward-char arg)))
(defun my-eshell-setup ()
(define-key eshell-mode-map (kbd "C-d") 'my-eshell-quit-or-delete-char))
(add-hook 'eshell-mode-hook 'my-eshell-setup)
For silly reasons I like having a rainbow-colored prompt.
(add-hook 'eshell-load-hook 'nyan-prompt-enable)
In their ingenuity the Emacs developers decided to make the GNU style the default style for C code written with it. While this is a decision that helps making contribution to GNU projects still adhering to this style (including Emacs itself) a fair bit easier, I’d hate using it for anything else. I don’t know my exact preferences yet, but for the time being the “user” style is good enough and can still be customized into something more sophisticated.
(setq c-default-style '((java-mode . "java")
(awk-mode . "awk")
(c-mode . "user")))
The default idle delay is way too long. Also, avoid displaying overly long function signatures.
(setq eldoc-idle-delay 0.1
eldoc-echo-area-use-multiline-p nil)
Cask files are just Emacs Lisp.
(add-to-list 'auto-mode-alist '("Cask\\'" . emacs-lisp-mode))
Additionally to the F1
keybindings I’d like to have two extra
keybinds for evaluation and a REPL.
(defun my-eval-region-or-buffer ()
(interactive)
(if (region-active-p)
(eval-region (region-beginning) (region-end))
(eval-buffer)))
(with-eval-after-load 'lisp-mode
(define-key emacs-lisp-mode-map (kbd "C-c C-c") 'my-eval-region-or-buffer)
(define-key emacs-lisp-mode-map (kbd "C-c C-z") 'ielm))
eldoc
is a nice helper to avoid looking up function signatures in
function documentation.
(add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
(add-hook 'ielm-mode-hook 'turn-on-eldoc-mode)
I like CHICKEN.
(setq scheme-program-name "csi")
(add-to-list 'interpreter-mode-alist '("chicken-scheme" . scheme-mode))
To avoid typing M-x run-scheme
, I define another useful keybinding.
(with-eval-after-load 'scheme
(define-key scheme-mode-map (kbd "C-c C-z") 'run-scheme))
The binding is replaced though after launching the REPL, I should eventually fix this. Perhaps with my very own major mode.
Indentation hints fortunately seem to work for other languages than Emacs Lisp.
(put 'match 'scheme-indent-function 1)
(put 'match-let 'scheme-indent-function 1)
(put 'match-let* 'scheme-indent-function 1)
(put 'when 'scheme-indent-function 1)
(put 'and-let* 'scheme-indent-function 1)
(put 'if-let 'scheme-indent-function 1)
(put 'let-location 'scheme-indent-function 1)
(put 'select 'scheme-indent-function 1)
(put 'bitmatch 'scheme-indent-function 1)
(put 'bitpacket 'scheme-indent-function 1)
(put 'with-transaction 'scheme-indent-function 1)
(put 'foreign-lambda* 'scheme-indent-function 2)
There’s a few schemey file formats I’d like to automatically recognize:
(add-to-list 'auto-mode-alist '("\\.sxml\\'" . scheme-mode))
(add-to-list 'auto-mode-alist '("\\.scss\\'" . scheme-mode))
(add-to-list 'auto-mode-alist '("\\.setup\\'" . scheme-mode))
(add-to-list 'auto-mode-alist '("\\.meta\\'" . scheme-mode))
(add-to-list 'auto-mode-alist '("\\.release-info\\'" . scheme-mode))
I like SBCL.
(setq inferior-lisp-program "/usr/bin/sbcl")
Let’s automatically complete closing tags.
(setq nxml-slash-auto-complete-flag t)
Indentation could be a bit more narrow.
(setq css-indent-offset 2)
Emacs is not aware of version-dependent shebangs.
(add-to-list 'interpreter-mode-alist '("python2" . python-mode))
(add-to-list 'interpreter-mode-alist '("python3" . python-mode))
For some reason guessing the indentation offset is on by default although nearly all Python code I’ve worked with did use 4 spaces. I wouldn’t even care weren’t it for the message displayed after it’s done.
(setq python-indent-guess-indent-offset nil)
Please don’t litter my home directory with a score file.
(setq toe-highscore-file "~/.emacs.d/etc/toe.score")
Interactive preview for RE construction.
It’s important to note that there’s three flavours of regular
expressions encountered in Emacs. The read
syntax is most
reminiscent of other RE dialects, but only used in prompts. The
string
syntax is used in code doubles the amount of backslashes as
the RE strings are passed through the reader which removes the
extraneous ones. Finally, there’s the rx
macro one can use for
writing lispy RE.
All listed RE syntaxes are supported by re-builder
. For whatever
reason though the read
syntax is default (which doesn’t make much
sense for me since Evil gives me search/replace preview), I prefer
having the string
syntax as default.
(setq reb-re-syntax 'string)
I’ve created a TAGS
file for finding the definitions to the C
sources quickly. To avoid prompting for its name, one can customize
the following:
(setq tags-file-name "TAGS")
Make copying use the lispy syntax by default and with a normal syntax argument copy the HTML link.
(defun my-info-copy-current-node-name (arg)
"Copy the lispy form of the current node.
With a prefix argument, copy the link to the online manual
instead."
(interactive "P")
(let* ((manual (file-name-sans-extension
(file-name-nondirectory Info-current-file)))
(node Info-current-node)
(link (if (not arg)
(format "(info \"(%s) %s\")" manual node)
;; NOTE this will only work with emacs-related nodes...
(format "https://www.gnu.org/software/emacs/manual/html_node/%s/%s.html"
manual (if (string= node "Top")
"index"
(replace-regexp-in-string " " "-" node))))))
(kill-new link)
(message link)))
(with-eval-after-load 'info
(define-key Info-mode-map (kbd "c") 'my-info-copy-current-node-name))
I’ll just pretend that mdoc is the same as nroff:
(add-to-list 'auto-mode-alist '("\\.mdoc\\'" . nroff-mode))
Welcome to the blind spot of emacs-devel
. Unlike the people on
there, I’ll not pretend external packages are something to speak of in
hushed tones.
Nice improvement over vanilla M-x
that gives you persistency and
better matching. Let’s give it more history and a different file.
(setq smex-save-file (concat user-emacs-directory "etc/smex")
smex-history-length 50)
After installing csv-mode from GNU ELPA, I found out it’s using a
:set
form in its customization option for the separators, therefore
I had to figure out what “internal” variables they were setting and
customized them.
(setq csv-separators '(";" " " ",")
csv-separator-chars '(?\; ? ?,)
csv--skip-regexp "^
; ,"
csv-separator-regexp "[; ,]"
csv-font-lock-keywords '(("[; ,]" (0 'csv-separator-face))))
A client-side MELPA. Hugely useful for development, also useful to obtain packages that are not there or need to be built differently from what it offers. vim-plug comes close, but the closest equivalent to it would be the makepkg utility.
This customization is necessary to have updates of packages happen, even if they already exist.
(setq quelpa-upgrade-p t)
Declarative popup window rules.
(setq shackle-rules
'(((svg-2048-mode circe-query-mode) :same t)
("*Help*" :align t :select t)
("\\`\\*helm.*?\\*\\'" :regexp t :align t)
((compilation-mode "\\`\\*firestarter\\*\\'"
"\\`\\*magit-diff: .*?\\'") :regexp t :noselect t)
("\\`\\*cider-repl .*" :regexp t :align t :size 0.2)
((inferior-scheme-mode "*shell*" "*eshell*") :popup t))
shackle-default-rule '(:select t)
shackle-default-size 0.4
shackle-inhibit-window-quit-on-same-windows t)
Less clumsy management of window configurations.
Switch back and forth just like my i3wm configuration, wrap around, too.
(setq eyebrowse-switch-back-and-forth t
eyebrowse-wrap-around t)
The best auto-completion mode we have out there.
The following sets up a good amount of UI tweaks and everything necessary for the global backends.
(setq company-idle-delay 0.1
company-minimum-prefix-length 2
company-selection-wrap-around t
company-show-numbers t
company-require-match 'never
company-dabbrev-downcase nil
company-dabbrev-ignore-case t
company-backends '(company-jedi company-nxml
company-css company-capf
(company-dabbrev-code company-keywords)
company-files company-dabbrev)
company-jedi-python-bin "python")
Sometimes it’s useful to narrow down the candidate list if it’s overly
long with something better than C-s
.
(with-eval-after-load 'company
(define-key company-active-map (kbd "C-:") 'helm-company))
Hitting ESC
does exit Evil’s insert state (which is where I’m
usually in when typing completable text), but still keeps the popup
open. A similiar problem applies to the candidate search, so here’s a
workaround for both:
(defun my-company-abort ()
(interactive)
(company-abort)
(when (and (bound-and-true-p evil-mode)
(eq evil-state 'insert))
(evil-force-normal-state)))
(with-eval-after-load 'company
(define-key company-active-map (kbd "<escape>") 'my-company-abort)
(define-key company-search-map (kbd "<escape>") 'company-search-abort))
I’ve transitioned from auto-complete-mode so I’m missing its selection behaviour. Company is not quite there yet, but this remapping helps:
(with-eval-after-load 'company
(define-key company-active-map (kbd "TAB") 'company-complete-common-or-cycle)
(define-key company-active-map (kbd "<tab>") 'company-complete-common-or-cycle)
(define-key company-active-map (kbd "S-TAB") 'company-select-previous)
(define-key company-active-map (kbd "<backtab>") 'company-select-previous))
Other helpful settings involve the frontends to preview the current
candidate inline and triggering completion of it on a few selected
keys, including SPC
:
(setq company-frontends
'(company-pseudo-tooltip-unless-just-one-frontend
company-echo-metadata-frontend
company-preview-frontend)
company-auto-complete t)
Very useful library, too bad I don’t know how to properly use it yet. Since it’s sprinkled all over in code I’d like to have extra syntax highlighting for it.
(with-eval-after-load 'dash
(dash-enable-font-lock))
A polarizing package to say the least. The good part of it is that it actually tries enabling abstractions over complex selection UI. The bad part is that it’s overly complex, hard to debug and prone to bizarre behaviour. I’ve handed in ten bugs for it already and don’t expect those to be the last. With that being said I find it essential to quickly find your way through Emacs, I just wish it were less idiosyncratic and with developer documentation.
The default navigation isn’t as fast as it could be. Automatically
switching directories is a must for me. Note the hack with
helm-ff--auto-update-state
, it’s supposedly internal, but only set
after using helm-find-files
which essentially means that everything
using the file selector won’t get the auto-switching goodies unless a
file has been found before. With this hack however it will. The
other hack goes beyond the helm-ff-ido-style-backspace
customization
and unconditionally enables backspace going up one level in both kinds
of file selectors.
(setq helm-ff-ido-style-backspace 'always
helm-ff-auto-update-initial-value t
helm-ff--auto-update-state t)
(with-eval-after-load 'helm-files
(define-key helm-read-file-map (kbd "<backspace>") 'helm-find-files-up-one-level)
(define-key helm-find-files-map (kbd "<backspace>") 'helm-find-files-up-one-level))
There are more idiosyncracies to be resolved with file selection. I don’t want to see boring files and not get prompted for creating a new file either. The creation of a new directory however is kept as is.
(setq helm-ff-newfile-prompt-p nil
helm-ff-skip-boring-files t)
grep is very fast, but not the best tool for code search, especially
not within compressed files. That’s why I’ll go for ag instead, its
-z
option enables the usage of the very great libarchive. For
helm
to recognize the matches properly I need to enable line numbers
and columns in its output, something the --vimgrep=
option (the
irony) does. Another subtle hack hidden in here is deliberately using
the recursing variant for both types of searches, this might break
something, but so far hasn’t shown any obvious side-effects.
(setq helm-grep-default-command "ag --vimgrep -z %p %f"
helm-grep-default-recurse-command "ag --vimgrep -z %p %f")
Here’s two commands for pretty common queries, one going through the official Emacs Lisp sources, the other through the C parts:
(defun my-grep-emacs-elisp ()
(interactive)
(helm-do-grep-1 '("/usr/share/emacs/*/lisp/*.el.gz"
"/usr/share/emacs/*/lisp/*/*.el.gz")))
(defun my-grep-emacs-C ()
(interactive)
(helm-do-grep-1 '("~/.emacs.d/src/*.c" "~/.emacs.d/src/*.h")))
For whatever reason find-library
isn’t used properly with
helm-mode
enabled, adding a read handler fixes this.
(with-eval-after-load 'helm-mode
(add-to-list 'helm-completing-read-handlers-alist
'(find-library . helm-completing-read-with-cands-in-buffer)))
I dislike helm
taking over tab-completion in my IRC client.
(setq helm-mode-no-completion-in-region-in-modes
'(circe-channel-mode
circe-query-mode
circe-server-mode))
Highlighting of token matches is a tad slow, let’s speed it up.
(setq helm-mp-highlight-delay 0.3)
I like having my dotfiles repo as default when using helm-cmd-t
on a
directory that’s not under version-control.
(setq helm-cmd-t-default-repo "~/code/dotfiles")
I don’t know why, but helm tries doing window management. Please stop:
(setq helm-display-function 'pop-to-buffer)
(with-eval-after-load 'helm
(defun my-helm-rdictcc ()
(interactive)
(helm :sources 'my-helm-rdictcc-source
:buffer "*helm rdictcc*"))
(defvar my-helm-rdictcc-source
(helm-build-async-source "rdictcc"
:candidates-process 'my-helm-rdictcc-process
:candidate-number-limit 99
:filtered-candidate-transformer 'my-helm-rdictcc-transformer
:requires-pattern 3))
(defun my-helm-rdictcc-process ()
(let ((proc (start-process "rdictcc" helm-buffer "rdictcc" "-c" helm-pattern)))
(set-process-sentinel
proc
(lambda (process event)
(helm-process-deferred-sentinel-hook process event default-directory)))
proc))
(defun my-helm-rdictcc-transformer (candidates _source)
(let (result)
(dolist (candidate candidates)
(when (string-match-p "=\\{20\\}\\[ [AB] => [AB] \\]=\\{20\\}" candidate)
(add-face-text-property 0 (length candidate) 'font-lock-comment-face
nil candidate))
(push candidate result))
(nreverse result))))
There’s a few languages I like having linting for, see Hooks.
Additionally to that there’s few things to tweak. For one I prever
the tex-lacheck
linter over the default tex-chktex
linter and
don’t want to use the emacs-lisp-checkdoc
one at all, another thing
is that I don’t want linting to start on an idle timer, but rather on
opening the buffer and saving it to disk.
(setq flycheck-disabled-checkers '(tex-chktex emacs-lisp-checkdoc)
flycheck-check-syntax-automatically '(mode-enabled save))
For whatever reason the emacs-lisp
checker stopped unconditionally
initializing packages before doing the check, the following avoids
errors for dependencies in packages I write:
(setq flycheck-emacs-lisp-initialize-packages t)
Not sure how to describe it. A library for defining key-centric interfaces? You use it to execute commands with single-key presses first and foremost, I have only come to define repetition-free ones.
(defun my-zsh ()
(interactive)
(ansi-term "zsh"))
(defun my-info-emacs-lisp-intro ()
(interactive)
(info "eintr"))
(defun my-info-emacs-lisp-manual ()
(interactive)
(info "elisp"))
(defun my-info-cl ()
(interactive)
(info "cl"))
(defun my-info-cl-loop ()
(interactive)
(info "(cl) Loop facility"))
(defun my-open-r5rs ()
(interactive)
(eww-open-file
"~/.usr/share/chicken/doc/manual/The R5RS standard.html"))
(defun my-capture-journal ()
(interactive)
(org-capture nil "j"))
(defun my-capture-note ()
(interactive)
(org-capture nil "n"))
(defun my-capture-pw ()
(interactive)
(org-capture nil "p"))
(defun my-capture-work ()
(interactive)
(org-capture nil "w"))
(defun my-open-inbox ()
(interactive)
(find-file "~/org/inbox.org"))
(defun my-open-journal ()
(interactive)
(find-file "~/org/journal.org"))
(defun my-open-pw ()
(interactive)
(find-file "~/org/pw.org"))
(defun my-open-tracking ()
(interactive)
(find-file "~/org/tracking.org"))
(autoload 'cfw:open-org-calendar "calfw-org" "Open Org calendar" t)
This is used in after-init-hook
.
(defun my-setup-hydra ()
(global-set-key
(kbd "<f1>")
(defhydra hydra-help (:color blue)
"Help"
("a" helm-apropos "Apropos")
("c" describe-char "Describe Char")
("f" find-function "Find Function")
("F" describe-function "Describe Function")
("k" describe-key "Describe Key")
("K" find-function-on-key "Find Key")
("m" describe-mode "Describe Modes")
("v" find-variable "Find Variable")
("V" describe-variable "Describe Variable")))
(global-set-key
(kbd "<f2>")
(defhydra hydra-packages (:color blue)
"Packages"
("c" helm-colors "Colors")
("f" find-library "Find Library")
("g" customize-group "Customize Group")
("i" package-install "Package Install")
("p" package-list-packages "Package List")
("q" quelpa "Quelpa")
("t" helm-themes "Load Theme")
("v" customize-variable "Customize Variable")))
(global-set-key
(kbd "<f3>")
(defhydra hydra-search (:color blue)
"Search"
("a" helm-imenu-anywhere "Imenu Anywhere")
("e" my-grep-emacs-elisp "Grep Emacs Elisp")
("E" my-grep-emacs-C "Grep Emacs C")
("g" helm-do-grep "Grep")
("h" helm-org-headlines "Org Headlines")
("i" helm-imenu "Imenu")
("m" helm-multi-occur "Multi-occur")
("o" helm-occur "Occur")))
(global-set-key
(kbd "<f4>")
(defhydra hydra-find (:color blue)
"Find"
("b" helm-buffers-list "Buffers")
("f" helm-find "Find")
("i" helm-find-files "Find Files")
("l" helm-locate "Locate")
("t" helm-cmd-t "Cmd-T")))
(global-set-key
(kbd "<f5>")
(defhydra hydra-eval (:color blue)
"Eval"
("c" calc "Calc")
("e" eshell "Eshell")
("g" magit-status "Magit")
("i" ielm "IELM")
("r" helm-regexp "Regexp")
("s" shell "Shell")
("t" my-zsh "Term")
("x" helm-calcul-expression "Calculate Expression")))
(global-set-key
(kbd "<f6>")
(defhydra hydra-doc (:color blue)
"Doc"
("c" my-info-cl "CL")
("e" info-emacs-manual "Emacs manual")
("i" info "Info")
("l" my-info-emacs-lisp-manual "Emacs Lisp manual")
("m" helm-man-woman "Man")
("o" my-info-cl-loop "LOOP")
("r" my-open-r5rs "R5RS")))
(global-set-key
(kbd "<f7>")
(defhydra hydra-zoom (:color blue)
"zoom"
("l" helm-insert-latex-math "LaTeX Math")
("u" helm-ucs "UCS")))
(global-set-key
(kbd "<f8>")
(defhydra hydra-misc (:color blue)
"Misc"
("g" helm-google-suggest "Google Suggest")
("p" helm-list-emacs-process "Emacs Process List")
("s" helm-surfraw "Surfraw")
("t" helm-top "Top")
("w" helm-world-time "World time")))
(global-set-key
(kbd "<f9>")
(defhydra hydra-distractions (:color blue)
"Distractions"
("i" my-irc "IRC")
("t" tetris "Tetris")))
(global-set-key
(kbd "<f11>")
(defhydra hydra-capture (:color blue)
"Org Capture"
("c" org-capture "Capture")
("j" my-capture-journal "Journal")
("n" my-capture-note "Note")
("p" my-capture-pw "PW")
("w" my-capture-work "Work")))
(global-set-key
(kbd "<f12>")
(defhydra hydra-lookup (:color blue)
"Org Lookup"
("c" cfw:open-org-calendar "Calendar")
("i" my-open-inbox "Inbox")
("j" my-open-journal "Journal")
("p" my-open-pw "PW")
("t" my-open-tracking "Tracking"))))
Clojure Interactive Development Environment that Rocks.
I like eldoc
for function signatures, hiding less interesting
buffers is also nice to have.
(add-hook 'nrepl-interaction-mode-hook 'nrepl-turn-on-eldoc-mode)
(setq nrepl-hide-special-buffers t)
The Superior Lisp Interaction Mode for Emacs.
Let’s fancy things up.
(setq slime-contribs '(slime-fancy))
An IDE-like mode for editing Javascript. Due to it actually parsing the code for highlighting and whatnot, it is used as dependency by a few other ones.
Not only CSS is using a low indentation width these days.
(setq js2-basic-offset 2)
Deals with all kinds of templates and other files with multiple modes one encounters in web development. Other than templates, I prefer using it for HTML these days.
(add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.tmpl\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode))
(setq web-mode-markup-indent-offset 2)
(setq web-mode-css-indent-offset 2)
(setq web-mode-code-indent-offset 2)
Used to be part of anaconda-mode which complements the built-in
python.el
with code navigation, documentation lookup and
auto-completion. I guess I should update soon.
Not to be confused with company-jedi which got factored out from jedi.el.
Bootstrap jedi automatically per file for completion.
(add-hook 'python-mode-hook 'company-jedi-start)
Tern is kind of cool. Don’t forget installing it via npm
, then
adding a .tern-project
file to your project root.
(add-hook 'js-mode-hook 'tern-mode)
(add-to-list 'company-backends 'company-tern)
I’m using this mode for everything the stock ruby-mode
would be used
for because it provides better syntax highlighting and indentation by
using an external process.
(add-to-list 'auto-mode-alist '("\\.rb\\'" . enh-ruby-mode))
(add-to-list 'auto-mode-alist '("Gemfile\\'" . enh-ruby-mode))
(add-to-list 'auto-mode-alist '("Rakefile\\'" . enh-ruby-mode))
(add-to-list 'auto-mode-alist '("\\.rake\\'" . enh-ruby-mode))
For deep indentation, allow bouncing towards a less deep level.
(setq enh-ruby-bounce-deep-indent t)
Because Emacs doesn’t come with M-x inf-ruby
(but has M-x run-python
).
pry is awesome and serves me as a better Ruby REPL.
(setq inf-ruby-default-implementation "pry")
Improves the standard editing facilities for all things TeX and LaTeX.
It’s 2015 and I prefer a TeX engine that can deal with Unicode and use any font I like.
(setq-default TeX-engine 'luatex)
Set up viewers and a few other things.
(setq TeX-quote-after-quote t
TeX-auto-save t
TeX-parse-self t
TeX-view-program-list '(("llpp" "llpp %o"))
TeX-view-program-selection '(((output-dvi style-pstricks)
"dvips and gv")
(output-dvi "xdvi")
(output-pdf "llpp")
(output-html "xdg-open")))
Enable PDF mode, enable folding and add a few convenience keybinds
(like C-c C-a
to run every command until the document can be
viewed).
(defun my-extend-hs-modes-alist ()
(add-to-list 'hs-special-modes-alist
`(latex-mode ,(latex/section-regexp) nil "%"
(lambda (arg) (latex/next-section 1)
(skip-chars-backward " \t\n")) nil)))
(autoload 'latex/section-regexp "latex-extra" "LaTeX section regexp" t)
(defun my-latex-setup ()
(TeX-PDF-mode)
(latex/setup-keybinds)
(my-extend-hs-modes-alist))
(add-hook 'LaTeX-mode-hook 'my-latex-setup)
helm-mode
enables more convenient completing-read
, however it’s a
bit silly that candidates for common AUCTEX functions aren’t required
matches.
(with-eval-after-load 'tex
(defun TeX-command-master (&optional override-confirm)
"Run command on the current document.
If a prefix argument OVERRIDE-CONFIRM is given, confirmation will
depend on it being positive instead of the entry in `TeX-command-list'."
(interactive "P")
(TeX-command (my-TeX-command-query (TeX-master-file)) 'TeX-master-file
override-confirm))
(defun TeX-command-query (name)
"Query the user for what TeX command to use."
(let* ((default
(cond ((if (string-equal name TeX-region)
(TeX-check-files (concat name "." (TeX-output-extension))
(list name)
TeX-file-extensions)
(TeX-save-document (TeX-master-file)))
TeX-command-default)
((and (memq major-mode '(doctex-mode latex-mode))
;; Want to know if bib file is newer than .bbl
;; We don't care whether the bib files are open in emacs
(TeX-check-files (concat name ".bbl")
(mapcar 'car
(LaTeX-bibliography-list))
(append BibTeX-file-extensions
TeX-Biber-file-extensions)))
;; We should check for bst files here as well.
(if LaTeX-using-Biber TeX-command-Biber TeX-command-BibTeX))
((TeX-process-get-variable name
'TeX-command-next
TeX-command-Show))
(TeX-command-Show)))
(completion-ignore-case t)
(answer (or TeX-command-force
(completing-read
(concat "Command: (default " default ") ")
(TeX-mode-specific-command-list major-mode) nil t
default 'TeX-command-history))))
;; If the answer is "latex" it will not be expanded to "LaTeX"
(setq answer (car-safe (TeX-assoc answer TeX-command-list)))
(if (and answer
(not (string-equal answer "")))
answer
default))))
(with-eval-after-load 'latex
(defun LaTeX-section-heading ()
"Hook to prompt for LaTeX section name.
Insert this hook into `LaTeX-section-hook' to allow the user to change
the name of the sectioning command inserted with `\\[LaTeX-section]'."
(let ((string (completing-read
(concat "Level: (default " name ") ")
LaTeX-section-list
nil nil name)))
; Update name
(if (not (zerop (length string)))
(setq name string))
; Update level
(setq level (LaTeX-section-level name))))
(defun LaTeX-environment (arg)
"Make LaTeX environment (\\begin{...}-\\end{...} pair).
With optional ARG, modify current environment.
It may be customized with the following variables:
`LaTeX-default-environment' Your favorite environment.
`LaTeX-default-style' Your favorite document class.
`LaTeX-default-options' Your favorite document class options.
`LaTeX-float' Where you want figures and tables to float.
`LaTeX-table-label' Your prefix to labels in tables.
`LaTeX-figure-label' Your prefix to labels in figures.
`LaTeX-default-format' Format for array and tabular.
`LaTeX-default-width' Width for minipage and tabular*.
`LaTeX-default-position' Position for array and tabular."
(interactive "*P")
(let ((environment (completing-read (concat "Environment type: (default "
(if (TeX-near-bobp)
"document"
LaTeX-default-environment)
") ")
(LaTeX-environment-list) nil t nil
'LaTeX-environment-history LaTeX-default-environment)))
;; Get default
(cond ((and (zerop (length environment))
(TeX-near-bobp))
(setq environment "document"))
((zerop (length environment))
(setq environment LaTeX-default-environment))
(t
(setq LaTeX-default-environment environment)))
(let ((entry (assoc environment (LaTeX-environment-list))))
(if (null entry)
(LaTeX-add-environments (list environment)))
(if arg
(LaTeX-modify-environment environment)
(LaTeX-environment-menu environment))))))
Promises to go beyond paredit (which is structured editing for Lisp code) by supporting other languages than Lisp-likes with arbitrary kinds of pairs. I only use its autopairing feature, pair highlighting and a bit of auto-indent though.
The following wall of code disables pairs for Lisp- and TeX-like modes that make absolutely no sense.
(with-eval-after-load 'smartparens
(sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil)
(sp-local-pair 'minibuffer-inactive-mode "`" nil :actions nil)
(sp-local-pair 'emacs-lisp-mode "'" nil :actions nil)
(sp-local-pair 'emacs-lisp-mode "`" nil :actions nil)
(sp-local-pair 'lisp-interaction-mode "'" nil :actions nil)
(sp-local-pair 'lisp-interaction-mode "`" nil :actions nil)
(sp-local-pair 'scheme-mode "'" nil :actions nil)
(sp-local-pair 'scheme-mode "`" nil :actions nil)
(sp-local-pair 'inferior-scheme-mode "'" nil :actions nil)
(sp-local-pair 'inferior-scheme-mode "`" nil :actions nil)
(sp-local-pair 'LaTeX-mode "\"" nil :actions nil)
(sp-local-pair 'LaTeX-mode "'" nil :actions nil)
(sp-local-pair 'LaTeX-mode "`" nil :actions nil)
(sp-local-pair 'latex-mode "\"" nil :actions nil)
(sp-local-pair 'latex-mode "'" nil :actions nil)
(sp-local-pair 'latex-mode "`" nil :actions nil)
(sp-local-pair 'TeX-mode "\"" nil :actions nil)
(sp-local-pair 'TeX-mode "'" nil :actions nil)
(sp-local-pair 'TeX-mode "`" nil :actions nil)
(sp-local-pair 'tex-mode "\"" nil :actions nil)
(sp-local-pair 'tex-mode "'" nil :actions nil)
(sp-local-pair 'tex-mode "`" nil :actions nil))
Working on college assignments in both C and Java made me wish for an interesting feature I’ve seen in IDEs: Automatic insertion of a correctly indented newline before the closing brace which allows you to enter its content right away. The following is stolen from its wiki.
(defun my-create-newline-and-enter-sexp (&rest _ignored)
"Open a new brace or bracket expression, with relevant newlines and indent."
(newline)
(indent-according-to-mode)
(forward-line -1)
(indent-according-to-mode))
(with-eval-after-load 'smartparens
(sp-local-pair 'c-mode "{" nil :post-handlers
'((my-create-newline-and-enter-sexp "RET")))
(sp-local-pair 'java-mode "{" nil :post-handlers
'((my-create-newline-and-enter-sexp "RET"))))
First of all, no long pair mismatch messages please, they’re reserved for debugging purposes.
(setq sp-message-width nil)
Because I’m using evil
, funny things are happening with my cursor,
like it not going beyond the end of the line in normal state. To
emulate a bit more Vim-like paren highlighting, pairs should be shown
from inside, too.
(setq sp-show-pair-from-inside t)
Automatic quote escaping feels like a mistake to me (and to its author as well ._.).
(setq sp-autoescape-string-quote nil)
This curiously named variable controls whether the overlay spanning the pair’s content disappears on backwards motions, something entirely different than its name suggests.
(setq sp-cancel-autoskip-on-backward-movement nil)
Here comes the set of sane text editing keybindings I can’t live without. Both implementation and execution are excellent and reuse as much from Emacs as possible, resulting in very high compatibility and feature coverage. The only thing I can complain about is that its sources are pretty much incomprehensible to me.
Despite that weakness I’ve managed writing my own additions to improve integration a good bit more according to my own tastes.
First of all, there are plenty of special modes where neither insert state nor motion state suffice. I’ve instead decided to do away with motion state and going for Emacs state whenever it makes sense. To aid Evil with this, I’ve modified its function that decides upon the initial state for a major mode to look up derived modes and aliases.
(defun my-real-function (fun)
"Figure out the actual symbol behind a function.
Returns a different symbol if FUN is an alias, otherwise FUN."
(let ((symbol-function (symbol-function fun)))
(if (symbolp symbol-function)
symbol-function
fun)))
(defun my-derived-mode-p (mode modes)
(let ((parent (my-real-function mode)))
(while (and parent (not (memq parent modes)))
(setq parent (my-real-function (get parent 'derived-mode-parent))))
parent))
(with-eval-after-load 'evil-core
(defun evil-initial-state (mode &optional default)
"Return the Evil state to use for MODE.
Returns DEFAULT if no initial state is associated with MODE.
The initial state for a mode can be set with
`evil-set-initial-state'."
(let (state modes)
(catch 'done
(dolist (entry (nreverse (evil-state-property t :modes)) default)
(setq state (car entry)
modes (symbol-value (cdr entry)))
(when (or (memq mode modes)
(my-derived-mode-p mode modes))
(throw 'done state)))))))
(setq evil-default-state 'emacs
evil-emacs-state-modes nil
evil-insert-state-modes nil
evil-motion-state-modes nil
evil-normal-state-modes '(text-mode prog-mode fundamental-mode
css-mode conf-mode
TeX-mode LaTeX-mode
diff-mode))
org-capture-mode
is a minor mode, that’s why I need to use its hook
instead. Same goes for view-mode
.
(add-hook 'org-capture-mode-hook 'evil-insert-state)
(add-hook 'with-editor-mode-hook 'evil-insert-state)
(add-hook 'view-mode-hook 'evil-emacs-state)
Allow quitting M-x magit-blame
with q
by toggling Evil’s current
state.
(defun my-evil-toggle ()
(interactive)
(cond
((memq evil-state '(insert normal))
(evil-emacs-state))
((eq evil-state 'emacs)
(evil-exit-emacs-state))))
(add-hook 'magit-blame-mode-hook 'my-evil-toggle)
These make movement, undo and search feel a bit less weird.
(setq evil-cross-lines t
evil-move-beyond-eol t
evil-want-fine-undo t
evil-symbol-word-search t)
However, I want C-w
to still be the window map prefix in Emacs state
(instead of the standard kill-region
command). As the customization
setting for that is applied in evil-maps.el
which is loaded by
evil.el
, I need to load it before enabling evil-mode
.
(with-eval-after-load 'evil-vars
(setq evil-want-C-w-in-emacs-state t))
(with-eval-after-load 'evil-common
(evil-declare-motion 'recenter-top-bottom))
I want Y
to yank to the end of line.
(setq evil-want-Y-yank-to-eol t)
Some minor modes come with keymaps reminiscent of special major modes,
these get overridden by Evil. These can be fixed by using
evil-normalize-keymaps
, at least for edebug-mode
.
(add-hook 'edebug-mode-hook 'evil-normalize-keymaps)
macrostep-mode
requires a bit more effort, see evil#511 for the code
involved and further explanation.
(defun my-macrostep-setup ()
(evil-make-overriding-map macrostep-keymap 'normal)
(evil-normalize-keymaps))
(add-hook 'macrostep-mode-hook 'my-macrostep-setup)
Same goes for cider-debug
:
(defun my-cider-debug-setup ()
(evil-make-overriding-map cider--debug-mode-map 'normal)
(evil-normalize-keymaps))
(add-hook 'cider--debug-mode-hook 'my-cider-debug-setup)
Let’s poke some holes into its keymaps. Anything not bound will be
passed through to Emacs other keymaps. Because SPC
, RET
and TAB
are bound to rather silly commands in Vim I’m unbinding them to allow
for much more useful Emacs commands (such as context-aware indentation, following
links, scrolling a page down, etc.).
(with-eval-after-load 'evil-maps
(define-key evil-motion-state-map (kbd "SPC") nil)
(define-key evil-motion-state-map (kbd "RET") nil)
(define-key evil-motion-state-map (kbd "TAB") nil))
Same story with C-.
and M-.
, the latter is usually bound to lookup
of symbol at point. The former is unbound because I’m fat-fingering
often.
(with-eval-after-load 'evil-maps
(define-key evil-normal-state-map (kbd "C-.") nil)
(define-key evil-normal-state-map (kbd "M-.") nil))
The hole poking continues, this time for the insert state and ex completion keymap. Everything with a modifier (except for the toggle key for Emacs state and other useful keys) has to go.
(with-eval-after-load 'evil-maps
(setcdr evil-insert-state-map
(let ((toggle-key (string-to-char (kbd evil-toggle-key))))
(--reject
(and (memq 'control (event-modifiers (car-safe it)))
(/= (car-safe it) toggle-key))
(cdr evil-insert-state-map))))
(setcdr evil-ex-completion-map
(--reject
(and (memq 'control (event-modifiers (car-safe it)))
;; abort prompt
(/= (car-safe it) ?\C-c)
(/= (car-safe it) ?\C-g)
;; previous/next input
(/= (car-safe it) ?\C-p)
(/= (car-safe it) ?\C-n))
(cdr evil-ex-completion-map))))
C-w
works in Emacs state, but not in insert state. Let’s fix that.
(with-eval-after-load 'evil-maps
(define-key evil-insert-state-map (kbd "C-w") 'evil-window-map))
C-i
is used in Vim as counterpart to C-o
for going back and forth
in the jump list. It also happens to be interpreted as TAB
, simply
because terminals are a nightmare. Fortunately GUI Emacs can be told
to not resolve C-i
to indentation. I’m stealing Evil’s trick of
making use of an empty menu item since these have the ability to make
use of a filter function to optionally translate the key into another
one. That way I’m sending a custom <C-i>
when Evil is active and in
normal state, otherwise TAB
is passed through.
(defun my-translate-C-i (_prompt)
(if (and (= (length (this-command-keys-vector)) 1)
(= (aref (this-command-keys-vector) 0) ?\C-i)
(bound-and-true-p evil-mode)
(eq evil-state 'normal))
(kbd "<C-i>")
(kbd "TAB")))
(define-key key-translation-map (kbd "TAB") 'my-translate-C-i)
(with-eval-after-load 'evil-maps
(define-key evil-motion-state-map (kbd "<C-i>") 'evil-jump-forward))
C-u
is bound to a scroll up command in Vim, in Emacs however it’s
used for the prefix argument. This feels pretty weird to me after
having bothered learning C-u
as command for killing a whole line in
everything using the readline library. I consider M-u
as a good
replacement considering it’s bound to the rather useless upcase-word
command by default which I most definitely will not miss.
(define-key global-map (kbd "C-u") 'kill-whole-line)
(define-key global-map (kbd "M-u") 'universal-argument)
(define-key universal-argument-map (kbd "C-u") nil)
(define-key universal-argument-map (kbd "M-u") 'universal-argument-more)
(with-eval-after-load 'evil-maps
(define-key evil-motion-state-map (kbd "C-u") 'evil-scroll-up))
Emacs 24.4 introduced electric-indent-mode
as default which happens
to be a global mode. I’m not particularly fond of it (and anything
starting with electric-
), that’s why I disable it later after
initialization is done and instead bind newline-and-indent
in insert
state.
(with-eval-after-load 'evil-maps
(define-key evil-insert-state-map (kbd "RET") 'newline-and-indent))
Let’s get rid of ;
for the questionable benefit of having a modifier
less to hit for entering ex state.
(with-eval-after-load 'evil-maps
(define-key evil-motion-state-map (kbd ";") 'evil-ex)
(define-key evil-visual-state-map (kbd ";") 'evil-ex))
U
is a much more fit key for redoing than C-r
.
(with-eval-after-load 'evil-maps
(define-key evil-normal-state-map (kbd "U") 'undo-tree-redo))
The evil-numbers package is pretty nice, but I don’t want to use the
standard Vim keybinds (C-a
and C-x
) for its commands. Instead I’m
going for the much more mnemonic +
and -
.
(with-eval-after-load 'evil-maps
(define-key evil-normal-state-map (kbd "-") 'evil-numbers/dec-at-pt)
(define-key evil-normal-state-map (kbd "+") 'evil-numbers/inc-at-pt))
I’m not sure what to think of the ace-jump. For convenience I’ve reduced its jump keys to the homerow and bound a few commands.
(setq ace-jump-mode-move-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l ?\;))
(with-eval-after-load 'evil-maps
(define-key evil-normal-state-map (kbd "SPC") 'evil-ace-jump-char-mode)
(define-key evil-normal-state-map (kbd "S-SPC") 'evil-ace-jump-word-mode)
(define-key evil-normal-state-map (kbd "C-SPC") 'evil-ace-jump-line-mode)
(define-key evil-operator-state-map (kbd "SPC") 'evil-ace-jump-char-mode)
(define-key evil-operator-state-map (kbd "S-SPC") 'evil-ace-jump-word-mode)
(define-key evil-operator-state-map (kbd "C-SPC") 'evil-ace-jump-line-mode))
The z
map is full of keybindings I can never remember for dealing
with code folding. First of all, get rid of them.
(with-eval-after-load 'evil-maps
(setcdr evil-normal-state-map
(--reject
(eq (car-safe it) ?z)
(cdr evil-normal-state-map)))
(setcdr evil-motion-state-map
(--reject
(eq (car-safe it) ?z)
(cdr evil-motion-state-map))))
Next, define a few toggling commands and bind them.
(defvar my-hs-hide nil
"Current state of hideshow for toggling all.")
(with-eval-after-load 'evil-common
(evil-define-command my-evil-toggle-folds ()
"Open or close all folds."
(setq my-hs-hide (not my-hs-hide))
(if my-hs-hide
(hs-hide-all)
(hs-show-all))))
(defun my-toggle-mode-line-minor-modes ()
(interactive)
(if rm-blacklist
(setq rm-blacklist nil)
(setq rm-blacklist ".*"))
(force-mode-line-update t))
(defun my-narrow-to-region-with-mode (beg end mode)
(interactive (list (region-beginning) (region-end)
(completing-read "Major mode: "
(mapcar 'cdr auto-mode-alist) nil t)))
(unless (region-active-p)
(error "No region for narrowing selected"))
(narrow-to-region beg end)
(deactivate-mark)
(funcall (intern mode)))
(defun my-revert-buffer ()
(interactive)
(revert-buffer nil t))
(with-eval-after-load 'evil-maps
(define-key evil-normal-state-map (kbd "z r") 'my-revert-buffer)
(define-key evil-normal-state-map (kbd "z b") 'magit-blame)
(define-key evil-normal-state-map (kbd "z s") 'describe-char)
(define-key evil-normal-state-map (kbd "z e") 'toggle-debug-on-error)
(define-key evil-normal-state-map (kbd "z q") 'toggle-debug-on-quit)
(define-key evil-normal-state-map (kbd "z t") 'my-solarized-toggle)
(define-key evil-normal-state-map (kbd "z m") 'my-toggle-mode-line-minor-modes)
(define-key evil-normal-state-map (kbd "z n") 'my-narrow-to-region-with-mode)
(define-key evil-normal-state-map (kbd "z TAB") 'evil-toggle-fold)
(define-key evil-normal-state-map (kbd "z <backtab>") 'my-evil-toggle-folds))
Define my most-used helpers (stolen from unimpaired.vim) next.
(defun my-evil-unimpaired-insert-newline-above (count)
"Insert an empty line below point"
(interactive "p")
(save-excursion
(dotimes (i count)
(evil-insert-newline-above))))
(defun my-evil-unimpaired-insert-newline-below (count)
"Insert an empty line below point"
(interactive "p")
(save-excursion
(dotimes (i count)
(evil-insert-newline-below))))
(with-eval-after-load 'evil-maps
(define-key evil-normal-state-map (kbd "[ SPC") 'my-evil-unimpaired-insert-newline-above)
(define-key evil-normal-state-map (kbd "] SPC") 'my-evil-unimpaired-insert-newline-below))
Add a few convenience bindings to the window map on C-w
.
(defun my-work-on-scratch ()
(interactive)
(switch-to-buffer (get-buffer-create "*scratch*")))
(with-eval-after-load 'evil-maps
(define-key evil-window-map (kbd "n") 'my-work-on-scratch)
(define-key evil-window-map (kbd "u") 'winner-undo)
(define-key evil-window-map (kbd "b") 'helm-mini)
(define-key evil-window-map (kbd "d") 'kill-buffer)
(define-key evil-window-map (kbd "D") 'kill-buffer-and-window)
(define-key evil-window-map (kbd "C-d") 'kill-buffer-and-window))
Then some “leader” bindings.
(defun my-switch-to-last-buffer ()
(interactive)
(switch-to-buffer (other-buffer)))
(defun my-find-file-with-root-privileges (filename)
(interactive "F")
(let ((pw (concat (password-read "Enter password: ") "\n"))
(sudo-process (start-process "Sudo" "*sudo*" "sudo" "-Se" filename)))
(process-send-string sudo-process pw)))
(with-eval-after-load 'evil-maps
(define-key evil-normal-state-map (kbd ", ,") 'my-switch-to-last-buffer)
(define-key evil-normal-state-map (kbd ", .") 'helm-mini)
(define-key evil-normal-state-map (kbd ", /") 'helm-find-files)
(define-key evil-normal-state-map (kbd ", ?") 'my-find-file-with-root-privileges))
As calc
keeps bewildering me, but calculator
doesn’t, I’ll bind
the latter for one-off calculations:
(with-eval-after-load 'evil-maps
(define-key evil-normal-state-map (kbd "=") 'calculator))
Finally, there’s a few minor modes depending on Evil being loaded before they are.
(defun my-after-evil ()
(global-surround-mode)
(eyebrowse-mode)
(eyebrowse-setup-opinionated-keys))
(add-hook 'evil-mode-hook 'my-after-evil)
Snippets are quite useful for boilerplatey languages. Like, Java.
Although, if you take it far enough, even something as org-mode
qualifies considering I can never remember the proper syntax for code
blocks. The following sets up a single directory for snippets.
(setq yas-snippet-dirs '("~/.emacs.d/snippets"))
(with-eval-after-load 'yasnippet
(yas-reload-all))
rcirc
is too small, ERC is too large. So I chose Circe as my IRC
client living inside Emacs. As for why IRC in Emacs in the first
place, I wanted to leave irssi behind and didn’t really like Weechat.
So, why not try something extensible?
First of all, let’s define who I am and change the quit/part message to something less advertising.
(setq circe-default-nick "wasamasa"
circe-default-user "wasamasa"
circe-default-realname "wasamasa"
circe-default-part-message "Bye"
circe-default-quit-message "Bye")
I’m using ZNC to connect to both Freenode and the f0o network, but Bitlbee for Jabber. Passwords for the Nickserv service are kept in a private file which is read in by a password function.
(setq my-credentials-file "~/.emacs.d/etc/private.el")
(defun my-retrieve-irc-password (_)
(let ((network circe-server-network))
(with-temp-buffer
(insert-file-contents-literally my-credentials-file)
(let ((plist (read (buffer-string))))
(if (string= network "Bitlbee")
(plist-get plist :bitlbee-password)
(plist-get plist :znc-password))))))
(setq circe-network-options
'(("ZNC/freenode" :host "brause.cc" :port 30832 :family ipv4
:user "wasamasa/freenode" :pass my-retrieve-irc-password)
("ZNC/f0o" :host "brause.cc" :port 30833 :family ipv4
:user "wasamasa/f0o" :pass my-retrieve-irc-password)
("Bitlbee" :nickserv-password my-retrieve-irc-password)))
I cannot imagine why I wouldn’t want to use in-line tab-completion with cycling just as it exists in other IRC clients.
(setq circe-use-cycle-completion t)
Smart filter is like the best IRC-related invention ever!
(setq circe-reduce-lurker-spam t)
Let’s customize a few format strings.
(setq circe-format-self-say "<{nick}> {body}"
circe-format-server-topic "*** Topic Change by {userhost}: {topic-diff}"
circe-server-buffer-name "{network}"
circe-prompt-string (propertize ">>> " 'face 'circe-prompt-face))
Other entities using my nickname are not ghosted by default, that’s why I enable it, but only after authenticating in some way.
(setq circe-nickserv-ghost-style 'after-auth)
ZNC handles autojoins for me, but Circe does not recognize these. So, instead I’ll just ignore all buffers that are opened implicitly.
(setq circe-new-buffer-behavior 'ignore)
I’m sending highlights to a *hl*
buffer for occasionally taking a
look at them, so please ignore tracking it.
(setq tracking-ignored-buffers '("*hl*"))
When using the circe-color-nicks
contrib module, please color
nicknames inside messages as well.
(setq circe-color-nicks-everywhere t)
Additionally to that, make use of colors more compatible with my theme.
(setq circe-color-nicks-pool-type
'("#b58900" "#cb4b16" "#dc322f" "#d33682" "#6c71c4"
"#268bd2" "#2aa198" "#859900" "#7b6000" "#deb542"
"#f2804f" "#ff6e64" "#f771ac" "#9ea0e5" "#00629d"
"#69b7f0" "#00736f" "#69cabf" "#546e00" "#b4c342"))
Let’s add a few extra keybindings common in all buffers Circe spawns.
I want word killing to behave the same as for comint
, C-l
to
redraw and reposition and C-u
to kill the whole line since there’s a
more appropriate command than the default one bound to C-u
.
(defun my-window-C-l ()
(interactive)
(goto-char (point-max))
(recenter-top-bottom -1))
(with-eval-after-load 'lui
(define-key lui-mode-map (kbd "<remap> <kill-word>") 'my-kill-word)
(define-key lui-mode-map (kbd "<remap> <backward-kill-word>") 'my-backward-kill-word)
(define-key lui-mode-map (kbd "C-l") 'my-window-C-l)
(define-key lui-mode-map (kbd "C-u") 'lui-kill-to-beginning-of-line))
Copy-pasting from other sources (like, browsers) can leave more than
one line of text in the input area. Directly sending it would be
annoying as this would result in either multiple messages or autopaste
detection. To avoid resorting to joining lines manually, I’ve written
a command doing the opposite of M-q
, but named it similiarly as the
intent is the same (making the given text conform to a more suitable
form).
(defun my-fill-lui-input ()
(interactive)
(fill-delete-newlines lui-input-marker (point-max) nil nil nil)
(goto-char (point-max)))
(with-eval-after-load 'lui
(define-key lui-mode-map (kbd "M-q") 'my-fill-lui-input))
Sometimes I like knowing just how many people are online.
(defun my-circe-count-nicks ()
(interactive)
(when (eq major-mode 'circe-channel-mode)
(message "%i entities are online on %s."
(length (circe-channel-nicks))
(buffer-name))))
(with-eval-after-load 'circe
(define-key circe-channel-mode-map (kbd "C-c n") 'my-circe-count-nicks))
The standard nickname switching function is a bit silly. I own a
bunch of nicknames and will use the wasa
one for switching.
(defun my-circe-nick-next (oldnick)
(cond ((string= oldnick "wasamasa") "wasa")
((string= oldnick "wasa" "wasamasa"))))
(setq circe-nick-next-function 'my-circe-nick-next)
There isn’t a highlighting function yet that could do something useful like setting a X urgency hint (taken from the wiki), asides from that I want a bit more of control to treat highlights in private queries different from channel highlights. The following code yanks out the default one and replaces it with something slightly better.
(defun my-x-urgency-hint ()
(let* ((wm-hints (append (x-window-property
"WM_HINTS" nil "WM_HINTS" nil nil t) nil))
(flags (car wm-hints)))
(setcar wm-hints (logior flags #x00000100))
(x-change-window-property "WM_HINTS" wm-hints nil "WM_HINTS" 32 t)))
(defun my-any-regex-in-string (regexes string)
(when string (--any-p (s-contains? it string) regexes)))
(defface my-circe-highlight-notification-face '((t (:weight bold)))
"Face for circe notifications")
(defun my-circe-message-option-highlight (nick user host command args)
(let* ((highlight-regexps '("webspid0r" "wubspider" "wasamasa" "wasa\\>"))
(irc-message (cadr args))
(highlight-match (my-any-regex-in-string highlight-regexps irc-message)))
(when irc-message
(when (not (equal nick circe-default-nick))
(when (and (not (equal major-mode 'circe-server-mode))
(and (not (s-matches? "LAGMON" irc-message))
(equal major-mode 'circe-query-mode))
(or highlight-match
(equal major-mode 'circe-query-mode)))
(my-x-urgency-hint))
(when highlight-match
'((text-properties . (face my-circe-highlight-notification-face message t))))))))
(add-hook 'circe-message-option-functions 'my-circe-message-option-highlight)
(setq circe-track-faces-priorities '(my-circe-highlight-notification-face
circe-my-message-face circe-server-face))
(defun my-circe-disable-highlight-nick ()
(remove-hook 'lui-pre-output-hook 'circe-highlight-nick t))
(add-hook 'circe-chat-mode-hook 'my-circe-disable-highlight-nick)
Highlight quoted text in a green color for fun and profit (or to be honest, to discern 4chan people from the rest).
(defface my-circe-greentext-face '((t (:foreground "spring green")))
"Face for greentext detected in circe.")
(defun my-circe-color-greentext ()
(when (memq major-mode '(circe-channel-mode circe-query-mode))
(let ((body-beg (text-property-any (point-min) (point-max)
'lui-format-argument 'body))
(greentext-regex "\\([^[:space:]]+?: \\)?\\(>[[:word:][:space:]]\\)"))
(when body-beg
(goto-char body-beg)
(when (looking-at greentext-regex)
(add-text-properties (match-beginning 2) (point-max)
'(face my-circe-greentext-face)))))))
(add-hook 'lui-pre-output-hook 'my-circe-color-greentext)
I dislike custom
, but want persistent fools. So, advice it is!
(defvar my-circe-fool-file "~/.emacs.d/etc/fools"
"File to store persistent fools in.")
(defun my-circe-serialize-fools (fools)
(with-temp-file my-circe-fool-file
(insert (prin1-to-string fools))))
(defun my-circe-deserialize-fools ()
(when (file-exists-p my-circe-fool-file)
(with-temp-buffer
(insert-file-contents-literally my-circe-fool-file)
(read (buffer-substring-no-properties (point-min) (point-max))))))
(defun my-circe-load-fools ()
(setq circe-fool-list (my-circe-deserialize-fools)))
(defun my-circe-update-fools ()
(my-circe-serialize-fools
(-union (my-circe-deserialize-fools) circe-fool-list)))
(defun my-circe-truncate-fools ()
(my-circe-serialize-fools circe-fool-list))
(defadvice circe-command-FOOL (after persistent-fools activate)
(my-circe-update-fools))
(defadvice circe-command-UNFOOL (after persistent-fools activate)
(my-circe-truncate-fools))
(add-hook 'circe-channel-mode-hook 'my-circe-load-fools)
There is a pretty annoying interaction between ZNC’s replay feature which makes the lagmon reconnect kick in if too much time is spent not replying to anything, here’s a hack to deal with it by disabling it as long as it’s going on.
(defun my-circe-display-PRIVMSG (nick user host command args)
(when (and (string= nick "***") (string= user "znc"))
(let ((message (cadr args)))
(cond
((string= message "Buffer Playback...")
(circe-lagmon-mode -1))
((string= message "Playback Complete.")
(circe-lagmon-mode)))))
(circe-display-PRIVMSG nick user host command args))
(with-eval-after-load 'circe
(circe-set-display-handler "PRIVMSG" 'my-circe-display-PRIVMSG))
Let’s test having fluid-width windows, now that both Circe and Emacs 24.4 seem to be less wonky about it. Adapted from the wiki, extended to avoid the rather annoying behaviour of the cursor jumping into the fringe when reaching the full text width.
(setq lui-time-stamp-position 'right-margin
lui-fill-type nil)
(defun my-no-fill-lui-setup ()
(setq fringes-outside-margins t
right-margin-width 7
fill-column 80
wrap-prefix " ")
(visual-line-mode)
(setf (cdr (assoc 'continuation fringe-indicator-alist)) nil)
(make-local-variable 'overflow-newline-into-fringe)
(setq overflow-newline-into-fringe nil))
(add-hook 'circe-chat-mode-hook 'my-no-fill-lui-setup)
And finally, the function for entering IRC.
(defun my-irc ()
"Connect to all my IRC servers after enabling contrib modules."
(interactive)
(circe-lagmon-mode)
(enable-circe-color-nicks)
(enable-lui-autopaste)
(require 'circe-chanop)
(circe "Bitlbee")
(circe "ZNC/f0o")
(circe "ZNC/freenode"))
(defun my-irc-debug ()
(interactive)
(circe "Freenode" :nick "not_wasamasa"))
Minor mode for coloring TODO
, NOTE
, FIXME
and many more keywords
of that sort prevalent in comments and strings.
(setq hl-todo-keyword-faces '(("TODO" . hl-todo)
("NOTE" . hl-todo)
("HACK" . hl-todo)
("FIXME" . hl-todo)
("KLUDGE" . hl-todo)))
(with-eval-after-load 'hl-todo
(hl-todo-set-regexp))
Expand macros interactively.
I’ll go with the recommended keybinding for it.
(define-key emacs-lisp-mode-map (kbd "C-x e") 'macrostep-expand)
There used to be longlines-mode
which did display a file
soft-wrapped without breaking words and using the value of
fill-column
. However it got deprecated for visual-line-mode
which
does the same except it doesn’t take fill-column
into account. The
visual-fill-column
package fixes that, I want to enable its mode
automatically when enabling visual-line-mode
.
(add-hook 'visual-line-mode-hook 'visual-fill-column-mode)
The very best. I’m currently on the next
branch to get the latest
bugfixes and features.
For some reason they’ve started nagging me to opt-in for default behaviour to be used without confirmation.
(setq magit-revert-buffers 'silent)
(setq magit-push-always-verify nil)
The closest thing to SLIME. It supports three Scheme implementations currently, so I’m picking my prefered one.
(setq geiser-default-implementation 'chicken)
We already have F1
for help, so let’s turn C-h
and M-h
more
readline-like.
(global-set-key (kbd "C-h") 'delete-backward-char)
(global-set-key (kbd "M-h") 'backward-kill-word)
Deactivate all other uses of insert than Shift-Insert
.
(global-set-key (kbd "<insert>") nil)
(global-set-key (kbd "<C-insert>") nil)
FWIW, yanking is what Emacs calls pasting text. Unlike Vim where it
stands for copying text. Anyways, I want to rectify the curious
default of making S-insert
paste from the same place as C-y
.
(defun my-yank-primary ()
(interactive)
(let ((primary (or (x-get-selection-value)
(x-get-selection))))
(unless primary
(error "No selection is available"))
(push-mark (point))
(insert-for-yank primary)))
(global-set-key (kbd "<S-insert>") 'my-yank-primary)
Install a keybind that saves all buffers with asking (use a prefix
argument to inhibit the questions), then kills Emacs (including the
daemon) on M-<f4>
.
(defun my-quit-emacs (arg)
(interactive "P")
(save-some-buffers (when (consp arg) t) t)
(kill-emacs))
(global-set-key (kbd "M-<f4>") 'my-quit-emacs)
Make M-x
more useful, put its original functionality on C-c M-x
instead.
(global-set-key (kbd "M-x") 'helm-smex)
(global-set-key (kbd "C-c M-x") 'execute-extended-command)
Helm stuff
(global-set-key (kbd "C-x C-f") 'helm-find-files)
(global-set-key (kbd "C-x b") 'helm-buffers-list)
(global-set-key (kbd "<f10>") 'helm-resume)
C-c C-+
and C-c C--
are pretty useful, but only resize the current
buffer. Here’s a hack using set-frame-font
and altering the font
size only:
(defun my-alter-frame-font-size (fn)
(let* ((current-font-name (frame-parameter nil 'font))
(decomposed-font-name (x-decompose-font-name current-font-name))
(font-size (string-to-int (aref decomposed-font-name 5))))
(aset decomposed-font-name 5 (int-to-string (funcall fn font-size)))
(set-frame-font (x-compose-font-name decomposed-font-name))))
(defun my-inc-frame-font-size ()
(interactive)
(my-alter-frame-font-size '1+))
(defun my-dec-frame-font-size ()
(interactive)
(my-alter-frame-font-size '1-))
(global-set-key (kbd "C-+") 'my-inc-frame-font-size)
(global-set-key (kbd "C-=") 'my-inc-frame-font-size)
(global-set-key (kbd "C--") 'my-dec-frame-font-size)
First of all, let’s define a helper function that does the boilerplate for us.
(defun my-add-function-to-hooks (function hooks)
(dolist (hook hooks)
(add-hook hook function)))
I’ll start with a list of hooks for everything that’s not a special mode or in other words, related to programming and text editing. This will inevitably contain modes that have not been properly derived, might be worth reporting those.
(defun my-non-special-modes-setup ()
(setq indicate-empty-lines t)
(setq indicate-buffer-boundaries '((top . left) (bottom . left)))
(setq show-trailing-whitespace t)
(hl-todo-mode)
(modify-syntax-entry ?_ "w")
(goto-address-mode)
(smartparens-mode)
(show-smartparens-mode)
(yas-minor-mode)
(form-feed-mode))
(my-add-function-to-hooks
'my-non-special-modes-setup
'(text-mode-hook prog-mode-hook css-mode-hook diff-mode-hook))
Same deal with programming-related hooks and text-related hooks.
(my-add-function-to-hooks
'auto-fill-mode
'(text-mode-hook css-mode-hook))
(defun my-prog-modes-setup ()
(make-local-variable 'comment-auto-fill-only-comments)
(setq comment-auto-fill-only-comments t)
(auto-fill-mode)
(column-enforce-mode))
(my-add-function-to-hooks
'my-prog-modes-setup
'(prog-mode-hook))
To fine tune completion behavior, I’ll prefer company-mode
over
global-company-mode
. I should add more hooks for REPLs, too.
(my-add-function-to-hooks
'company-mode
'(prog-mode-hook css-mode-hook nxml-mode-hook sgml-mode-hook
css-mode-hook ielm-mode-hook))
Enable linting for a few select modes by default. The rest is
programming languages I don’t happen to use, languages I don’t have a
linter installed for or that would be too annoying to always check.
Like, the default emacs-lisp
(and emacs-lisp-checkdoc
, too) linter
which always assumes I’m writing an Emacs package.
(my-add-function-to-hooks
'flycheck-mode
'(python-mode-hook ruby-mode-hook enh-ruby-mode-hook LaTeX-mode-hook))
Rainbow-colored delimiters still work best in Lisp modes.
(my-add-function-to-hooks
'rainbow-delimiters-mode
'(emacs-lisp-mode-hook ielm-mode-hook scheme-mode-hook
inferior-scheme-mode-hook lisp-mode-hook lisp-interaction-mode-hook
clojure-mode-hook nrepl-interaction-mode-hook))
Colored color codes however only make sense for CSS, HTML and XML.
(my-add-function-to-hooks
'rainbow-mode
'(css-mode-hook sgml-mode-hook nxml-mode-hook web-mode-hook))
smartparens
ought to be enabled in the minibuffer, but only for M-:
.
(defun my-smartparens-minibuffer-setup ()
(when (eq this-command 'eval-expression)
(smartparens-mode)
(show-smartparens-mode)))
(add-hook 'minibuffer-setup-hook 'my-smartparens-minibuffer-setup)
Emacs Lisp files can be easily turned into packages one can load and
install via package.el
. While initialization is done, it only
happens after successful startup.
My unpublished packages reside in ~/.emacs.d/unpublished
and need to
be added to the load-path
.
(add-to-list 'load-path "~/.emacs.d/unpublished")
I have a bunch of stuff I’m working on.
(require 'helm-smex)
While packages were prematurely initialized, the following is unconditionally run after init has finished.
(defun my-after-init ()
(add-to-list 'load-path "~/code/circe")
(add-to-list 'load-path "~/code/shackle")
(add-to-list 'load-path "~/code/eyebrowse")
(add-to-list 'load-path "~/code/form-feed")
(add-to-list 'load-path "~/code/firestarter")
(sml/setup)
(require 'dash)
(recentf-mode)
(savehist-mode)
(require 'saveplace)
(winner-mode)
(shackle-mode)
(firestarter-mode)
(smex-initialize)
(helm-mode)
(electric-indent-mode -1)
(my-setup-hydra)
(evil-mode)
(my-disable-tramp-file-handlers)
(cl-lib-highlight-initialize)
(cl-lib-highlight-warn-cl-initialize)
(line-number-mode)
(column-number-mode))
(add-hook 'after-init-hook 'my-after-init)
- Learn Yasnippet and write snippets
- Improve company-mode UI
- Check backends and reconsider their uses
- Mix in Yasnippet
- Update from company-jedi to anaconda-mode
- Configure smartparens properly, contribute defaults
- Fix show-smartparens on that configuration block
- Figure out how to use Evil initial states via major mode hooks
- Get modes not working with prog- or text-mode-hook fixed
- Make use of toc-org after fixing its bugs
I consider this experiment successful. I’ve managed to uncruftify my previous setup, have a highly readable init file and get recommendations and Github favourites from an reasonable amount of people. It went better than expected because explaining my reasoning and hacks in a config file fits me better than doing the same for a program’s code.
Debugging has suffered a bit due to the indirection. While C-x C-e
works globally, I no longer have C-M-x
and the prefix argument
version for instrumenting, so I need to copy the piece of code over to
a scratch buffer. esup reports an artificially low time, unlike what
M-x emacs-init-time
does.