Reputation: 42152
I wrote an elisp macro that preserves the region when in transient-mark-mode
:
(defmacro keep-region (command)
"Wrap command in code that saves and restores the region"
(letrec ((command-name (symbol-name command))
(advice-name (concat command-name "-keep-region")))
`(progn
(defadvice ,command (around ,(intern advice-name))
(let (deactivate-mark)
(save-excursion
ad-do-it)))
(ad-activate (quote ,command)))))
(keep-region replace-string)
(keep-region replace-regexp)
This preserves the region for commands that are advised using the keep-region
macro; very helpful when you want to make multiple replacements in a selected block.
The problem is that after running a command that has been advised using this macro, the region loses its transient nature; subsequent movement commands extend the region, rather than deselecting it.
How can I programmatically re-enable the transience of the marked region?
Upvotes: 3
Views: 467
Reputation: 5027
The problem is that after running a command that has been advised using this macro, the region loses its transient nature; subsequent movement commands extend the region, rather than deselecting it.
You should rather talk about "shift-selected nature": movement commands extending the region is what happens when the mark is activated in the "normal" way.
The shift-select status is stored inside the transient-mark-mode
variable, and is modified by someone (handle-shift-selection
?) who doesn't care about the value of deactivate-mark
. We can get around this by saving the value of transient-mark-mode
:
(defmacro keep-region (command)
"Wrap command in code that saves and restores the region"
(letrec ((command-name (symbol-name command))
(advice-name (concat command-name "-keep-region")))
`(progn
(defadvice ,command (around ,(intern advice-name))
(let ((deactivate-mark nil)
(transient-mark-mode transient-mark-mode))
(save-excursion
ad-do-it)))
(ad-activate (quote ,command)))))
transient-mark-mode is a variable defined in `buffer.c'.
...
Lisp programs may give this variable certain special values:
...
- A value of
(only . OLDVAL)
enables Transient Mark mode temporarily. After any subsequent point motion command that is not shift-translated, or any other action that would normally deactivate the mark (e.g. buffer modification), the value of
`transient-mark-mode' is set to OLDVAL.
Upvotes: 1
Reputation: 28541
Just remove your calls to exchange-point-and-mark
. The preservation is done by the deactive-mark
let-binding anyway.
Upvotes: 1
Reputation:
From C-h f transient-mark-mode
:
Transient Mark mode is a global minor mode. When enabled, the region is highlighted whenever the mark is active. The mark is "deactivated" by changing the buffer, and after certain other operations that set the mark but whose main purpose is something else--for example, incremental search, <, and >.
Hence, activate-mark
after exchange-point-and-mark
should restore the transient nature of the mark.
I am not sure, though, why you are using exchange-point-and-mark
here, and why you are calling it twice. In my opinion, just saving (point)
and (mark)
in a let
-binding and restoring them after ad-do-it
would be easier. push-mark
and pop-mark
might help as well, since the latter automatically reactivates the mark anyway.
Upvotes: 2