all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Useful elisp I wrote, send-region-to-shell-mode
@ 2014-03-14  3:18 Ian Kelling
  2014-03-14 10:59 ` Alan Schmitt
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Ian Kelling @ 2014-03-14  3:18 UTC (permalink / raw)
  To: help-gnu-emacs

I just switched over to shell-mode and definitely recommend it over a normal
terminal in general. This elisp seems to fill in an obviously missing piece of
integration with emacs. It's currently too hackish to recommend for going into
emacs, but I figured I would share and see if anyone had feedback or found it
useful. I'll post this it on the emacs wiki as well.



(defun send-region-to-shell-mode ()
  "Send region as input to shell in a shell-mode buffer.
Temporarily hide shell prompts for multi-line input.
Create & show shell buffer in a new window if needed.

Main use = enhance workflow when editing shell a script.

Minimal setup needed:
1. Adjust the arguments within this function
depending on how you set and unset the prompt in your shell.
2. Make a keybind to this function."
  (interactive)
  (send-region-comint "*shell*"
                      "unset PROMPT_COMMAND; unset PS1"
                      "PROMPT_COMMAND=prompt_command"
                      'shell))




;; supporting functions

(defun send-region-comint (buffer-name &optional before after init)
  "Input the region to BUFFER-NAME, assuming it is a comint-derived buffer.
        Show BUFFER-NAME if it is not show.
        Call INIT if BUFFER-NAME does not exist.
        Invisibly execute BEFORE & AFTER by comint process."
  (interactive)
  (let ((input (buffer-substring-no-properties (mark) (point)))
        (buffer (get-buffer buffer-name)))
    (unless buffer
      ;; save-excursion etc. don't work for (shell), so I do this instead
      (if init (let ((original-buffer (current-buffer)))
                 (funcall init)
                 (switch-to-buffer original-buffer))
        (error "No existing buffer found and no init function argument. ")))
    (setq buffer (get-buffer buffer-name))
    (buffer-window-show buffer)
    (with-current-buffer buffer
      (let ((proc (get-buffer-process buffer)))
        (if before (send-invisible-string proc before))
        (goto-char (process-mark proc))
        (insert input)
        (comint-send-input)
        (if after (send-invisible-string proc after))))))

(defun send-invisible-string (proc string)
  "Like send-invisible, but non-interactive"
  (comint-snapshot-last-prompt)
  (funcall comint-input-sender proc string))

;; modified version of temp-buffer-window-show,
;; only removed a few initial lines which set buffer read only etc.
;; I tried to find an existing function, but no dice
(defun buffer-window-show (&optional buffer action)
  "Like temp-buffer-window-show, but without any modifications to the buffer,
      like read only etc."
  (let (window frame)
    (with-current-buffer buffer
      (when (let ((window-combination-limit
                   ;; When `window-combination-limit' equals
                   ;; `temp-buffer' or `temp-buffer-resize' and
                   ;; `temp-buffer-resize-mode' is enabled in this
                   ;; buffer bind it to t so resizing steals space
                   ;; preferably from the window that was split.
                   (if (or (eq window-combination-limit 'temp-buffer)
                           (and (eq window-combination-limit
                                    'temp-buffer-resize)
                                temp-buffer-resize-mode))
                       t
                     window-combination-limit)))
              (setq window (display-buffer buffer action)))
        (setq frame (window-frame window))
        (unless (eq frame (selected-frame))
          (raise-frame frame))
        (setq minibuffer-scroll-window window)
        (set-window-hscroll window 0)
        (with-selected-window window
          (run-hooks 'temp-buffer-window-show-hook)
          (when temp-buffer-resize-mode
            (resize-temp-buffer-window window)))
        ;; Return the window.
        window))))





- Ian Kelling



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Useful elisp I wrote, send-region-to-shell-mode
  2014-03-14  3:18 Useful elisp I wrote, send-region-to-shell-mode Ian Kelling
@ 2014-03-14 10:59 ` Alan Schmitt
  2014-03-15  7:54 ` Ian Kelling
  2014-03-15  7:58 ` Ian Kelling
  2 siblings, 0 replies; 4+ messages in thread
