index rss mastodon twitter github linkedin email
Álvaro Ramírez
sponsor

Álvaro Ramírez

24 July 2025 Interactive ordering of dired items

Redditor sauntcartas offers a nice solution for getting Emacs dired filenames in an arbitrary order. I have to say, while relatively rare, this is something I need from time to time. You see, I like to apply batch file operations from the comfort of dired buffers (via dwim-shell-command).

Take, for example, M-x dwim-shell-commands-join-images-horizontally. It does what it says on the tin. I can mark a handful of image files via dired and easily join them into a single file. The problem is that the file listing isn't always in the desired order, so this is where custom ordering comes in handy.

I wanted an interactive way of reordering dired items. While bouncing ideas with arthurno1, it led us to a drag-stuff experience, which I'm already a fan of. While drag-stuff works in most editable buffers, it breaks on dired. For now, I figured I could just write a couple of dired-specific interactive commands to provide a similar dragging experience, and so I did.

We can likely improve the commands a bit, but hey they do the job as is…

(defun ar/dired-drag-item-up ()
  "Drag dired item down in buffer."
  (interactive)
  (unless (dired-get-filename nil t)
    (error "Not a dired draggable item"))
  (when (= (line-number-at-pos) 2)
    (error "Already at top"))
  (let* ((inhibit-read-only t)
         (col (current-column))
         (item-start (line-beginning-position))
         (item-end (1+ (line-end-position)))
         (item (buffer-substring item-start item-end)))
    (delete-region item-start item-end)
    (forward-line -1)
    (beginning-of-line)
    (insert item)
    (forward-line -1)
    (move-to-column col)))

(defun ar/dired-drag-item-down ()
  "Drag dired item down in buffer."
  (interactive)
  (unless (dired-get-filename nil t)
    (error "Not a dired draggable item"))
  (when (save-excursion
          (forward-line 1)
          (eobp))
    (error "Already at bottom"))
  (let* ((inhibit-read-only t)
         (col (current-column))
         (item-start (line-beginning-position))
         (item-end (1+ (line-end-position)))
         (item (buffer-substring item-start item-end)))
    (delete-region item-start item-end)
    (forward-line 1)
    (beginning-of-line)
    (insert item)
    (forward-line -1)
    (move-to-column col)))

I gotta say, these dired dragging commands work great with M-x dwim-shell-commands-join-images-horizontally. I bind them to M-<up> and M-<down> same as drag-stuff elsewhere (already in my config).

(use-package dired
  :bind (:map dired-mode-map
              ("M-<up>" . ar/dired-drag-item-up)
              ("M-<down>" . ar/dired-drag-item-down)))

You can see the new dired commands in action.

dired-reordering.gif

Bonus

While bouncing ideas with arthurno1, we also came up with another helper to create new dired buffers populated from marked items, maybe needed for those times you want a more focused experience.

(defun ar/dired-from-marked-items ()
  "Create a new dired buffer containing only the marked files.

Also allow dragging items up and down via M-<up> and M-x<down>."
  (interactive)
  (let ((marked-files (dired-get-marked-files))
        (buffer-name (generate-new-buffer-name
                      (format "*%s (selection)*"
                              (file-name-nondirectory
                               (directory-file-name default-directory))))))
    (unless marked-files
      (error "No dired marked files"))
    (dired (cons buffer-name
                 (mapcar (lambda (path)
                           (file-relative-name path default-directory))
                         marked-files)))))

new-from-marked.gif

Make it all sustainable

Learned something new? Enjoying this blog or my projects? Help make it sustainable by ✨sponsoring

Need a blog? I can help with that. Maybe buy my iOS apps too ;)