deft

Deft as a file search tool

We’ve looked at using deft to make quick notes, with a couple of tweaks. I found I really like the deft interface for finding files. In particular, in the directory where I keep my blog posts, I have a large number of org files, and the deft interface is a very convenient way to search them to see in which posts I have mentioned particular things. I like the overview that deft gives you, and the incremental search as you type.

However, by default deft launches in my deft notes directory. To get around this I created a simple function to wrap around deft:

;;function to run deft in specified directory
(defun bjm-deft (dir)
  "Run deft in directory DIR"
  (setq deft-directory dir)
  (switch-to-buffer "*Deft*")
  (kill-this-buffer)
  (deft)
  )

This sets the desired directory to run deft, kills any current deft buffer (which may be in the wrong directory) and then starts deft.

I then bound this to convenient keys:

(global-set-key (kbd "C-c d")
                (lambda () (interactive) (bjm-deft "~/docs/deft")))
(global-set-key (kbd "C-c D")
                (lambda () (interactive) (bjm-deft "~/docs/emacs/blog")))
Advertisement

Tweaking deft: no spaces in file names

Building on my earlier posts on deft, I have another minor improvement I wanted to make. I really like the way deft creates files from the search string, generating the file name from the string. The only problem is that I don’t want spaces in my file names, which I may well have in my deft search string.

My solution was to add the following advice to the function in deft that creates new files, telling it to replace spaces in the search string with hyphens before creating the file.

;;advise deft-new-file-named to replace spaces in file names with -
(defun bjm-deft-strip-spaces (orig-fun &rest file)
  ;;this probably doesn't need to be done in three steps!
  (setq name (pop file))
  (setq name (replace-regexp-in-string " " "-" name))
  (push name file)
  (apply orig-fun file)
  )

(advice-add 'deft-new-file-named :around #'bjm-deft-strip-spaces)

Maybe someone can help me improve my novice lisp programming here, as I am sure there must be a more direct way to use replace-regexp-in-string here!

I think this is a nice illustration of how the user can relatively easily adjust the behaviour of a function which is quite buried inside a package. You have to be a bit careful when advising functions that you don’t introduce unwanted behaviour if that function is called in a different context, but in this case it should be safe as the function deft-new-file-named is not used elsewhere.

In a comment on my earlier post Kaushal Modi linked to his solution to this, which also removes any upper cases.

Update

The wise commenters below pointed out that my function above is a bit dangerous, since the variable name that I use has global scope so could modify another variable called name elsewhere. This can be solved by using (let ...) to give the variable local scope, as Kaushal Modi points out, but I ended up going with the compact version suggested by Noam Postavsky:

;;advise deft-new-file-named to replace spaces in file names with -
(defun bjm-deft-strip-spaces (args)
  "Replace spaces with - in the string contained in the first element of the list args. Used to advise deft's file naming function."
  (list (replace-regexp-in-string " " "-" (car args)))
  )
(advice-add 'deft-new-file-named :filter-args #'bjm-deft-strip-spaces)

Which uses filter-args in the advice function to specify that my bjm-quit-strip-spaces function is applied to the arguments to deft-new-file-named before they are passed to the latter function.

Tweaking deft: improving navigation

Following up on my earlier posts on deft, I had another small tweak I wanted to make, that also illustrates a useful technique for customised Emacs.

In the deft buffer, each entry takes two lines so I have to hit the up/down arrow key twice to move between them. To simplify this, I tweaked the keybindings in deft-mode to make the arrow keys jump 2 lines instead of 1, but only when deft-mode is active:

;;override normal settings to jump 2 lines as deft has time stamps on second line
(define-key deft-mode-map (kbd "<down>")
  (lambda () (interactive) (next-line 2)))
(define-key deft-mode-map (kbd "<up>")
  (lambda () (interactive) (next-line -2)))

Here, lambda is the name used for anonymous functions in emacs lisp, which are useful in cases like this where I want to make a simple function that I won’t need to use anywhere else. The function is very simple, it just calls the normal line movement command next-line with an argument of 2 or -2 to skip up or down by 2 lines. We then define the arrow keys to call this function, but only in deft-mode-map, which contains the keybindings for deft-mode.

Touch-typists (which, to my shame I am not) generally prefer to use C-n and C-p to move up and down lines, so those could be used instead here.

Update

Commenter Kaushal Modi pointed out that I should not have been seeing the entries in the deft buffer taking up more than one line. It turns out the problem was that I had global-visual-line-mode switched on on my emacs config file which caused the problem.

This means the above changes are redundant, but I’ll leave them up as an example of adding keybindings to a specific mode.

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…

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.