From: Alan Schmitt @ 2014-03-14 10:59 UTC (permalink / raw)
  To: Ian Kelling; +Cc: help-gnu-emacs

Hello Ian,

Ian Kelling <ianowl@gmail.com> writes:

> I just switched over to shell-mode and definitely recommend it over
> a normal terminal in general.

I'm curious about this. I tried several times to use a shell in emacs
instead of a terminal, and I have always gone back. The main two reasons
I could not keep using them are some incompatibilities with some shells
(I use fish, for instance, which can only run in ansi-term, and even
there it's not perfect), and window management (having a quick access
to a shell window). Do you have tips for using a shell in emacs? In
particular, what shell do you run?

> This elisp seems to fill in an obviously missing piece of integration
> with emacs. It's currently too hackish to recommend for going into
> emacs, but I figured I would share and see if anyone had feedback or
> found it useful. I'll post this it on the emacs wiki as well.

This is very nice, thanks for sharing. I often use org-mode when
developing scripts, but this could be a useful alternative.

Best,

Alan



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Useful elisp I wrote, send-region-to-shell-mode
  2014-03-14  3:18 Useful elisp I wrote, send-region-to-shell-mode Ian Kelling
  2014-03-14 10:59 ` Alan Schmitt
@ 2014-03-15  7:54 ` Ian Kelling
  2014-03-15  7:58 ` Ian Kelling
  2 siblings, 0 replies; 4+ messages in thread
From: Ian Kelling @ 2014-03-15  7:54 UTC (permalink / raw)
  To: help-gnu-emacs

[-- Attachment #1: Type: text/plain, Size: 7376 bytes --]

On 03/14/2014 03:59 AM, Alan Schmitt wrote:> Hello Ian,
>
> Ian Kelling <ianowl@gmail.com> writes:
>
>> I just switched over to shell-mode and definitely recommend it over
>> a normal terminal in general.
>
> I'm curious about this. I tried several times to use a shell in emacs
> instead of a terminal, and I have always gone back. The main two reasons
> I could not keep using them are some incompatibilities with some shells
> (I use fish, for instance, which can only run in ansi-term, and even
> there it's not perfect), and window management (having a quick access
> to a shell window).

You get emacs window management, it is what it is. I'm happy with it. I can put
shell-mode's in their own frames.

> This is very nice, thanks for sharing. I often use org-mode when
> developing scripts, but this could be a useful alternative.

I'm using org-babel in conjunction with this :) Related: I'm going to send a patch
to org-mode which sends std-err to babel results.


> Do you have tips for using a shell in emacs? In
> particular, what shell do you run?


I use bash, which luckily is very compatible. You lose the bash keybinds and
replace them with emacs ones, which are generally the same or better.

The only notable thing you lose is compatibility with interactive shell programs
that do anything more interactive than prompting for input. If you use those
enough, shell-mode is a no go. term/ansi-term mode is one avenue to try, but I
wouldn't bother.

Tips, yes. A bit of reading in ctrl-h m in shell-mode covers almost everything.

I use the bash-completion elisp package via melpa. It enables the smart command
argument completions of http://bash-completion.alioth.debian.org/, which come
standard in most gnu/linux distros.


;; don't store successive duplicates in comint command history
    (setq comint-input-ignoredups t)

I also bound 'comint-previous-input / 'comint-next-input to the up/down keys I'm
used to in a shell.




After sending off my first email, I realized some more functionality I wanted in
my function; Send the current line if region isn't active. Also, when in shell
mode, send lines BACK to the previous window. The new version:


;; example keybind
(global-set-key (kbd "<S-delete>") 'send-shell)

;; optional variables used by send-shell
(setq shell-unset-prompt "unset PROMPT_COMMAND; unset PS1")
(setq shell-set-prompt "PROMPT_COMMAND=prompt_command")
(setq shell-send-yank-key (kbd "C-v"))

(defun send-shell ()
  "Send current line or region (if active) to shell-mode buffer.
When in shell-mode, copy the current line,
paste it in the most recently visited visible window.

Optional variables:
`shell-unset-prompt/shell-set-prompt': shell commands invisibly
sent to temporarily hide shell prompts for multi-line input.
`send-shell-buffer-name': Use this instead of default *shell*

`shell-send-yank-key' key to use instead
of yank to paste into recent window. This allows compatibility with
modes like org-mode which have their own yank function.

