2

I'd like to use shell-command-on-region on the region, in a way that keeps the selection, even in the case the length of the output changes, and works in regular emacs and in evil mode?


Note, I've already asked this related question which is similar, just not dealing with selection: How to use an external command to modify selected words and lines?

4
  • 2
    Does reactivating the mark do what you expect? E.g. hit C-x C-x
    – YoungFrog
    Commented Aug 15, 2017 at 9:26
  • @YoungFrog, no it failed for a few reasons, added answer that lists them.
    – ideasman42
    Commented Aug 15, 2017 at 12:14
  • @ideasman42 Don't know what's your issue, but it works nicely from here with vanilla Emacs, though I don't know about evil mode.
    – xuchunyang
    Commented Aug 15, 2017 at 13:31
  • @xuchunyang - even with vanilla emacs, iirc adding more text has some issues. Though maybe under some conditions it works OK. Evil mode definitely needs special handling.
    – ideasman42
    Commented Aug 15, 2017 at 14:27

1 Answer 1

1

This can be done, I managed to get it working (with help from @wasamasa), notes:

  • The selection needs to be stored and restored.
  • It's important to run deactivate-mark is nil so Emacs can restore the selection.
  • Special handling is needed for evil-visual-line mode,
    (evil-visual-select ...) needs to be called.

Below is a wrapper for shell-command-on-region that handles re-selection.

;; Wrapper for 'shell-command-on-region', keeps the selection.
(defun my-shell-command-on-region-and-select
    (start
     end
     command
     &optional
     output-buffer
     replace
     error-buffer
     display-error-buffer
     region-noncontiguous-p)
  "Wrapper for 'shell-command-on-region', re-selecting the output.

Useful when called with a selection, so it can be modified in-place."
  (interactive)
  (let ((buffer-size-init (buffer-size)))
    (shell-command-on-region
     start
     end
     command
     output-buffer
     replace
     error-buffer
     display-error-buffer
     region-noncontiguous-p)
    (setq deactivate-mark nil)
    (setq end (+ end (- (buffer-size) buffer-size-init)))
    (set-mark start)
    (goto-char end)
    (activate-mark)
    ;; needed for evil line mode
    (when (and (boundp evil-state) (string= evil-state "visual"))
      (when (eq (evil-visual-type) evil-visual-line)
        (evil-visual-select start end 'line)))))

Example call:

(my-shell-command-on-region-keep-region-and-select
 (region-beginning) (region-end)
 "tr A-Za-z a-zA-Z && echo ' extra text'")
1
  • 2
    Note that (let (deactivate-mark) ...) is another idiom of that kind that protects the let-bound variable so that changes to it are limited to the body.
    – wasamasa
    Commented Aug 15, 2017 at 18:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.