2

One of my workflows is drag lines from Firefox to Emacs (X). What I'm trying to accomplish is putting a new line after dragging every line. I've tried zillions of combination but I can't find the correct syntax. Here is one of my more promising tries:

(setq inhibit-modification-hooks nil)

(defun put-nl ()
   (interactive)
   (insert "\n"))

(add-hook 'after-change-functions #'put-nl nil t)

This one almost works:

(add-hook 'post-self-insert-hook #'put-nl nil t)

But only responds when I press a key. Not when I paste or drag.

3
  • 1
    How do you paste? E.g if you are doing it with middle-click, then find which function is bound to it (probably mouse-yank-primary) and how it is getting the selection (for mouse-yank-primary, that is done with gui-get-primary-selection), then advise that function to add a newline to the end of the selection (if one is not there already perhaps).
    – NickD
    Jul 13, 2023 at 19:28
  • In this situation with "pasting" I mean select a line in Firefox and then drag and drop from there to Emacs. Jul 14, 2023 at 9:57
  • Don't know if the d-n-d event can be catched. And relating to "\n", what I'd like is a new line (I shouldn't have said carriage return). I try to avoid pressing "Enter" manually every time I add a line. Jul 14, 2023 at 10:03

1 Answer 1

2

The Elisp manual for drag and drop suggests that it's handled by dnd.el and x-dnd.el. Looking at the source, dnd.el contains the generic parts of handling drag-and-drop, and x-dnd.el is the code specific to X11.

For this task, hooking into dnd.el should be enough. The docstring for dnd-protocol-alist says:

The functions to call for different protocols when a drop is made. This variable is used by dnd-handle-one-url and dnd-handle-file-name. The list contains of (REGEXP . FUNCTION) pairs.

The functions shall take two arguments, URL, which is the URL dropped and ACTION which is the action to be performed for the drop (move, copy, link, private or ask).

If no match is found here, and the value of browse-url-browser-function is a pair of (REGEXP . FUNCTION), those regexps are tried for a match. If no match is found, the URL is inserted as text by calling dnd-insert-text.

The function shall return the action done (move, copy, link or private) if some action was made, or nil if the URL is ignored.

So when the thing that's been dropped isn't a URL, it's handled by dnd-insert-text. The source of this function is:

(defun dnd-insert-text (window action text)
  "Insert text at point or push to the kill ring if buffer is read only.
TEXT is the text as a string, WINDOW is the window where the drop happened."
  (if (or buffer-read-only
          (not (windowp window)))
      (progn
        (kill-new text)
        (message "%s"
         (substitute-command-keys
          "The dropped text can be accessed with \\[yank]")))
    (insert text))
  action)

We can advise it to add a newline after the text that has been dropped. We should probably match its conditions for inserting text too.

(defun my-dnd-insert-text-advice (window _action _text)
  (unless (or buffer-read-only (not (windowp window)))
    (newline)))
(advice-add #'dnd-insert-text :after #'my-dnd-insert-text-advice)
1
  • It works great! I can drag lines and also can drag url's from the address bar. In both cases adds a new line. Thank you very much!! Jul 14, 2023 at 11:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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