Creates & shows shell-mode buffer in a new window if needed."
  (interactive)
  (if (string-equal mode-name "Shell")
      (progn
        (call-interactively 'copy-line)
        (select-window (previous-window nil nil 'visible))
        (if (and (boundp 'shell-send-yank-key) shell-send-yank-key)
            (call-interactively (global-key-binding shell-send-yank-key))
          (yank))
        (select-window (next-window nil nil 'visible)))
    (let (start end)
      (if mark-active
          (setq start (mark)
                end (point))
        (setq start (save-excursion (beginning-of-line) (point))
              end (save-excursion (end-of-line) (point)))
        (next-line))
      (send-comint-input
       (or (and (boundp 'send-shell-buffer-name) send-shell-buffer-name) "*shell*")
       start end 'shell))))


;; supporting functions

(defun copy-line (arg)
  "Copy lines (as many as prefix argument) in the kill ring.
Pretend the last line of the buffer has a trailing newline to avoid insanity."
  (interactive "p")
  (kill-ring-save (line-beginning-position) (line-end-position arg))
  (kill-append "\n" nil)
  (if (and arg (not (= 1 arg))) (message "%d lines copied" arg)))


(defun send-comint-input (buffer-name start end &optional init)
  "Input the region to BUFFER-NAME, assuming it is a comint-derived buffer.
                   Show BUFFER-NAME if it is not show.
                   Call INIT if BUFFER-NAME does not exist.
                   Invisibly execute BEFORE & AFTER by comint process."
  (interactive)
  (let ((input (filter-buffer-substring start end))
        (buffer (get-buffer buffer-name)))
    (unless buffer
      ;; save-excursion etc. don't work for (shell), so I do this instead
      (if init (let ((original-buffer (current-buffer)))
                 (funcall init (and (boundp 'send-shell-buffer-name)
send-shell-buffer-name))
                 (switch-to-buffer original-buffer))
        (error "No existing buffer found and no init function argument. ")))
    (setq buffer (get-buffer buffer-name))
    (buffer-window-show buffer)
    (with-current-buffer buffer
      (let ((proc (get-buffer-process buffer)))
        (if (boundp 'shell-unset-prompt)
            (send-invisible-string proc shell-unset-prompt))
        (goto-char (process-mark proc))
        (insert input)
        (comint-send-input)
        (if (boundp 'shell-set-prompt)
            (send-invisible-string proc shell-set-prompt))))))

(defun send-invisible-string (proc string)
  "Like send-invisible, but non-interactive"
  (comint-snapshot-last-prompt)
  (funcall comint-input-sender proc string))

;; modified version of temp-buffer-window-show,
;; only removed a few initial lines which set buffer read only etc.
;; I tried to find an existing function, but no dice
(defun buffer-window-show (&optional buffer action)
  "Like temp-buffer-window-show, but without any modifications to the buffer,
                 like read only etc."
  (let (window frame)
    (with-current-buffer buffer
      (when (let ((window-combination-limit
                   ;; When `window-combination-limit' equals
                   ;; `temp-buffer' or `temp-buffer-resize' and
                   ;; `temp-buffer-resize-mode' is enabled in this
                   ;; buffer bind it to t so resizing steals space
                   ;; preferably from the window that was split.
                   (if (or (eq window-combination-limit 'temp-buffer)
                           (and (eq window-combination-limit
                                    'temp-buffer-resize)
                                temp-buffer-resize-mode))
                       t
                     window-combination-limit)))
              (setq window (display-buffer buffer action)))
        (setq frame (window-frame window))
        (unless (eq frame (selected-frame))
          (raise-frame frame))
        (setq minibuffer-scroll-window window)
        (set-window-hscroll window 0)
        (with-selected-window window
          (run-hooks 'temp-buffer-window-show-hook)
          (when temp-buffer-resize-mode
            (resize-temp-buffer-window window)))
        ;; Return the window.
        window))))




[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 897 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Useful elisp I wrote, send-region-to-shell-mode
  2014-03-14  3:18 Useful elisp I wrote, send-region-to-shell-mode Ian Kelling
  2014-03-14 10:59 ` Alan Schmitt
  2014-03-15  7:54 ` Ian Kelling
@ 2014-03-15  7:58 ` Ian Kelling
  2 siblings, 0 replies; 4+ messages in thread
From: Ian Kelling @ 2014-03-15  7:58 UTC (permalink / raw)
  To: help-gnu-emacs

I've been sitting at the computer too long, hopefully this isn't a double post.


On 03/14/2014 03:59 AM, Alan Schmitt wrote:> Hello Ian,
>
> Ian Kelling <ianowl@gmail.com> writes:
>
>> I just switched over to shell-mode and definitely recommend it over
>> a normal terminal in general.
>
> I'm curious about this. I tried several times to use a shell in emacs
> instead of a terminal, and I have always gone back. The main two reasons
> I could not keep using them are some incompatibilities with some shells
> (I use fish, for instance, which can only run in ansi-term, and even
> there it's not perfect), and window management (having a quick access
> to a shell window).

You get emacs window management, it is what it is. I'm happy with it. I can put
shell-mode's in their own frames.

> This is very nice, thanks for sharing. I often use org-mode when
> developing scripts, but this could be a useful alternative.

I'm using org-babel in conjunction with this. Related: going to send a patch to
org-mode which sends std-err to babel results.


> Do you have tips for using a shell in emacs? In
> particular, what shell do you run?


I use bash, which luckily is very compatible. You lose the bash keybinds and
replace them with emacs ones, which are generally the same or better.

The only notable thing you lose is compatibility with interactive shell programs
that do anything more interactive than prompting for input. If you use those
enough, shell-mode is a no go. term/ansi-term mode is one avenue to try, but I
wouldn't bother.

Tips, yes. A bit of reading in ctrl-h m in shell-mode covers almost everything.

I use the bash-completion elisp package via melpa. It enables the smart command
argument completions of http://bash-completion.alioth.debian.org/, which come
standard in most gnu/linux distros.


;; don't store successive duplicates in comint command history
    (setq comint-input-ignoredups t)

I also bound 'comint-previous-input / 'comint-next-input to the up/down keys I'm
used to in a shell.




After sending off my first email, I realized some more functionality I wanted in
my function; Send the current line if region isn't active. Also, when in shell
mode, send lines BACK to the previous window. The new version:


;; example keybind
(global-set-key (kbd "<S-delete>") 'send-shell)

;; optional variables used by send-shell
(setq shell-unset-prompt "unset PROMPT_COMMAND; unset PS1")
(setq shell-set-prompt "PROMPT_COMMAND=prompt_command")
(setq shell-send-yank-key (kbd "C-v"))

(defun send-shell ()
  "Send current line or region (if active) to shell-mode buffer.
When in shell-mode, copy the current line,
paste it in the most recently visited visible window.

Optional variables:
`shell-unset-prompt/shell-set-prompt': shell commands invisibly
sent to temporarily hide shell prompts for multi-line input.
`send-shell-buffer-name': Use this instead of default *shell*

`shell-send-yank-key' key to use instead
of yank to paste into recent window. This allows compatibility with
modes like org-mode which have their own yank function.

Creates & shows shell-mode buffer in a new window if needed."
  (interactive)
  (if (string-equal mode-name "Shell")
      (progn
        (call-interactively 'copy-line)
        (select-window (previous-window nil nil 'visible))
        (if (and (boundp 'shell-send-yank-key) shell-send-yank-key)
            (call-interactively (global-key-binding shell-send-yank-key))
          (yank))
        (select-window (next-window nil nil 'visible)))
    (let (start end)
      (if mark-active
          (setq start (mark)
                end (point))
        (setq start (save-excursion (beginning-of-line) (point))
              end (save-excursion (end-of-line) (point)))
        (next-line))
      (send-comint-input
       (or (and (boundp 'send-shell-buffer-name) send-shell-buffer-name) "*shell*")
       start end 'shell))))


;; supporting functions

(defun copy-line (arg)
  "Copy lines (as many as prefix argument) in the kill ring.
Pretend the last line of the buffer has a trailing newline to avoid insanity."
  (interactive "p")
  (kill-ring-save (line-beginning-position) (line-end-position arg))
  (kill-append "\n" nil)
  (if (and arg (not (= 1 arg))) (message "%d lines copied" arg)))


(defun send-comint-input (buffer-name start end &optional init)
  "Input the region to BUFFER-NAME, assuming it is a comint-derived buffer.
                   Show BUFFER-NAME if it is not show.
                   Call INIT if BUFFER-NAME does not exist.
                   Invisibly execute BEFORE & AFTER by comint process."
  (interactive)
  (let ((input (filter-buffer-substring start end))
        (buffer (get-buffer buffer-name)))
    (unless buffer
      ;; save-excursion etc. don't work for (shell), so I do this instead
      (if init (let ((original-buffer (current-buffer)))
                 (funcall init (and (boundp 'send-shell-buffer-name)
send-shell-buffer-name))
                 (switch-to-buffer original-buffer))
        (error "No existing buffer found and no init function argument. ")))
    (setq buffer (get-buffer buffer-name))
    (buffer-window-show buffer)
    (with-current-buffer buffer
      (let ((proc (get-buffer-process buffer)))
        (if (boundp 'shell-unset-prompt)
            (send-invisible-string proc shell-unset-prompt))
        (goto-char (process-mark proc))
        (insert input)
        (comint-send-input)
        (if (boundp 'shell-set-prompt)
            (send-invisible-string proc shell-set-prompt))))))

(defun send-invisible-string (proc string)
  "Like send-invisible, but non-interactive"
  (comint-snapshot-last-prompt)
  (funcall comint-input-sender proc string))

;; modified version of temp-buffer-window-show,
;; only removed a few initial lines which set buffer read only etc.
;; I tried to find an existing function, but no dice
(defun buffer-window-show (&optional buffer action)
  "Like temp-buffer-window-show, but without any modifications to the buffer,
                 like read only etc."
  (let (window frame)
    (with-current-buffer buffer
      (when (let ((window-combination-limit
                   ;; When `window-combination-limit' equals
                   ;; `temp-buffer' or `temp-buffer-resize' and
                   ;; `temp-buffer-resize-mode' is enabled in this
                   ;; buffer bind it to t so resizing steals space
                   ;; preferably from the window that was split.
                   (if (or (eq window-combination-limit 'temp-buffer)
                           (and (eq window-combination-limit
                                    'temp-buffer-resize)
                                temp-buffer-resize-mode))
                       t
                     window-combination-limit)))
              (setq window (display-buffer buffer action)))
        (setq frame (window-frame window))
        (unless (eq frame (selected-frame))
          (raise-frame frame))
        (setq minibuffer-scroll-window window)
        (set-window-hscroll window 0)
        (with-selected-window window
          (run-hooks 'temp-buffer-window-show-hook)
          (when temp-buffer-resize-mode
            (resize-temp-buffer-window window)))
        ;; Return the window.
        window))))






^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2014-03-15  7:58 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-03-14  3:18 Useful elisp I wrote, send-region-to-shell-mode Ian Kelling
2014-03-14 10:59 ` Alan Schmitt
2014-03-15  7:54 ` Ian Kelling
2014-03-15  7:58 ` Ian Kelling

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.