Month: May 2015

Tweaking deft: quicker notes

I posted recently about using deft to make quick notes, and after using it for a bit I like it a lot, but wanted to make a few tweaks to the way it works. This gave me an excuse to learn a few lisp techniques, which other lisp novices might find useful.

I really like the way that org-capture lets me quickly make a note and return me seamlessly to where I was before, and so I wanted deft to be a bit more like that. By default, if I launch deft and make a note, I have to:

  • save the buffer
  • kill the buffer which takes me back to the deft menu
  • quit deft

This is too much work. Okay, I could save the note buffer and then switch back to my original buffer using e.g. winner-undo but that is still too much work!

Instead I’ve dabbled in a bit of lisp coding which I think illustrates a few nice ways you can customise your emacs with minimal lisp skills (like mine).

To start with I made my first attempt to advise a function. This is a way to make a function built into emacs or a package behave differently. Here I advise deft to save my window configuration before it launches:

;;advise deft to save window config
(defun bjm-deft-save-windows (orig-fun &rest args)
  (setq bjm-pre-deft-window-config (current-window-configuration))
  (apply orig-fun args)
  )

(advice-add 'deft :around #'bjm-deft-save-windows)

Side note: in principal, I think something similar could be done using hooks, but my reading of the deft code suggested that the hooks would run after the window configuration had been changed, which is not what I wanted.

I then make a function to save the current buffer, kill the current buffer, kill the deft buffer, and then restore the pre-deft configuration. I then set up a shortcut for this function.

;;function to quit a deft edit cleanly back to pre deft window
(defun bjm-quit-deft ()
  "Save buffer, kill buffer, kill deft buffer, and restore window config to the way it was before deft was invoked"
  (interactive)
  (save-buffer)
  (kill-this-buffer)
  (switch-to-buffer "*Deft*")
  (kill-this-buffer)
  (when (window-configuration-p bjm-pre-deft-window-config)
    (set-window-configuration bjm-pre-deft-window-config)
    )
  )

