rejeep / drag-stuff.el

Drag stuff around in Emacs
233 stars 12 forks source link

Unintended line drag #4

Closed kaushalmodi closed 10 years ago

kaushalmodi commented 10 years ago

Hi,

Thanks for coding this package. It's very helpful for moving lines or blocks of code.

I face only one issue when I am moving block of lines using region select.

Let's say the code has 3 lines. If I go to line 1, C-a, C-space, C-n, C-n, that will select lines 1 and 2. When I do drag-stuff-down, I expect to drag only lines 1 and 2. But line 3 moves as well.

Can you please change the behavior so that only the selected region moves? Right now it looks like line 3 is being moved because after selecting lines 1 and 2, the cursor position is on line 3.

rejeep commented 10 years ago

Hi, this behavior is intended. If you instead do C-a, C-space, C-n and then drag, only the lines 1 and 2 should be dragged, right?

I like this behavior because it allows you to move a bunch of lines without being required to put the mark at the beginning/end and point at the beginning/end.

With this behavior you can move lines like this (# shows the point and mark).

Lin#e 1
Line 2
L#ine 3
Line 4

If you now drag the block down, you will drag line 1, 2 and 3. If drag-stuff did not have this behavior, you would have to do this to move the first three lines:

#Line 1
Line 2
Line 3#
Line 4

Which if you ask me is less efficient.

kaushalmodi commented 10 years ago

Hi Johan,

Thanks for the explanation.

I was wondering if a corner case can be added to the code so that if the begin and end are as below (end point being the first char of a line)

Lin#e 1 Line 2

Line 3

Line 4

then Line 3 is not moved.

I understand the reason of the current behavior but my small request to implement the above special case that can be enabled by customizing a var.

Thanks anyways; this is an awesome package! :)

Kaushal Modi

On Mon, Jan 20, 2014 at 2:53 PM, Johan Andersson notifications@github.comwrote:

Hi, this behavior is intended. If you instead do C-a, C-space, C-n and then drag, only the lines 1 and 2 should be dragged, right?

I like this behavior because it allows you to move a bunch of lines without being required to put the mark at the beginning/end and point at the beginning/end.

With this behavior you can move lines like this (# shows the point and mark).

Lin#e 1 Line 2 L#ine 3 Line 4

If you now drag the block down, you will drag line 1, 2 and 3. If drag-stuff did not have this behavior, you would have to do this to move the first three lines:

Line 1

Line 2 Line 3# Line 4

Which if you ask me is less efficient.

— Reply to this email directly or view it on GitHubhttps://github.com/rejeep/drag-stuff.el/issues/4#issuecomment-32791387 .

rejeep commented 10 years ago

I added a branch that adds two hooks. One that runs before the dragging occurs and one that runs after. You should be able to solve your issue with help of those. I even wrote a test for how you could do it. See https://github.com/rejeep/drag-stuff.el/commit/86a6ef32b76a09a05d93bf25b2c35fc06fef5d29

Would that work out for you?

kaushalmodi commented 10 years ago

You're awesome. The branched version worked great! Thanks for providing the example too. Will you merge it into the main branch and update on Melpa too?

rejeep commented 10 years ago

No worries! Merged to master, will be available on Melpa as soon as they rebuild!

kaushalmodi commented 10 years ago

Hi, I'd like to add that the hooks work as expected only when a region is active. With your example, if I am in col 0 of line x with no region selected and if I drag, then line (x-1) is dragged :)

So I made the following little modification to the `drag-stuff-before-drag-hook in your example. Now I believe everything is working as I expected. I believe that using the variable mark-active should do the trick. Let me know if there is a better way.

(defvar drag-stuff-hax nil)

(add-hook 'drag-stuff-before-drag-hook
          (lambda ()
            ;; added and condition so that the hook activates only when a region is selected
            (when (and mark-active (zerop (current-column))) 
              (backward-char 1)
              (setq drag-stuff-hax t))))

(add-hook 'drag-stuff-after-drag-hook
          (lambda ()
            (when drag-stuff-hax
              (forward-char 1)
              (setq drag-stuff-hax nil))))
rejeep commented 10 years ago

I would do it like you have, unless that I would use the function (region-active-p) instead of mark-active.

kaushalmodi commented 10 years ago

Thanks.

kaushalmodi commented 9 years ago

For anyone ending up on this issue, I have updated the above hack as below to support the cases where the point could be above the mark (when user sets mark and moves the cursor upwards):

;; https://github.com/kaushalmodi/.emacs.d/blob/master/setup-files/setup-drag-stuff.el
;; http://emacs.stackexchange.com/a/13942/115
(defvar modi/drag-stuff--point-adjusted nil)
(defvar modi/drag-stuff--point-mark-exchanged nil)

(defun modi/drag-stuff--adj-pt-pre-drag ()
  "If a region is selected AND the `point' is in the first column, move
back the point by one char so that it ends up on the previous line. If the
point is above the mark, exchange the point and mark temporarily."
  (when (region-active-p)
    (when (< (point) (mark)) ; selection is done starting from bottom to up
      (exchange-point-and-mark)
      (setq modi/drag-stuff--point-mark-exchanged t))
    (if (zerop (current-column))
        (progn
          (backward-char 1)
          (setq modi/drag-stuff--point-adjusted t))
      ;; If point did not end up being on the first column after the
      ;; point/mark exchange, revert that exchange.
      (when modi/drag-stuff--point-mark-exchanged
        (exchange-point-and-mark) ; restore the original point and mark loc
        (setq modi/drag-stuff--point-mark-exchanged nil)))))

(defun modi/drag-stuff--rst-pt-post-drag ()
  "Restore the `point' to where it was by forwarding it by one char after
the vertical drag is done."
  (when modi/drag-stuff--point-adjusted
    (forward-char 1)
    (setq modi/drag-stuff--point-adjusted nil))
  (when modi/drag-stuff--point-mark-exchanged
    (exchange-point-and-mark) ; restore the original point and mark loc
    (setq modi/drag-stuff--point-mark-exchanged nil)))

(add-hook 'drag-stuff-before-drag-hook #'modi/drag-stuff--adj-pt-pre-drag)
(add-hook 'drag-stuff-after-drag-hook  #'modi/drag-stuff--rst-pt-post-drag)