2

Adapting to a rather functional programming style, I often end up with files consisting of a lot of individual function definitions. Oftentimes, I then decide at some later point that I want to reorder those in some way as to facilitate understanding of the code, e.g. most general functions on top/ less general functions after, ...

At the moment I am mostly using manual killing and yanking to do this but find this rather cumbersome and time-consuming.

Is there a good (and maybe canonical) way in Emacs to do that? Ideally, I could call some command that presents me with a new buffer of all function names (one per line) and each change in that buffer would automatically update the order of the functions in my code-buffer (and vice-versa).

If only mode-specific solutions exist (which is probably all I can hope for since source code parsing is required) I would be interested in the following languages:

- Haskell
- Elisp
- Python  
  • 1
    I'm not sure that this specific feature is implemented anywhere (though I'd look into things like imenu and its clones), but the way to approach it would be to use mark-defun which should use mode-specific implementation to select a definition. Then have some occur-like buffer where you can bind keys to moving reduced representation of definitions around. – wvxvw Feb 16 '18 at 13:25
  • Thanks, I was not aware of imenu so far! It seems pretty useful. Maybe one could try to add my suggestion as a feature to one of the clones if it does not exist yet. – Simon Fromme Feb 16 '18 at 14:47
  • What @wvxvw said. Look into keyboard macros also. – Drew Feb 16 '18 at 17:05
1

One package, which might help here, is hide-show. You can call hs-hide-all, which will show you only the function definition lines plus a language-dependent abbreviation of the hidden part. You get an overview this way and can copy and paste the functions as needed with the standard commands. When you are done, you can invoke hs-show-all to return to the standard display.

I haven't used this myself for any of the languages you mentioned, but my understanding is, that hide-show builds on top of the mode-dependent functionality to recognize functions, so it should work with those languages too.

  • What a great idea! Just define a couple of functions to drag defintiions around and bind them to simple keys and you're off to the races! (I wrote an answer with suggested fuctions and keys.) – Omar Sep 28 '18 at 2:52
0

As far as this forms are separated by an empty line --but don't contain empty lines--, M-x transpose-paragraphs RET should be helpful.

Below a more specific solution WRT Python and beyond:

(defvar-local  generic-transpose-beginning nil
  "Section above cursor: the function reaching the upper bound.")

(defvar-local generic-transpose-end nil
  "Below cursor: function should reach the lower bound.")

(defun generic-transpose-set-variables ()
  (interactive)
  (cond ((functionp 'py-beginning-of-top-level)
     (setq generic-transpose-beginning 'py-beginning-of-top-level)
     (setq generic-transpose-end 'py-end-of-top-level))
    ;; shipped python.el
    ((functionp 'python-nav-beginning-of-defun)
     (setq generic-transpose-beginning 'python-nav-beginning-of-defun)
     (setq generic-transpose-end 'python-nav-end-of-defun))
    ((eq major-mode 'emacs-lisp-mode)
     (setq generic-transpose-beginning 'beginning-of-defun)
     (setq generic-transpose-end 'end-of-defun)))
  (message "Forward-funktions %s" generic-transpose-beginning))

(defun generic-transpose ()
  "Transposes sections from above and below position in buffer.

See setting of ‘generic-transpose-beginning’ and ‘generic-transpose-end’"
  (interactive "*")
  (let* ((orig (copy-marker (point)))
     (ENDR1 (progn (skip-chars-backward " \t\r\n\f") (point)))
     (STARTR1 (progn (funcall generic-transpose-beginning)(point)))
     (STARTR2 (progn (goto-char orig) (skip-chars-forward " \t\r\n\f") (point)))
     (ENDR2 (progn (funcall generic-transpose-end)(point))))
    (transpose-regions STARTR1 ENDR1 STARTR2 ENDR2)
    (goto-char orig)))

(add-hook 'python-mode-hook 'generic-transpose-set-variables)

Watch further implemention at: https://github.com/andreas-roehler/generic-transpose

  • Yeah, this might help for Lisp or Haskell but not for Python. – Simon Fromme Feb 17 '18 at 17:56
  • @SimonFromme Okay, updated. – Andreas Röhler Mar 1 '18 at 13:21
0

I think @Martin's idea to use hs-minor-mode was fantastic! You just need to define command to transpose function definitions and bind them to easily accesible keys. Here's one suggestion:

(defun drag-defun-up (arg)
  (interactive "p")
  (transpose-subr #'end-of-defun (- arg))
  (hs-hide-all))

(defun drag-defun-down (arg)
  (interactive "p")
  (transpose-subr #'end-of-defun arg)
  (hs-hide-all))

(define-key hs-minor-mode-map (kbd "<M-down>") #'drag-defun-down)

(define-key hs-minor-mode-map (kbd "<M-up>") #'drag-defun-up)

Also, note how great transpose-subr is: you just provide it with a move-forward-over-thingy command and it implements the corresponding transpose-thingy command, including the little-known but very useful case where you put the mark on one thingy, the point in another, possibly far away, thingy and call tranpose-thingy with a prefix argument of 0 to swap them!

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.