13 Jan 2015
I've been posting a lot about dired
lately, and with good cause. A
lot of people say that org-mode
is the killer app of Emacs, but
dired
should be in that group as well, especially if you count
tramp
as part of dired
. Below, I'll list a few dired options in my
config that deviate from the defaults.
dired-listing-switches
This is the essence of what dired
presents and how it presents it.
A great thing about it is that these are just the ls
switches, so
you can look them up with info ls
.
(setq dired-listing-switches "-laGh1v --group-directories-first")
l
: Is the only mandatory one.
a
: Means to list invisible files.
G
: Don't show group information. These days, when there are more
laptops than people, the group info is rarely useful.
h
: Human readable sizes, such as M for mebibytes.
1v
: Affects the sorting of digits, hopefully in a positive way.
--group-directories-first
: self-explanatory, I like to have the directories on the top,
separate from the files.
On recursion
(setq dired-recursive-copies 'always)
(setq dired-recursive-deletes 'always)
These settings make dired skip the confirmation when you copy or
delete a directory that contains other directories. What's the worse
that could happen, right?
- rm -rf /usr /lib/nvidia-current/xorg/xorg
+ rm -rf /usr/lib/nvidia-current/xorg/xorg
12 Jan 2015
Today, I'll continue with the trend of posting a small piece of my
config when I don't have the time to post something more substantial.
Some code
This one looks nice, although it only works on systems with /usr/bin/du
,
which actually comprise 100% of the systems that I use:
(defun dired-get-size ()
(interactive)
(let ((files (dired-get-marked-files)))
(with-temp-buffer
(apply 'call-process "/usr/bin/du" nil t nil "-sch" files)
(message
"Size of all marked files: %s"
(progn
(re-search-backward "\\(^[ 0-9.,]+[A-Za-z]+\\).*total$")
(match-string 1))))))
On doing a search, turns out that I got this code from
the wiki at some
point. I can confirm that, unlike some of the other code on the wiki,
this one still works as advertised: you can use it on a directory or
on a series of marked files and directories.
Standard dired
marking
In dired
, you can:
- mark an item with m
- unmark an item with DEL
- inverse selection with t
- unmark everything with U
Compared to this, the selection with the mouse and the control and
shift keys that many file browsers use looks like kindergarten.
This new action, getting the size of marked things, I've bound to z:
(define-key dired-mode-map (kbd "z") 'dired-get-size)
11 Jan 2015
This is just a small improvement to make e.g. \\(
show up in
regular expressions without the escape chars, but instead fontified
with font-lock-keyword-face
. It doesn't affect the underlying code
at all, just makes it look nicer. For the \\|
I chose ∨
- the
logical or character.
The code
(defun fontify-glyph (item glyph)
`((,item
(0 font-lock-keyword-face t)
(0 (prog1
(compose-region (match-beginning 0)
(match-end 0)
,glyph) nil)))))
(font-lock-add-keywords 'emacs-lisp-mode
(fontify-glyph "\\\\\\\\|" "∨"))
(font-lock-add-keywords 'emacs-lisp-mode
(fontify-glyph "\\\\\\\\(" "("))
(font-lock-add-keywords 'emacs-lisp-mode
(fontify-glyph "\\\\\\\\)" ")"))
How it looks like
At first, I wanted to just inline a picture,
but then I thought that htmlize-buffer
would be able to handle it.
It didn't, so I just edited a small snippet by hand:
(or (string-match "^([^\n%|]*?)|(([^\n]*)?$" str)
(string-match "^([^\n%|]*?)(%[^\n]*)?$" str))
It's really satisfying to see those escape chars vanish as I type in
a capture group in the regex, especially with the help of
lispy-mode.
Here are some relevant tests for the regex support:
(should (string= (lispy-with "\"a regex \\\\|\"" "(")
"\"a regex \\\\(|\\\\)\""))
(should (string= (lispy-with "\"\\\\(|foo\\\\)\"" "\C-?")
"\"|foo\""))
(should (string= (lispy-with "\"\\\\(foo\\\\)|\"" "\C-?")
"\"foo|\""))
(should (string= (lispy-with "\"|\\\\(foo\\\\)\"" "\C-d")
"\"|foo\""))
(should (string= (lispy-with "\"\\\\(foo|\\\\)\"" "\C-d")
"\"foo|\""))
10 Jan 2015
In the comments to
my previous post on ansi-term, I discovered
sane-term - a package that
cycles though your terminals in Emacs, as well as implements some of
the tips that I gave. While it's nice and all, and you should check it
out if you're looking for something like that, it's not really for
me. I will describe the system that I'm currently using below.
What is the best list length for cycling?
In my opinion, it's one or two. If it's one, you're not really
cycling, if it's two, it's fine. Anything more than that causes
stress, since you have to check each time if the outcome of the cycle
ended up being the one that you wanted.
That's why I usually have only one *ansi-term*
active in my Emacs
session at all times. Here's how it looks like:
(defun terminal ()
"Switch to terminal. Launch if nonexistent."
(interactive)
(if (get-buffer "*ansi-term*")
(switch-to-buffer "*ansi-term*")
(ansi-term "/bin/bash"))
(get-buffer-process "*ansi-term*"))
(defalias 'tt 'terminal)
At one point, I had terminal
bound to C-t, until I found
a command even better suited for that binding, which was
smex. The actual terminal
command isn't bound right now, I just launch it from smex
on very
rare occasions.
How I launch terminal 95% of the time
From dired
of course. The shell's natural way of switching the
directory with cd
is extremely inefficient compared to dired
. So
any time I want to have a shell in a specific directory, I first
navigate there with dired
, sometimes combined with
ido-find-file
. Then I get my current *ansi-term*
and tell it to
switch to the current dired
buffer's directory with `
binding:
(define-key dired-mode-map (kbd "`") 'dired-open-term)
(defun dired-open-term ()
"Open an `ansi-term' that corresponds to current directory."
(interactive)
(let ((current-dir (dired-current-directory)))
(term-send-string
(terminal)
(if (file-remote-p current-dir)
(let ((v (tramp-dissect-file-name current-dir t)))
(format "ssh %s@%s\n"
(aref v 1) (aref v 2)))
(format "cd '%s'\n" current-dir)))))
I also have a similar eshell
setup, although I have yet to
comprehend why eshell
is great and am using *ansi-term*
most of the
time instead.
(define-key dired-mode-map (kbd "'")
(lambda ()
(interactive)
(eshell-cmd
(format "cd %s"
(expand-file-name
default-directory)))))
How I launch dired
100% of the time
With dired-jump
, of course. This command will examine your current
buffer's default-directory
and open a dired
buffer there. All you
need is:
The dired-jump
command will be bound automatically to C-x
C-j. I have it also bound to C-:, since that's more
convenient to press with my keyboard layout.
It's also better in the common situation when I want to jump to a
dired
buffer from *ansi-term*
. In that situation, C-x
C-j will not work by default, and will call term-line-mode
instead. But it will work once you are in term-line-mode
. You can
go back to the default term-char-mode
with C-x C-k. To
avoid this nonsense, just bind dired-jump
to some binding that's
convenient for you and works from *ansi-term*
.
What I do when I need more than one terminal
Then I just name one: since the default one is supposed to be named
*ansi-term*
, if I create one named e.g. *jekyll*
, it will be
ignored by dired-open-term
. This is exactly what I want, since I just
create named terminals for long running processes like jekyll serve
.
And I can switch to the named terminals with just ido-switch-buffer
.
Here is the very simple code:
(defun named-term (name)
(interactive "sName: ")
(ansi-term "/bin/bash" name))
09 Jan 2015
On seeing
this Emacs Stack Exchange question,
it occurred to me that if some config code is old for me, it's not old
for the new Emacs users. So I'll share one of the old ido-find-file
hacks that I've been using for ages.
This song is an oldie ...but, uh ... pause
Well, it's an oldie where I come from.
-- Marty
The code
This is the original code that I was using:
(defun oleh-ido-setup-hook ()
(define-key ido-file-dir-completion-map "~"
(lambda ()
(interactive)
(ido-set-current-directory "~/")
(setq ido-exit 'refresh)
(exit-minibuffer))))
(add-hook 'ido-setup-hook 'oleh-ido-setup-hook)
The generalization
It wouldn't be a LISP if I couldn't generalize the code:
(defun ido-find-file-jump (dir)
"Return a command that sends DIR to `ido-find-file'."
`(lambda ()
(interactive)
(ido-set-current-directory ,dir)
(setq ido-exit 'refresh)
(exit-minibuffer)))
And here's how to leverage this generalization:
(defun oleh-ido-setup-hook ()
(define-key ido-file-dir-completion-map "~"
(ido-find-file-jump "~/"))
(define-key ido-file-dir-completion-map "!"
(ido-find-file-jump "~/Dropbox/source/site-lisp/"))
(define-key ido-file-dir-completion-map "@"
(ido-find-file-jump "~/git/lispy/")))
Note that this is pretty ugly, implementation-wise, since
ido-find-file-jump
would be called three times each time you do an
ido
related command, like ido-switch-buffer
etc.
I would have preferred to do it like this instead:
(eval-after-load "ido"
`(progn
(define-key ido-file-dir-completion-map "~"
(ido-find-file-jump "~/"))
(define-key ido-file-dir-completion-map "!"
(ido-find-file-jump "~/Dropbox/source/site-lisp/"))
(define-key ido-file-dir-completion-map "@"
(ido-find-file-jump "~/git/lispy/"))))
But, for some strange reason, ido
keeps overriding
ido-file-dir-completion-map
and I actually have to re-set my
bindings in ido-setup-hook
.
The further generalization
Here is the final iteration of the code:
(defvar oleh-ido-shortcuts
'(("~/" "~")
("~/Dropbox/source/site-lisp/" "!")
("~/git/lispy/" "@")))
(mapc (lambda (x)
(setcar x (ido-find-file-jump (car x))))
oleh-ido-shortcuts)
(defun oleh-ido-setup-hook ()
(mapc
(lambda (x)
(define-key ido-file-dir-completion-map (cadr x) (car x)))
oleh-ido-shortcuts))
(add-hook 'ido-setup-hook 'oleh-ido-setup-hook)
The customize tricks
"Custom setters?
In my Elisp?"
It's more likely than you think.
Note that the mapc
statement needs to be evaluated if I dynamically
modify oleh-ido-shortcuts
. This isn't a problem for me, but if I
wanted to package a code like this, I would define
oleh-ido-shortcuts
like this:
(defcustom oleh-ido-shortcuts
'(("~/" "~")
("~/Dropbox/source/site-lisp/" "!")
("~/git/lispy/" "@"))
"A list of directory-shortcut pairs for `ido-find-file'."
:set (lambda (symbol value)
(set-default
symbol
(mapcar
(lambda (x)
(if (stringp (car x))
(cons (ido-find-file-jump (car x))
(cdr x))
x))
value))))
Now, this should work:
(csetq oleh-ido-shortcuts
(progn
(setcar (rassoc '("@") oleh-ido-shortcuts)
"~/git/worf")
oleh-ido-shortcuts))
(csetq oleh-ido-shortcuts
(cons '("~/git/" "^")
oleh-ido-shortcuts))
Here, the appropriate lambda is auto-generated by using the :set
property of oleh-ido-shortcuts
.
And csetq
is just a customize
-aware version of setq
:
(defmacro csetq (variable value)
`(funcall (or (get ',variable 'custom-set) 'set-default)
',variable ,value))