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?

  • 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 Oct 7 '17 at 9:42
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.

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)
                     )))

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.