14

90% of the time I visit a directory in Emacs, the first thing I do is move point to a specific file and open it. A lot of times, the file I need to open is very predictable. For instance:

  • If there are .tex files in this directory, I usually go straight to master.tex.
  • At the root of Android projects, it's commonly AndroidManifest.xml.
  • In a directory where I develop an emacs package, it's almost always the package's main source file. That's usually, but not always, the only .el file in this directory.

Is there a way I can have dired automatically place point on these files when I open these directories?

This would offer a lot of convenience (as I could open them by just hitting RET), but would still give the flexibility of visiting other files.

16

Write a custom function for dired-initial-position-hook. In this function you can use various dired functions to search for files, and eventually call dired-goto-file to move point to the “best” file:

(defun my-dired-goto-important-file ()
  "Go to an important file in the current dired buffer."
  (unless (bound-and-true-p save-place)
    (let ((candidates '("master.tex" "AndroidManifest.xml"))
          candidate)
      (while (and candidates
                  (not (dired-goto-file (expand-file-name (pop candidates)))))
        nil))))

(add-hook 'dired-initial-position-hook #'my-dired-goto-important-file)

In the following I'll explain specific aspects of this function, and highlight potential extensions.

Jumping to files

dired-goto-file returns nil if the file does not exist in the current dired buffer, so it's safe to call without prior checking. However dired-goto-file requires an absolute file name (it errors on relative names), hence expand-file-name.

Searching for files

Dired does not provide a straight-forward API to search for files. Generally, you can iterate over all files just like you'd iterate over all lines in a buffer:

(let (files)
  (goto-char (point-min))
  (while (not (eobp))
    (let ((filename (dired-get-filename nil 'no-error)))
      (when filename
        (push filename files)))
    (forward-line 1))
  ;; Now `files' is a list of files, which you can search for patterns
  ;; …
  )

However, it's more convenient to use the marking functions. Just take care to use an “unusual” marker characters, to not loose existing interactive marks:

(unwind-protect
    (when (dired-mark-files-regexp (rx ".tex" string-end) ?t)
      (dired-goto-file (expand-file-name "master.tex")))
  (dired-unmark-all-files ?t))

Save Place Mode

You also need to be careful with regards to Save Place Mode, in case you enabled it. In Dired buffers, Save Place Mode does not remember the point value as in other modes, but rather the last file name point was on. It restores this file name just like we do: It hooks into dired-initial-position-hook and calls dired-goto-file (in fact, that's where I stole that code from :) ).

If our hook comes before save place, Save Place Mode will unconditionally overwrite our position. However, if it comes before our hook (as is the case when Save Place is enabled after you setup this hook) our function would actually overwrite Save Place which is not desirable. However, as in any buffer, Save Place sets the local variable save-place to a non-nil value, so we can simply check that and only act it Save Place did not restore a file name.

If you'd rather have Save Place “loose”, remove the check for save-place, and make sure that our hook is run after Save Place, by enabling Save Place Mode first, and then calling add-hook with a non-nil value for its APPEND argument.

  • Great answer. You may want to define bound-and-true-p. :-) – Malabarba Sep 24 '14 at 10:14
  • @Malabarba bound-and-true-p is a built-in macro from bindings.el. – lunaryorn Sep 24 '14 at 10:17
  • Dear god, I don't what makes me feel worse, the fact I didn't know that or the fact I didn't even check. – Malabarba Sep 24 '14 at 10:22
  • @Malabarba Never mind, it took me a year (literally) to discover the existence of this function. – lunaryorn Sep 24 '14 at 10:29
  • Yes, that's a wonderful function of which I had also never heard until now! – sanityinc Sep 24 '14 at 15:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.