Emacs Tips

2009 March 20, Friday 07:11 PM

1.1  languages

1.1.1  c/c++

1.1.1.1  insert header protection

Use this (very dumb) function to wrap your header file in #ifndef header protection:

1
2
3
4
5
6
7
8
9
10
11
12
(defun c/do-header-protection ()
  (interactive)
  (let ((name buffer-file-name))
    (when name
      (setq name (upcase (replace-regexp-in-string
                          "[^a-zA-Z]+" "_"
                          (file-name-nondirectory name)))))
    (save-excursion
      (goto-char (point-min))
      (insert "#ifndef " name "\n#define " name "\n\n")
      (goto-char (point-max))
      (insert "\n\n#endif\n"))))

1.2  flymake

1.2.1  using flymake in TTY/console emacs

I only use flymake with Perl at the moment and I only use TTY emacs. Flymake seems to assume one is a toolkit user (grrrr) so I wrote this:

1
2
3
4
5
6
7
8
9
10
11
12
13
(defun perl/next-flymake-error ()
  (interactive)
  (let ((err-buf nil))
    (condition-case err
        (setq err-buf (next-error-find-buffer))
      (error))
    (if err-buf
        (next-error)
        (progn
          (flymake-goto-next-error)
          (let ((err (get-char-property (point) 'help-echo)))
            (when err
              (message err)))))))

It will use a normal grep/error buffer first, if it doesn’t find one then it will take you to the next flymake error and describe it to you in the minibuffer. This defiantly won’t suit everyone but it works well with my work flow.

1.2.2  only have flymake do it’s thing on save

I really like flymake but it can be a total pain in the ass to have it highlighting stuff as erroneous whilst you’re editing it.

1
2
(setq flymake-no-changes-timeout 9999
      flymake-start-syntax-check-on-newline nil)

1.2.3  add Perl test files to flymakes ‘can do’ list

1
2
(push (list "\\.\\([pP][Llm]\\|al\\|t\\)$"
      'flymake-perl-init) flymake-allowed-file-name-masks)

1.3  dired

1.3.1  only open one dired buffer at most

If you don’t want your buffer list cluttering up with dired buffers then put the following in your .emacs:

1
(define-key dired-mode-map (kbd "RET") 'dired-find-alternate-file)

1.3.2  delete directories recursively

If you want to be able to remove directories recursively from dired then add the following to your .emacs:

1
(setq dired-recursive-deletes 'top)

The argument can be one of the following (straight from the docs to dissociate myself from that patronising “DANGEROUS” comment):

- nil :?? means no recursive deletes. - always :?? means delete recursively without asking. This is DANGEROUS! - top :?? means ask for each directory at top level, but delete its subdirectories without asking. - anything else :?? means ask for each directory.

1.3.3  edit filenames from the buffer

What a great little tip this is, you can edit filenames from within the dired buffer using wdired-change-to-wdired-mode. I like to have this bound to e which previously would have done the same thing as RET:

1
2
3
(add-hook 'dired-mode-hook
          '(lambda ()
             (define-key dired-mode-map "e" 'wdired-change-to-wdired-mode)))

1.4  third-party integration

1.4.1  functionality of grep.el on the command line

This depends on emacsclientw.sh and GNU grep or compatible.

Roland McGrath put all of that effort into putting grep into emacs and now I’m going to put emacs into grep…. kind of.

If you are in the command line and want to use the emacs grep without actually moving to emacs and finding the shell cwd then you could use the following script:

1
2
3
#!/usr/bin/env sh

grep -R -nH -e $@ . | emacsclientw.sh -e '(grep-mode)'

(Check back later, this need vastly improving.)

You can use this script to customise to your hearts content. In fact you can use this technique to load anything into emacs from the shell – the possibilities are endless.

1.4.2  export variables to emacs

I work on lots of projects at once each requiring different environments. As I run one instance of emacs all of the time I don’t really want to exit, build the environment and then restart emacs. It’s not a problem for small variables but when you have monsters like PERL5LIB then using setenv is no good either. What I’ve done is put this function in my .zshrc (I think it’s bash compatible too):

1
2
3
4
5
6
7
8
9
10
function export-emacs {
    if [ "$(emacsclient -e t)" !?? 't' ]; then
        return 1
    fi

    for name in "${@}"; do
        value??$(eval echo \"\$${name}\")
        emacsclient -e "(setenv \"${name}\" \"${value}\")" >/dev/null
    done
}

Now from the command line you can do this:

1
export-emacs PATH PERL5LIB DEBUG

Unfortunately it doesn’t support the VAR??blah syntax you may know from your shell so if a variable isn’t set then what you would have to do is this:

1
2
3
DEBUG??1
export DEBUG
export-emacs DEBUG

or:

1
2
export DEBUG??1
export-emacs DEBUG

This combined with the cd function in the [[Shell]] section means that you can tell emacs about things like PERL5LIB automatically so that, for example, perldb works.

1.4.3  have emacsclient accept stuff from stdin

Haven’t you always wished you could do something like this:

1
2
3
4
# cat all executables into emacs
for f in *; do
  [ -x "${f}" -a ! -d "${f}" ] && cat "${f}"
done | emacsclient

Some simple elisp and shell can sort you out, here is the lisp:

1
2
3
4
5
(defun fake-stdin-slurp (filename)
  "Emulate stdin slurp using emacsclient hack"
  (switch-to-buffer (generate-new-buffer "*stdin*"))
  (insert-file filename)
  (end-of-buffer))

And the shellscript (which is very basic and you should probably hack on it to suit your needs):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env bash

# desc:
#     Allows stdin to be 'piped' to an emacs server.
#
# options:
#     none
#
# usage:
#     $ echo "hello there" | emacsclientw.sh
#     $ cat ~/.emacs | emacsclientw.sh
#     $ emacsclientw.sh ~/.emacs
#
# author:
#     Phil Jackson (phil@shellarchive.co.uk)

unset DISPLAY

tmp_file="$(mktemp)"
lisp_to_accept_file="(phil/fake-stdin-slurp \"${tmp_file}\")"

if [ ! -t 0  ]; then
    cat > "${tmp_file}"

    emacsclient -a emacs -e "${lisp_to_accept_file}" ${@}

    if [ ${?} -ne 0 ]; then
        echo "failed: your input was saved in '${tmp_file}'"
    else
        rm -f "${tmp_file}"
    fi
else
    # nothing from stdin
    emacsclient -n -a emacs ${@}
fi

Call this emacsclientw.sh or something and then pipe some text into it. Hopefully, if you have a server running you’ll get a buffer called stdin with the text in it.

1.5  buffers

1.5.1  gnus style groups in ibuffer

ibuffer has the ability to display its buffers grouped by various attributes like mode, name and filename. This is great for things like [[Gnus]] and Erc as they tend to spawn lots of buffers that you don’t want to ignore yet don’t want moving up and down the buffer list.

Here is the configuration I use at the moment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
;; default groups for ibuffer
(setq ibuffer-saved-filter-groups
      (quote (("default"
               ("dired" (mode . dired-mode))
               ("perl" (mode . cperl-mode))
               ("erc" (mode . erc-mode))
               ("planner" (or
                           (name . "^\\*Calendar\\*$")
                           (name . "^diary$")))
               ("emacs" (or
                         (name . "^\\*scratch\\*$")
                         (name . "^\\*Messages\\*$")))
               ("gnus" (or
                        (mode . message-mode)
                        (mode . bbdb-mode)
                        (mode . mail-mode)
                        (mode . gnus-group-mode)
                        (mode . gnus-summary-mode)
                        (mode . gnus-article-mode)
                        (name . "^\\.bbdb$")
                        (name . "^\\.newsrc-dribble")))))))

;; ibuffer, I like my buffers to be grouped
(add-hook 'ibuffer-mode-hook
          (lambda ()
            (ibuffer-switch-to-saved-filter-groups
             "default")))
h3. hide buffers in ibuffer

If you don’t want to see certain buffers in your ibuffer then you can use `ibuffer-never-show-predicates’ to hide them:

1
2
3
(setq ibuffer-never-show-predicates
      (list "\\*Completions\\*"
            "\\*vc\\*"))

1.6  editor

1.6.1  (dec|inc)rement number at point

The following functions allow you increment or decrement what they think is a number under point:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(defun increment-number-at-point (&optional amount)
  "Increment the number under point by `amount'"
  (interactive "p")
  (let ((num (number-at-point)))
    (when (numberp num)
      (let ((newnum (+ num amount))
         (p (point)))
    (save-excursion
      (skip-chars-backward "-.0123456789")
      (delete-region (point) (+ (point) (length (number-to-string num))))
      (insert (number-to-string newnum)))
    (goto-char p)))))

(defun decrement-number-at-point (&optional amount)
  (interactive "p")
  "Decrement the number under point by `amount'"
  (increment-number-at-point (- (abs amount))))

I don’t use the arrow keys so I have the above functions bound like this:

1
2
(define-key global-map [up] 'increment-number-at-point)
(define-key global-map [down] 'decrement-number-at-point)

Which makes them pretty handy for keyboard macros.

1.6.2  change the place the backup files go

If you want to stop littering directories with ~ files then try redefining make-backup-file-name with something along the lines of:

1
2
(defun make-backup-file-name (FILE)
  (concat "~/.backups/emacs/" (file-name-nondirectory FILE)))

Even better you can backup according to the date:

1
2
3
4
5
6
(defun make-backup-file-name (FILE)
  (let ((dirname (concat "~/.backups/emacs/"
                         (format-time-string "%y/%m/%d/"))))
    (if (not (file-exists-p dirname))
        (make-directory dirname t))
    (concat dirname (file-name-nondirectory FILE))))

1.6.3  cut/copy a line without marking it

To have C-w and M-w function on the line when the kill-ring is empty evaluate the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(defadvice kill-ring-save (before slickcopy activate compile)
  "When called interactively with no active region, copy
 a single line instead."
  (interactive
   (if mark-active (list (region-beginning) (region-end))
     (list (line-beginning-position)
           (line-beginning-position 2)))))

(defadvice kill-region (before slickcut activate compile)
  "When called interactively with no active region, kill
 a single line instead."
  (interactive
   (if mark-active (list (region-beginning) (region-end))
     (list (line-beginning-position)
           (line-beginning-position 2)))))

1.6.4  bookmaking with org-mode

If you haven’t tried Org Mode then go and take a quick look at a tutorial.

I like to use it and remember mode to keep my bookmarks for me. Just visit a file/web page/email and then do an M-x remember and add the item to a bookmarks.org file. With it’s clever hierarchy handling you can keep bookmarks well organised for different projects.

1.6.5  adding all files in a directory to the agenda

All .org files in org-directory will now appear in the agenda.

1
2
3
4
5
6
7
(setq org-agenda-files
 (append org-agenda-files
  (mapcar
   '(lambda (x)
     (concat (file-name-as-directory org-directory) x))
   (remove-if-not '(lambda (x) (string-match "\\.org" x))
 (directory-files org-directory)))))


blog comments powered by Disqus