From: Ian Kelling <ianowl@gmail.com>
To: help-gnu-emacs@gnu.org
Subject: Re: Useful elisp I wrote, send-region-to-shell-mode
Date: Sat, 15 Mar 2014 00:58:30 -0700 [thread overview]
Message-ID: <53240826.7030501@gmail.com> (raw)
In-Reply-To: <5322751C.3020108@gmail.com>
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))))
prev parent reply other threads:[~2014-03-15 7:58 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=53240826.7030501@gmail.com \
--to=ianowl@gmail.com \
--cc=help-gnu-emacs@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.