"People talk about getting used to a new editor, but over time, it is
precisely the opposite that should happen - the editor should get used
to us."
-
Vivek Haldar
Switching between the two most recent buffers is something I do often enough to warrant its own keybinding:
(fset 'quick-switch-buffer [?\C-x ?b return])
(global-set-key (kbd "s-b") 'quick-switch-buffer)
On my Mac I have bound super (s) to the option key, which opens up a whole world of new possible keybindings.
I don't much enjoy my editor beeping at me.
(setq ring-bell-function (lambda ()
(invert-face 'mode-line)
(run-with-timer 0.05 nil 'invert-face 'mode-line)))
This should help Emacs be a bit more subtle in the face of everyday errors, you know, like pressing C-g. BEEP BEEP!
Now that Emacs has moved to git, maybe it's time to start
contributing directly? Here's how you build Emacs from source on OS X:
git clone git://git.savannah.gnu.org/emacs.git
cd emacs
./autogen.sh
./configure --with-ns
make install
cd nextstep
open Emacs.app
For more info and instructions for other distros,
see Lars'
post.
Ever been annoyed at the lack of reindentation after using
sgml-delete-tag?
(defadvice sgml-delete-tag (after reindent activate)
(indent-region (point-min) (point-max)))
Be annoyed no more!
This blogpost brought to you live from WebRebels 2013.
I already covered the awesomely
commented diminish.el.
Here's another trick to reduce the cruft in your modeline:
(defmacro rename-modeline (package-name mode new-name)
`(eval-after-load ,package-name
'(defadvice ,mode (after rename-modeline activate)
(setq mode-name ,new-name))))
(rename-modeline "js2-mode" js2-mode "JS2")
(rename-modeline "clojure-mode" clojure-mode "Clj")
With this, I reduce the js2-mode
modeline lighter from "JavaScript IDE" to just "JS2".
I stole it from Bodil's
.emacs.d and macroified it a little.
The first argument is the package name, the second is the mode in
question, and the third is the new lighter for the mode.
Undo in region is one of those mind-blowing things about emacs.
However, the region keeps jumping about when I use it. So I added this:
(defadvice undo-tree-undo (around keep-region activate)
(if (use-region-p)
(let ((m (set-marker (make-marker) (mark)))
(p (set-marker (make-marker) (point))))
ad-do-it
(goto-char p)
(set-mark m)
(set-marker p nil)
(set-marker m nil))
ad-do-it))
Now the region stays in place while I'm undoing.
Since I use undo-tree, that's what it advises, but I would guess it
works the same for regular old undo too.
Where do you put your project specific settings?
(defmacro project-specifics (name &rest body)
(declare (indent 1))
`(progn
(add-hook 'find-file-hook
(lambda ()
(when (string-match-p ,name (buffer-file-name))
,@body)))
(add-hook 'dired-after-readin-hook
(lambda ()
(when (string-match-p ,name (dired-current-directory))
,@body)))))
(project-specifics "projects/zombietdd"
(set (make-local-variable 'slime-js-target-url) "http://localhost:3000/")
(ffip-local-patterns "*.js" "*.jade" "*.css" "*.json" "*.md"))
I created this macro to help me set up local vars. So in the
example, any files in projects/zombietdd
will see
these slime-js-target-url and the find-file-in-projects patterns.
I keep these in a projects-folder to keep track of all the different
settings for my projects.
I mainly use org-mode for a collection of TODO-lists.
(defun myorg-update-parent-cookie ()
(when (equal major-mode 'org-mode)
(save-excursion
(ignore-errors
(org-back-to-heading)
(org-update-parent-todo-statistics)))))
(defadvice org-kill-line (after fix-cookies activate)
(myorg-update-parent-cookie))
(defadvice kill-whole-line (after fix-cookies activate)
(myorg-update-parent-cookie))
So I get a little annoyed when the [17/23]
cookies at
the parent level aren't updated when I remove an item.
This code fixes that.
In html-mode, forward/backward-paragraph is infuriatingly slow.
(defun skip-to-next-blank-line ()
(interactive)
(let ((inhibit-changing-match-data t))
(skip-syntax-forward " >")
(unless (search-forward-regexp "^\\s *$" nil t)
(goto-char (point-max)))))
(defun skip-to-previous-blank-line ()
(interactive)
(let ((inhibit-changing-match-data t))
(skip-syntax-backward " >")
(unless (search-backward-regexp "^\\s *$" nil t)
(goto-char (point-min)))))
(eval-after-load "sgml-mode"
'(progn
(define-key html-mode-map
[remap forward-paragraph] 'skip-to-next-blank-line)
(define-key html-mode-map
[remap backward-paragraph] 'skip-to-previous-blank-line)))
I use them a lot for quick navigation. In html-mode, they are anything but quick.
Defining paragraphs in Emacs is black magic, and I'm not sure it's a
good idea to change that in case something else relies on its erratic behavior.
Instead I just remap the commands to my home brewed
skip-to-next/previous-blank-line. Ahh, speedy and predictable
navigation once more.
Okay, this is a bad idea if your files are prefixed with ~
.
(add-hook 'ido-setup-hook
(lambda ()
(define-key ido-file-completion-map
(kbd "~")
(lambda ()
(interactive)
(if (looking-back "/")
(insert "~/")
(call-interactively 'self-insert-command))))))
But if they're not, this keybinding lets you even more quickly reach
your home folder when in ido-find-file.
It doesn't matter if you're a million directories in, just
press ~ to go home.
In dired, M-> and M- never take me where I want to go.
(defun dired-back-to-top ()
(interactive)
(beginning-of-buffer)
(dired-next-line 4))
(define-key dired-mode-map
(vector 'remap 'beginning-of-buffer) 'dired-back-to-top)
(defun dired-jump-to-bottom ()
(interactive)
(end-of-buffer)
(dired-next-line -1))
(define-key dired-mode-map
(vector 'remap 'end-of-buffer) 'dired-jump-to-bottom)
That is, now they do.
Instead of taking me to the very beginning or very end, they now
take me to the first or last file.
I use Phil
Hagelbergs' find-file-in-project,
but fuzzy matching with LOTS of files can be suboptimal.
(defun ffip-create-pattern-file-finder (&rest patterns)
(lexical-let ((patterns patterns))
(lambda ()
(interactive)
(let ((ffip-patterns patterns))
(find-file-in-project)))))
(global-unset-key (kbd "C-x C-o"))
(global-set-key (kbd "C-x C-o ja")
(ffip-create-pattern-file-finder "*.java"))
(global-set-key (kbd "C-x C-o js")
(ffip-create-pattern-file-finder "*.js"))
(global-set-key (kbd "C-x C-o jp")
(ffip-create-pattern-file-finder "*.jsp"))
This function limits the search to files of a specific file type.
I've got loads more of these keybindings, all of them with the
two-letter mnemonic shortcut.
It really speeds up finding files. Both
because ido-completing-read
has less matches to worry
about, because there are fewer similarly named files, and especially
when the .java, the .js and the .jsp share a name.
Here's one keybinding I could not live without.
(global-set-key (kbd "M-j")
(lambda ()
(interactive)
(join-line -1)))
It joins the following line onto this one.
Let's say I want to collapse this paragraph-tag to one line:
<p class="example">
Some text
over multiple
lines.
</p>
With point anywhere on the first line, I simply press M-j
multiple times to pull the lines up.
There are lots of neat ways of moving around quickly in a buffer.
(global-set-key (kbd "C-S-n")
(lambda ()
(interactive)
(ignore-errors (next-line 5))))
(global-set-key (kbd "C-S-p")
(lambda ()
(interactive)
(ignore-errors (previous-line 5))))
(global-set-key (kbd "C-S-f")
(lambda ()
(interactive)
(ignore-errors (forward-char 5))))
(global-set-key (kbd "C-S-b")
(lambda ()
(interactive)
(ignore-errors (backward-char 5))))
For instance, check
out Emacs Rocks e10:
Jumping Around.
But sometimes I just want to browse a little. Or move a few lines
down. These keybindings let me do that more quickly
than C-n C-n C-n C-n C-n C-n
...
In fact, with these I can navigate to any line within a distance of
11 in 3 keystrokes or less. Or close enough to count. Two of them
require 4 keystrokes. Can you figure out which ones?
Everybody knows about moving Control to Caps Lock. These are my
extra neat tricks for my MacBook Pro:
(setq mac-command-modifier 'meta)
(setq mac-option-modifier 'super)
(setq ns-function-modifier 'hyper)
First of all, Meta M- needs to be really easy to hit. On a Mac
keyboard, that means Command - and not the default Option - since
we want the key that is right next to Space.
The good news is that now Option is available for
Super s-. And even more amazing, you can also bind the
Function-key to Hyper H- - without losing the ability to
change the volume or pause/play.
So now I can use crazy keybindings
like H-SPC hyperspace. I haven't entirely
decided what I should be using this newfound superpower for, but one
thing I've done is reserve all the C-s- prefixed letters for
refactorings with js2-refactor,
as you can see here.
I love the symbiosis between expand-region and delete-selection-mode.
(put 'paredit-forward-delete 'delete-selection 'supersede)
(put 'paredit-backward-delete 'delete-selection 'supersede)
(put 'paredit-open-round 'delete-selection t)
(put 'paredit-open-square 'delete-selection t)
(put 'paredit-doublequote 'delete-selection t)
(put 'paredit-newline 'delete-selection t)
This makes paredit-mode work with delete-selection-mode, replacing
its wrapping behavior. If I want to wrap, I'll do it with the
paredit-wrap-* commands explicitly.
Yesterday Kototama commented about another neat paredit addition: duplicating sexps. This is my take on that:
(defun paredit--is-at-start-of-sexp ()
(and (looking-at "(\\|\\[")
(not (nth 3 (syntax-ppss))) (not (nth 4 (syntax-ppss)))))
(defun paredit-duplicate-closest-sexp ()
(interactive)
(while (not (paredit--is-at-start-of-sexp))
(paredit-backward))
(set-mark-command nil)
(while (and (bounds-of-thing-at-point 'sexp)
(<= (point) (car (bounds-of-thing-at-point 'sexp)))
(not (= (point) (line-end-position))))
(forward-sexp)
(while (looking-at " ")
(forward-char)))
(kill-ring-save (mark) (point))
(paredit-newline)
(yank)
(exchange-point-and-mark))
Like Kototama
says in
his blogpost, duplicating a line is very useful, but sometimes
it leads to invalid sexps. In the blogpost he shows a snippet that
will duplicate the sexp after point. I immediately realized I had
really been wanting this.
The version listed here is a little modified: It will duplicate the
sexp you are currently inside, or looking at, or looking behind at.
So basically, point can be in any of these positions:
|(my sexp)
(my| sexp)
(my sexp)|
Insta-useful!
Programming any lisp? Then this paredit-inspired snippet may be for you.
(defun paredit-wrap-round-from-behind ()
(interactive)
(forward-sexp -1)
(paredit-wrap-round)
(insert " ")
(forward-char -1))
(define-key paredit-mode-map (kbd "M-)")
'paredit-wrap-round-from-behind)
With point in front of a sexp, paredit-wrap-round
(bound to M-(), will open a paren in front the the sexp,
and place the closing paren at the end of it. That's pretty handy.
This snippet does the same, but from the other end. It saves me
a C-M-b ever so often. I like it.
Annoyed when Emacs opens the window below instead at the side?
(defun toggle-window-split ()
(interactive)
(if (= (count-windows) 2)
(let* ((this-win-buffer (window-buffer))
(next-win-buffer (window-buffer (next-window)))
(this-win-edges (window-edges (selected-window)))
(next-win-edges (window-edges (next-window)))
(this-win-2nd (not (and (<= (car this-win-edges)
(car next-win-edges))
(<= (cadr this-win-edges)
(cadr next-win-edges)))))
(splitter
(if (= (car this-win-edges)
(car (window-edges (next-window))))
'split-window-horizontally
'split-window-vertically)))
(delete-other-windows)
(let ((first-win (selected-window)))
(funcall splitter)
(if this-win-2nd (other-window 1))
(set-window-buffer (selected-window) this-win-buffer)
(set-window-buffer (next-window) next-win-buffer)
(select-window first-win)
(if this-win-2nd (other-window 1))))))
This snippet toggles between horizontal and vertical layout of two windows.
Neat.
Ever open a file in the wrong window?
(defun rotate-windows ()
"Rotate your windows"
(interactive)
(cond ((not (> (count-windows)1))
(message "You can't rotate a single window!"))
(t
(setq i 1)
(setq numWindows (count-windows))
(while (< i numWindows)
(let* (
(w1 (elt (window-list) i))
(w2 (elt (window-list) (+ (% i numWindows) 1)))
(b1 (window-buffer w1))
(b2 (window-buffer w2))
(s1 (window-start w1))
(s2 (window-start w2))
)
(set-window-buffer w1 b2)
(set-window-buffer w2 b1)
(set-window-start w1 s2)
(set-window-start w2 s1)
(setq i (1+ i)))))))
This snippet flips a two-window frame, so that left is right, or up
is down. It's sanity preserving if you've got a sliver of OCD.
Ido gives fuzzy matching in my completing-read. I want that everywhere.
(require 'ido-ubiquitous)
(ido-ubiquitous-mode 1)
(defmacro ido-ubiquitous-use-new-completing-read (cmd package)
`(eval-after-load ,package
'(defadvice ,cmd (around ido-ubiquitous-new activate)
(let ((ido-ubiquitous-enable-compatibility nil))
ad-do-it))))
(ido-ubiquitous-use-new-completing-read webjump 'webjump)
(ido-ubiquitous-use-new-completing-read yas/expand 'yasnippet)
(ido-ubiquitous-use-new-completing-read yas/visit-snippet-file 'yasnippet)
ido-ubiquitous
delivers on that promise.
However, there is some discrepancies in
the completing-read
API between newer and older
versions regarding the case where you just press enter to choose the
first item.
To fix these, some of the newer usages of completing read need a
slightly different implementation. These tweaks fix that problem.
Uneven application of white-space is bad, m'kay?
(defun cleanup-buffer-safe ()
"Perform a bunch of safe operations on the whitespace content of a buffer.
Does not indent buffer, because it is used for a before-save-hook, and that
might be bad."
(interactive)
(untabify (point-min) (point-max))
(delete-trailing-whitespace)
(set-buffer-file-coding-system 'utf-8))
(add-hook 'before-save-hook 'cleanup-buffer-safe)
(defun cleanup-buffer ()
"Perform a bunch of operations on the whitespace content of a buffer.
Including indent-buffer, which should not be called automatically on save."
(interactive)
(cleanup-buffer-safe)
(indent-region (point-min) (point-max)))
(global-set-key (kbd "C-c n") 'cleanup-buffer)
I use these two literally all the time. The first one removes
trailing whitespace and replaces all tabs with spaces before save.
The last one I've got on a key - it also indents the entire buffer.
These might not be for everybody. Sometimes you do want tabs (I'm looking at
you Makefile
grrrrr). Then this isn't optimal.
The same can be said for when Emacs doesn't indent correctly. But
that is a horrid, unacceptable situation in any case. I always fix
those as soon as I can.
I find the default dired look a bit spammy, especially in narrow windows.
(require 'dired-details)
(setq-default dired-details-hidden-string "--- ")
(dired-details-install)
By installing M-x package-install
dired-details
and using this snippet, we hide all the
unnecessary ls
-details.
That rare occasion where you actually need that information, you can
show it with ) and hide again with (.
Like rename yesterday, I think
delete deserves a designated keybinding.
(defun delete-current-buffer-file ()
"Removes file connected to current buffer and kills buffer."
(interactive)
(let ((filename (buffer-file-name))
(buffer (current-buffer))
(name (buffer-name)))
(if (not (and filename (file-exists-p filename)))
(ido-kill-buffer)
(when (yes-or-no-p "Are you sure you want to remove this file? ")
(delete-file filename)
(kill-buffer buffer)
(message "File '%s' successfully removed" filename)))))
(global-set-key (kbd "C-x C-k") 'delete-current-buffer-file)
This is it. C-x C-k: file begone!
I like the feel between C-x k to kill the buffer
and C-x C-k to kill the file. Release ctrl to kill it a little,
hold to kill it a lot.
For some reason, renaming the current buffer file is a multi-step process in Emacs.
(defun rename-current-buffer-file ()
"Renames current buffer and file it is visiting."
(interactive)
(let ((name (buffer-name))
(filename (buffer-file-name)))
(if (not (and filename (file-exists-p filename)))
(error "Buffer '%s' is not visiting a file!" name)
(let ((new-name (read-file-name "New name: " filename)))
(if (get-buffer new-name)
(error "A buffer named '%s' already exists!" new-name)
(rename-file filename new-name 1)
(rename-buffer new-name)
(set-visited-file-name new-name)
(set-buffer-modified-p nil)
(message "File '%s' successfully renamed to '%s'"
name (file-name-nondirectory new-name)))))))
(global-set-key (kbd "C-x C-r") 'rename-current-buffer-file)
This defun fixes that. And unlike some other alternatives to perform
this common task, you don't have to type the name out from scratch -
but get the current name to modify. Like it should be.
When programming I tend to shuffle lines around a lot.
(defun move-line-down ()
(interactive)
(let ((col (current-column)))
(save-excursion
(forward-line)
(transpose-lines 1))
(forward-line)
(move-to-column col)))
(defun move-line-up ()
(interactive)
(let ((col (current-column)))
(save-excursion
(forward-line)
(transpose-lines -1))
(move-to-column col)))
(global-set-key (kbd "<C-S-down>") 'move-line-down)
(global-set-key (kbd "<C-S-up>") 'move-line-up)
Maybe not when I program elisp, since that's sexp-based, but for
other programming languages these two come in very handy. They
simply move the current line one step up or down.
Opening new lines can be finicky.
(defun open-line-below ()
(interactive)
(end-of-line)
(newline)
(indent-for-tab-command))
(defun open-line-above ()
(interactive)
(beginning-of-line)
(newline)
(forward-line -1)
(indent-for-tab-command))
(global-set-key (kbd "<C-return>") 'open-line-below)
(global-set-key (kbd "<C-S-return>") 'open-line-above)
With these shortcuts you can open a new line above or below the
current one, even if the cursor is midsentence.
Try it out, it's a nice convenience.
C-d on an empty line in the shell terminates the process.
(defun comint-delchar-or-eof-or-kill-buffer (arg)
(interactive "p")
(if (null (get-buffer-process (current-buffer)))
(kill-buffer)
(comint-delchar-or-maybe-eof arg)))
(add-hook 'shell-mode-hook
(lambda ()
(define-key shell-mode-map
(kbd "C-d") 'comint-delchar-or-eof-or-kill-buffer)))
With this snippet, another press of C-d will kill the buffer.
It's pretty nice, since you then just tap C-d twice to
get rid of the shell and go on about your merry way.
Actual changes lost in a sea of whitespace diffs?
(defun magit-toggle-whitespace ()
(interactive)
(if (member "-w" magit-diff-options)
(magit-dont-ignore-whitespace)
(magit-ignore-whitespace)))
(defun magit-ignore-whitespace ()
(interactive)
(add-to-list 'magit-diff-options "-w")
(magit-refresh))
(defun magit-dont-ignore-whitespace ()
(interactive)
(setq magit-diff-options (remove "-w" magit-diff-options))
(magit-refresh))
(define-key magit-status-mode-map (kbd "W") 'magit-toggle-whitespace)
This adds W to toggle ignoring whitespace in magit.
It has some weird interactions with the changed files list, in that
files with nothing but whitespace changes go missing. Toggle back to
find them again.
Tired of seeing stale dired buffers?
(global-auto-revert-mode 1)
(setq global-auto-revert-non-file-buffers t)
(setq auto-revert-verbose nil)
Auto revert mode looks for changes to files, and updates them for you.
With these settings, dired buffers are also updated. The last
setting makes sure that you're not alerted every time this happens.
Which is every time you save something.
You are using magit with your git, right?
(defadvice magit-status (around magit-fullscreen activate)
(window-configuration-to-register :magit-fullscreen)
ad-do-it
(delete-other-windows))
(defun magit-quit-session ()
"Restores the previous window configuration and kills the magit buffer"
(interactive)
(kill-buffer)
(jump-to-register :magit-fullscreen))
(define-key magit-status-mode-map (kbd "q") 'magit-quit-session)
This code makes magit-status
run alone in the frame,
and then restores the old window configuration when you quit out of magit.
No more juggling windows after commiting. It's magit bliss.
What are those line numbers for anyway?
(global-set-key [remap goto-line] 'goto-line-with-feedback)
(defun goto-line-with-feedback ()
"Show line numbers temporarily, while prompting for the line number input"
(interactive)
(unwind-protect
(progn
(linum-mode 1)
(goto-line (read-number "Goto line: ")))
(linum-mode -1)))
I don't have line numbers visible in the fringe of my Emacs. If I
want to go to a line number, that is usually because it is
referenced in an error message somewhere. Showing them all the time
is just noise.
Still, many people want line numbers visible. I guess that is
because they use them for navigation. This snippet shows line
numbers temporarily just when you're going to a line number
with goto-line
.
Notice the nice remap
-trick in the key binding. It will
remap all key bindings from goto-line
to
goto-line-with-feedback
. Neat!
Searching the web can also be improved with Emacs.
(global-set-key (kbd "C-x g") 'webjump)
(eval-after-load "webjump"
'(add-to-list 'webjump-sites
'("Urban Dictionary" .
[simple-query
"www.urbandictionary.com"
"http://www.urbandictionary.com/define.php?term="
""])))
Webjump let's you quickly search Google, Wikipedia, Emacs Wiki and
other pages. I've got it bound to C-x g.
This snippet adds Urban Dictionary to the list of pages, so the next
time you wonder what those dastardly kids mean when they write
faceroll or sassafrassa or Technotard or kthxbye or whatever else is
hip these days, well, then you can find out. With webjump
.
Need different settings for different machines?
(setq user-settings-dir
(concat user-emacs-directory "users/" user-login-name))
(when (file-exists-p user-settings-dir)
(mapc 'load (directory-files user-settings-dir nil "^[^#].*el$")))
These are the last lines of my init.el. They will load
any *.el
files in the
~/.emacs.d/users/user-login-name/
folder.
Anything specific for that machine goes there.
Do you program any elisp, at all, ever?
(autoload 'elisp-slime-nav-mode "elisp-slime-nav")
(add-hook 'emacs-lisp-mode-hook (lambda () (elisp-slime-nav-mode t)))
(eval-after-load 'elisp-slime-nav '(diminish 'elisp-slime-nav-mode))
Then you need to M-x package-install
elisp-slime-nav-mode
.
It lets you jump to the definition of a function
with M-., and back again afterwards with M-,.
That last line says that we want elisp-slime-nav-mode to continue
doing its work for us, but we no longer
want to be reminded of it.
Is your modeline chock full of minor-mode abbreviations and cruft?
(require 'diminish)
(diminish 'wrap-region-mode)
(diminish 'yas/minor-mode)
After a quick M-x package-install diminish
, you too can
have the pleasure of using a lot of minor modes, without those minor
modes making a mess of the modeline. Mmm.
As for diminish.el itself, it contains the most beautifully poetic
code commentary of all time. Here's an excerpt:
"When we diminish a mode, we are saying we want it to continue doing its
work for us, but we no longer want to be reminded of it. It becomes a
night worker, like a janitor; it becomes an invisible man; it remains a
component, perhaps an important one, sometimes an indispensable one, of
the mechanism that maintains the day-people's world, but its place in
their thoughts is diminished, usually to nothing. As we grow old we
diminish more and more such thoughts, such people, usually to nothing."
- Will Mengarini in
diminish.el
Tired of navigating back to where you were last in a file?
(require 'saveplace)
(setq-default save-place t)
(setq save-place-file (expand-file-name ".places" user-emacs-directory))
The saveplace
package is part of Emacs, and remembers the
position of point - even between emacs sessions.
The last line sets the path to where saveplace stores your position
data. Change it at your peril! *
* Ahem, there really is no peril. That was just melodrama.
Annoyed by those pesky ~
files?
(setq backup-directory-alist
`(("." . ,(expand-file-name
(concat user-emacs-directory "backups")))))
(setq vc-make-backup-files t)
Backup files are so very annoying, until the day they save your
hide. That's when you don't want to look back and say "Man, I
really shouldn't have disabled those stupid backups."
These settings move all backup files to a central location.
Bam! No longer annoying.
As an added bonus, that last line makes sure your files are backed
up even when the files are in version control. Do it.
Behold the very first lines in my .emacs.d/init.el
:
(if (fboundp 'menu-bar-mode) (menu-bar-mode -1))
(if (fboundp 'tool-bar-mode) (tool-bar-mode -1))
(if (fboundp 'scroll-bar-mode) (scroll-bar-mode -1))
(setq inhibit-startup-message t)
They hide the menu bar, tool bar, scroll bar and splash screen.
Doing so early avoids ever having to see them - not even for a brief
flash when starting Emacs.
These four lines move us into the tranquil zone of nothing but
the text. A raster interface can never hold the seeming
infinitude of Emacs functionality, so we just let it go.
"What I don't understand is: why should you ever care how your editor
looks, unless you're trying to win a screenshot competition? The
primary factor in looking good should be the choice of a good font
at a comfortable size, and a syntax coloring theme that you like.
And that is not something specific to an editor. Editors like Emacs
and vi have almost no UI! If Emacs is configured right, the only UI
it has is the modeline and the minibuffer."
- Vivek Haldar in
New Frontiers In Text Editing