Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preview images inline #162

Open
mickesv opened this issue Nov 5, 2019 · 6 comments
Open

Preview images inline #162

mickesv opened this issue Nov 5, 2019 · 6 comments

Comments

@mickesv
Copy link

mickesv commented Nov 5, 2019

Hi,
I like the way dired-subtree inserts subtrees, but I didn't like that I could only do this with directories. So I played around this morning and ended up with the following code. Since it is heavily copy-pasted from your dired-subtree code I'm hesitant to do a pull request for it. Instead, here it is, use it or throw it away as you please:


(defun dired-preview--dired-line-is-previewable ()
  "Return non-nil if line under point is previewable"
  (let* ((fname (dired-get-filename nil))
         (ext (upcase (file-name-extension fname)))
         (allowed-extensions '("PBM" "XBM" "XPM" "GIF" "JPEG" "JPG" "TIFF" "TIF" "PNG" "SVG"))
         (search-fun (apply-partially (lambda (a b) (string= a b)) ext))
         (is-ext-allowed (seq-find search-fun allowed-extensions nil)))
    is-ext-allowed))

(defun dired-preview--readin (filename)
  "Read in the file.

Return a string suitable for insertion in `dired' buffer."
    (let ((preview-image (create-image filename 'imagemagick nil :height 200)))
      (with-temp-buffer
        (insert-image preview-image)
        (insert "\n")
        (buffer-string))))

(defun dired-preview-insert ()          ;; Copied more or less directly from dired-subtree
  "Insert preview under this file."
  (interactive)
  (when (and (dired-preview--dired-line-is-previewable)
             (not (dired-subtree--is-expanded-p)))
    (let* ((filename (dired-get-filename nil))
           (listing (dired-preview--readin filename))
           beg end)
      (read-only-mode -1)
      (move-end-of-line 1)
      ;; this is pretty ugly, I'm sure it can be done better
      (save-excursion
        (insert listing)
        (setq end (+ (point) 2)))
      (newline)
      (setq beg (point))
      (let ((inhibit-read-only t))
        (remove-text-properties (1- beg) beg '(dired-filename)))
      (let* ((ov (make-overlay beg end))
             (parent (dired-subtree--get-ov (1- beg)))
             (depth (or (and parent (+ 2 (overlay-get parent 'dired-subtree-depth)))
                        2))
             (face (intern (format "dired-subtree-depth-%d-face" depth))))
        (when dired-subtree-use-backgrounds
          (overlay-put ov 'face face))
        ;; refactor this to some function
        (overlay-put ov 'line-prefix
                     (if (stringp dired-subtree-line-prefix)
                         (if (not dired-subtree-use-backgrounds)
                             (apply 'concat (-repeat depth dired-subtree-line-prefix))
                           (cond
                            ((eq nil dired-subtree-line-prefix-face)
                             (apply 'concat
                                    (-repeat depth dired-subtree-line-prefix)))
                            ((eq 'subtree dired-subtree-line-prefix-face)
                             (concat
                              dired-subtree-line-prefix
                              (propertize
                               (apply 'concat
                                      (-repeat (1- depth) dired-subtree-line-prefix))
                               'face face)))
                            ((eq 'parents dired-subtree-line-prefix-face)
                             (concat
                              dired-subtree-line-prefix
                              (apply 'concat
                                     (--map
                                      (propertize dired-subtree-line-prefix
                                                  'face
                                                  (intern (format "dired-subtree-depth-%d-face" it)))
                                      (number-sequence 1 (1- depth))))))))
                       (funcall dired-subtree-line-prefix depth)))
        (overlay-put ov 'dired-subtree-name filename)
        (overlay-put ov 'dired-subtree-parent parent)
        (overlay-put ov 'dired-subtree-depth depth)
        (overlay-put ov 'evaporate t)
        (push ov dired-subtree-overlays))
      (goto-char (- beg 1))
      (dired-move-to-filename)
      (read-only-mode 1)
      (run-hooks 'dired-subtree-after-insert-hook))))

(defun dired-preview-insert-preview-or-subtree (orig-fun)
  "Call the right insert function for a preview or a subtree"
  (interactive)
  (cond ((dired-subtree--dired-line-is-directory-or-link-p) (apply orig-fun nil))
        ((dired-preview--dired-line-is-previewable) (dired-preview-insert))))

(advice-add 'dired-subtree-insert :around #'dired-preview-insert-preview-or-subtree)
  

Mandatory screenshot:
Screen Shot 2019-11-05 at 11 12 56

@Fuco1
Copy link
Owner

Fuco1 commented Nov 5, 2019

This is pretty sick!

I think it could very well form a basis of another package, dired-inline-preview or something similar. We could also extend it to support other files, such as:

  • text file: show a couple first lines
  • pdf: show the title page
  • archive file: show listing

@mickesv
Copy link
Author

mickesv commented Nov 6, 2019

I had similar ideas, but thought I'd start with something "easy" to see if it at all worked. I am amazed by how little I had to do to your original code to repurpose it like this. Kudos!

Having slept upon the matter, I think that modifying the original dired-subtree-insert to be more generic is probably a better path forward than the extensive copypasta and defadvice-approach I submitted yesterday. Going overboard a bit, maybe a subtree is just another type of inline preview?

@Fuco1
Copy link
Owner

Fuco1 commented Nov 6, 2019

Hm, I like that idea to unifying the subtree as a preview, that makes a lot of sense in this extended context.

I haven't compared your code extensively to the "original", but maybe it would be possible to add a couple callbacks here and there which would decide based on the item type at hand what kind of preview to insert. It can then dispatch to bunch of functions.

The API I invision is similar to the one of many Emacs special hooks: you have a list of functions taking the item as an argument, and you run them one by one until one returns non-nil, which means it inserted something and you should stop trying. This way it is easily extensible by adding more stuff to this list, either by us or by the end user themselves.

@mickesv
Copy link
Author

mickesv commented Nov 6, 2019

in the dired-preview-insert function there are only three changes:

(when (and (dired-preview--dired-line-is-previewable)
             (not (dired-subtree--is-expanded-p)))
    (let* ((filename (dired-get-filename nil))
           (listing (dired-preview--readin filename))

This was the main reason for copy-pasting the function, these checks differ from the ones in dired-subtree. With an API as suggested by you, this becomes moot.

(depth (or (and parent (+ 2 (overlay-get parent 'dired-subtree-depth)))
                        2))

For some reason, I did not manage to indent the image as I think you do with the inserted subtree (by running through each line and prepending two spaces). So I cheated and indented one more step with the help of the overlay.

(goto-char (- beg 1))

I needed to "step outside" of the image for dired to find the beginning of the right line.

... and then the defadvice to keep me from having to directly modify dired-subtree-insert.

My guess is that with a more holistic approach to all types of previews, none of these changes are significant or specific to image-inlines.

@Fuco1
Copy link
Owner

Fuco1 commented Nov 6, 2019

Would you like to continue working on this? To be upfront I'm afraid I won't be available any time soon, it's quite packed in my schedule (I'm juggling three projects at work at the same time :/ unfortunate timing). I can help and answer questions / provide reviews of course.

@mickesv
Copy link
Author

mickesv commented Nov 7, 2019

It would be my pleasure and an honour to take a swing at it.

I can't promise any quick deliveries from my side either, however.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Backlog
Development

No branches or pull requests

2 participants