(global-set-key (kbd "C-c q") 'bjm-quit-deft)

So now, I can launch deft with C-c d make a quick note and then quit with C-c q to get back to where I was. This is pleasingly close to the experience of org-capture.

Note that bjm-quit-deft is not bullet proof; there is nothing to stop you running it in a buffer that is not a note opened from deft, but if you do, nothing terrible will happen. If I was a better lisp programmer I could probably come up with a way to test if the current buffer was opened from deft and issue a warning from bjm-quit-deft if not, but I am not much of a lisp programmer!

More to follow on tweaking deft…

Advertisement

Expand region

I posted recently about cutting text by word, line and sentence, but by default most of the commands cut from the point to the beginning or end of the word/line/sentence. I previously posted a nice fix for cutting a whole line, but in this post I’ll cover a more general solution.

The package expand region expands the selected region by semantic units i.e. going from word to sentence to paragraph in prose, but also by sensible units for code as well.

If you use my recommended setup, prelude then the command you need is already there – just hit C-– and away you go.

Otherwise, install the package expand-region, and then add the following to your emacs config file

;;expand region
(require 'expand-region)
(global-set-key (kbd "C-=") 'er/expand-region)

I would make an animated gif to illustrate this, but there is a great emacs rocks video by the author of the package. Check out the other videos in that series for more good things.

Delete/Cut whole words or sentences

Here is a short tip for beginners.

Use M-<DEL> to delete (“kill” in Emacs terminology) back to the start of a word, and M-d to delete up to the end of a word.

Use C-x DEL to delete back to the beginning of a sentence, and M-k to delete forward to the end of a sentence (we already covered moving by sentences and how Emacs defines a sentence).

Finally, refer to this post about deleting a whole line.

Note that all of these commands put the text in your clipboard so you can paste it elsewhere.

Dired: rename multiple files

Following on from the previous dired tips we can do much more in dired. A very powerful feature is that you can switch the dired buffer which lists the files into editable mode. Then you can directly edit file names, links and so on. Here we will look at using dired to rename multiple files in a nice visual way.

Now we will use dired to rename multiple files at the same time. Suppose in some directory we have a bunch of files called test_foo_01.dat, test_foo_02.dat, etc and we wanted to rename them to replace foo with bar, then we would do the following:

  • use C-x d to enter dired and choose the directory with the files in
  • use C-x C-q to turn dired into editing mode. You can then edit the file names by hand or
  • use M-% to replace foo with bar in the dired buffer. This will change the file names
  • use C-c C-c so apply the changes, or C-c ESC to cancel

The animation below shows this example. Of course you can also manually change the name of the file, as I do for the last file on the list.

dired-rename.gif

Make quick notes with deft

I tend to organise my notes into files related to a specific topic or project, so I find org-mode with org-capture very useful to add quick notes to a specific file, like adding a TODO item or an idea for a blog post (I will post lots more about org-mode in due course, but there is an overview on my tutorial page). However, if I want to make a more general note that doesn’t fit into a category (say notes from a meeting), then deft is a nice alternative.

This post is a short introduction to deft, and I will describe some tweaks I have made in subsequent posts.

Deft is an emacs package (available through package-list-packages) that lets you quickly create, search and add to files containing notes. It is inspired by the Mac programme Notational Velocity.

My use case is that I launch deft, start typing a search string to very quickly find the file I want to add notes to, or create a new file on the fly, and then add my notes, quit deft and be back to where I was before.

Deft works simply by having all of your note files in a single directory, and the files themselves are simple text (org-mode if you like) files that can be viewed and edited anywhere else as well as via deft. Deft is really just a nice quick interface for finding/creating and opening the right file for editing.

To use deft, launch it with M-x deft and you will see a list of the files in your deft directory with short summaries. Start typing a search string and the list will dynamically filter down to files that match the string in their file name or body text. Use arrow keys to move up and down through the list of files and hit return to open that file for editing.

If no files match the search string then hitting enter creates a new file with a name taken from the search string. If you want to create a new file with a specific name, use C-c C-n.

You can also rename C-c C-r and delete C-c C-d files from the deft buffer.

Use C-c C-q to quit deft.

This is best illustrated with a couple of examples. In the first example I launch deft and type a search string to find an existing file and then open that file for editing. In this case I am looking for the file about an open day in 2014.

deft1.gif

In the second example I launch deft and type a search string that doesn’t match any files and so create a new file with a name based on the search string. In this case, my search string “open day 2015” doesn’t match anything so deft creates a new file for me.

deft2.gif

Once you have installed deft, add the following to your emacs config file to get the behaviour described above

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; deft                                                                   ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'deft)
(setq deft-directory "~/docs/deft")
(setq deft-extensions '("org"))
(setq deft-default-extension "org")
(setq deft-text-mode 'org-mode)
(setq deft-use-filename-as-title t)
(setq deft-use-filter-string-for-filename t)
(setq deft-auto-save-interval 0)
;;key to launch deft
(global-set-key (kbd "C-c d") 'deft)

With this setup, my deft files are all stored in ~/docs/deft/ and have .org file extensions, and deft will open them for editing in org-mode.

The option (setq deft-use-filename-as-title t) tells deft to use the search string to generate the filename for a new file if the string does not match an existing file.

Those are the basics of deft, but stay tuned for some tweaks that (for me at least) make the experience even smoother.

Update

An update to deft introduced a new way to tell deft to use the filter to create the filename

(setq deft-use-filter-string-for-filename t)

I have added that to the deft setup code above. This solves an issue raised in the comments below.

Change case of text

By default, you can use M-c to change the case of a character at the cursor’s position. This also jumps you to the end of the word. However it is far more useful to define a new function by adding the following code to your emacs config file. Once you have done this, M-c will cycle through “all lower case”, “Initial Capitals”, and “ALL CAPS” for the word at the cursor position, or the selected text if a region is highlighted.

This is taken from ergoemacs – a useful resource for all things emacs.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; change case of letters                                                 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; http://ergoemacs.org/emacs/modernization_upcase-word.html
(defun toggle-letter-case ()
  "Toggle the letter case of current word or text selection.
Toggles between: “all lower”, “Init Caps”, “ALL CAPS”."
  (interactive)
  (let (p1 p2 (deactivate-mark nil) (case-fold-search nil))
    (if (region-active-p)
        (setq p1 (region-beginning) p2 (region-end))
      (let ((bds (bounds-of-thing-at-point 'word) ) )
        (setq p1 (car bds) p2 (cdr bds)) ) )

    (when (not (eq last-command this-command))
      (save-excursion
        (goto-char p1)
        (cond
         ((looking-at "[[:lower:]][[:lower:]]") (put this-command 'state "all lower"))
         ((looking-at "[[:upper:]][[:upper:]]") (put this-command 'state "all caps") )
         ((looking-at "[[:upper:]][[:lower:]]") (put this-command 'state "init caps") )
         ((looking-at "[[:lower:]]") (put this-command 'state "all lower"))
         ((looking-at "[[:upper:]]") (put this-command 'state "all caps") )
         (t (put this-command 'state "all lower") ) ) )
      )

    (cond
     ((string= "all lower" (get this-command 'state))
      (upcase-initials-region p1 p2) (put this-command 'state "init caps"))
     ((string= "init caps" (get this-command 'state))
      (upcase-region p1 p2) (put this-command 'state "all caps"))
     ((string= "all caps" (get this-command 'state))
      (downcase-region p1 p2) (put this-command 'state "all lower")) )
    )
  )

;;set this to M-c
(global-set-key "\M-c" 'toggle-letter-case)

Editing your emacs config file

To configure emacs you can use the customize interface M-x customize, but I’ll often provide bits of code to put into your emacs config file. This is a file containing pieces of emacs lisp (the language emacs is written in) that alter the behaviour of emacs.

Your emacs config file is probably in one of three places:

  1. in ~/.emacs though this is now a bit outdated and instead it will usually be in
  2. ~/.emacs.d/init.el
  3. but, if you are using my recommended set up prelude then your emacs config file will be in ~/.emacs.d/personal/. Any .el file in that directory will be read when emacs starts.

Once you make a change to your emacs config file, you can highlight the section of code and run M-x eval-region, or if it is a big change, you might need to restart emacs for it to take effect properly.