3

In most modern editors, after canceling a selection by a left/right motion, the cursor will be moved to the beginning/end of the previously-selected region correspondingly. Similarly, if we cancel a selection by a up/down motion, the cursor will be moved up/down one line from the beginning/end of the previously-selected region correspondingly.

Could we have the same behaviors in Emacs under shift-select-mode?

I tried to define my own movement function:

(bind-key* "M-l"
  (lambda (arg)
    (interactive "^p")
    (if (use-region-p)
        (goto-char (region-end))
      (syntax-subword-forward arg))))

But it doesn't work. The goto-char never run even if I am selecting a region. Is it because I used (interactive "^p")? Any solution?

AhLeung
  • 1,083
  • 5
  • 14
  • I very much prefer Emacs's behavior: I especially don't like it when a selection extends past the edge of the screen and a motion command brings me to the invislble end. But of course each to their own preference. In case anybody wants this and is not aware: **you can switch to the other end of the selection with `C-x C-x`**. Thus, in shift selection mode, an unshifted motion command exits the selection at the end that you've been focusing on (moving the cursor), and you press `C-x C-x` first to exit the selection at the other end. – Gilles 'SO- stop being evil' Oct 07 '17 at 09:42

2 Answers2

2

You can probably do something like

(defvar my-old-region-bounds nil)

(advice-add 'handle-shift-selection :around
  (lambda (orig-fun)
    (let ((was-active (region-active-p)))
      (funcall orig-fun)
      (when (and was-active (not (region-active-p)))
        (setq-local my-old-region-bounds
                    (cons (point-marker) (mark-marker)))
        (add-hook 'post-command-hook #'my-move-to-old-region-bound)))))

(defun my-move-to-old-region-bound ()
  (remove-hook 'post-command-hook #'my-move-to-old-region-bound)
  (let ((bounds my-old-region-bounds))
    (kill-local-variable 'my-old-region-bounds)
    (when bounds
      (when (funcall (cond
                      ((> (point) (car bounds)) #'<)
                      ((= (point) (car bounds)) #'=)
                      (t #'>))
                     (point) (cdr bounds))
          ;; We moved towards the other (old)boundary.
          (goto-char (cdr bounds))))))

Of course, this is guaranteed 100% untested.

Edited by AhLeung: I tested it, and it works for most motion functions.

Drew
  • 75,699
  • 9
  • 109
  • 225
Stefan
  • 26,154
  • 3
  • 46
  • 84
1

I found a solution, feel free to comment / improve.

(advice-add 'handle-shift-selection :before
            (lambda ()
              (setq-local was-active (region-active-p))))
(bind-key* "M-l" (lambda (arg)
                   (interactive "^p")
                   (if (and was-active (not (region-active-p)))
                       (goto-char (region-end))
                     (syntax-subword-forward arg)
                     )))
(bind-key* "M-h" (lambda (arg)
                   (interactive "^p")
                   (if (and was-active (not (region-active-p)))
                       (goto-char (region-beginning))
                     (syntax-subword-backward arg)
                     )))
AhLeung
  • 1,083
  • 5
  • 14