em-term.el 13.5 KB
Newer Older
1
;;; em-term.el --- running visual commands  -*- lexical-binding:t -*-
Gerd Moellmann's avatar
Gerd Moellmann committed
2

Paul Eggert's avatar
Paul Eggert committed
3
;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
Gerd Moellmann's avatar
Gerd Moellmann committed
4

Gerd Moellmann's avatar
Gerd Moellmann committed
5 6
;; Author: John Wiegley <johnw@gnu.org>

Gerd Moellmann's avatar
Gerd Moellmann committed
7 8
;; This file is part of GNU Emacs.

9
;; GNU Emacs is free software: you can redistribute it and/or modify
Gerd Moellmann's avatar
Gerd Moellmann committed
10
;; it under the terms of the GNU General Public License as published by
11 12
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Gerd Moellmann's avatar
Gerd Moellmann committed
13 14 15 16 17 18 19

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
20
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
Gerd Moellmann's avatar
Gerd Moellmann committed
21 22 23 24 25 26

;;; Commentary:

;; At the moment, eshell is stream-based in its interactive input and
;; output.  This means that full-screen commands, such as "vi" or
;; "lynx", will not display correctly.  These are therefore thought of
Paul Eggert's avatar
Paul Eggert committed
27
;; as "visual" programs.  In order to run these programs under Emacs,
Gerd Moellmann's avatar
Gerd Moellmann committed
28 29 30 31
;; Eshell uses the term.el package, and invokes them in a separate
;; buffer, giving the illusion that Eshell itself is allowing these
;; visual processes to execute.

32 33
;;; Code:

34
(require 'cl-lib)
35 36
(require 'esh-util)
(require 'esh-ext)
37
(eval-when-compile (require 'eshell))
Gerd Moellmann's avatar
Gerd Moellmann committed
38 39
(require 'term)

40
;;;###autoload
41 42
(progn
(defgroup eshell-term nil
43
  "This module causes visual commands (e.g., `vi') to be executed by
44 45 46 47 48
the `term' package, which comes with Emacs.  This package handles most
of the ANSI control codes, allowing curses-based applications to run
within an Emacs window.  The variable `eshell-visual-commands' defines
which commands are considered visual in nature."
  :tag "Running visual commands"
49
  :group 'eshell-module))
50

Gerd Moellmann's avatar
Gerd Moellmann committed
51 52
;;; User Variables:

53
(defcustom eshell-term-load-hook nil
Glenn Morris's avatar
Glenn Morris committed
54
  "A list of functions to call when loading `eshell-term'."
55
  :version "24.1"			; removed eshell-term-initialize
Gerd Moellmann's avatar
Gerd Moellmann committed
56 57 58 59 60 61 62 63 64
  :type 'hook
  :group 'eshell-term)

(defcustom eshell-visual-commands
  '("vi"                                ; what is going on??
    "screen" "top"                      ; ok, a valid program...
    "less" "more"                       ; M-x view-file
    "lynx" "ncftp"                      ; w3.el, ange-ftp
    "pine" "tin" "trn" "elm")           ; GNUS!!
65 66 67 68 69
  "A list of commands that present their output in a visual fashion.

Commands listed here are run in a term buffer.

See also `eshell-visual-subcommands' and `eshell-visual-options'."
Gerd Moellmann's avatar
Gerd Moellmann committed
70 71 72
  :type '(repeat string)
  :group 'eshell-term)

73 74
(defcustom eshell-visual-subcommands
  nil
75 76 77
  "An alist of subcommands that present their output in a visual fashion.

An alist of the form
78 79 80 81 82 83 84 85 86

  ((COMMAND1 SUBCOMMAND1 SUBCOMMAND2...)
   (COMMAND2 SUBCOMMAND1 ...))

of commands with subcommands that present their output in a
visual fashion.  A likely entry is

  (\"git\" \"log\" \"diff\" \"show\")

87 88 89
because git shows logs and diffs using a pager by default.

See also `eshell-visual-commands' and `eshell-visual-options'."
90
  :type '(repeat (cons (string :tag "Command")
91 92 93
		       (repeat (string :tag "Subcommand"))))
  :version "24.4"
  :group 'eshell-term)
94 95 96 97 98 99 100 101 102 103 104

(defcustom eshell-visual-options
  nil
  "An alist of the form

  ((COMMAND1 OPTION1 OPTION2...)
   (COMMAND2 OPTION1 ...))

of commands with options that present their output in a visual
fashion.  For example, a sensible entry would be

105
  (\"git\" \"--help\" \"--paginate\")
106 107

because \"git <command> --help\" shows the command's
108 109
documentation with a pager and \"git --paginate <command>\"
always uses a pager for output.
110 111

See also `eshell-visual-commands' and `eshell-visual-subcommands'."
112
  :type '(repeat (cons (string :tag "Command")
113 114 115
		       (repeat (string :tag "Option"))))
  :version "24.4"
  :group 'eshell-term)
116

117 118 119
;; If you change this from term-term-name, you need to ensure that the
;; value you choose exists in the system's terminfo database.  (Bug#12485)
(defcustom eshell-term-name term-term-name
Glenn Morris's avatar
Glenn Morris committed
120
  "Name to use for the TERM variable when running visual commands.
Gerd Moellmann's avatar
Gerd Moellmann committed
121 122
See `term-term-name' in term.el for more information on how this is
used."
123
  :version "24.3"	       ; eterm -> term-term-name = eterm-color
Gerd Moellmann's avatar
Gerd Moellmann committed
124 125 126 127
  :type 'string
  :group 'eshell-term)

(defcustom eshell-escape-control-x t
Glenn Morris's avatar
Glenn Morris committed
128
  "If non-nil, allow <C-x> to be handled by Emacs key in visual buffers.
129 130 131 132
See the variables `eshell-visual-commands',
`eshell-visual-subcommands', and `eshell-visual-options'.  If
this variable is set to nil, <C-x> will send that control
character to the invoked process."
Gerd Moellmann's avatar
Gerd Moellmann committed
133 134 135
  :type 'boolean
  :group 'eshell-term)

136 137 138 139
(defcustom eshell-destroy-buffer-when-process-dies nil
  "If non-nil, term buffers are destroyed after their processes die.
WARNING: Setting this to non-nil may result in unexpected
behavior for short-lived processes, see bug#18108."
Glenn Morris's avatar
Glenn Morris committed
140
  :version "25.1"
141 142 143
  :type 'boolean
  :group 'eshell-term)

Gerd Moellmann's avatar
Gerd Moellmann committed
144 145 146 147 148 149 150 151 152 153
;;; Internal Variables:

(defvar eshell-parent-buffer)

;;; Functions:

(defun eshell-term-initialize ()
  "Initialize the `term' interface code."
  (make-local-variable 'eshell-interpreter-alist)
  (setq eshell-interpreter-alist
154
	(cons (cons #'eshell-visual-command-p
Gerd Moellmann's avatar
Gerd Moellmann committed
155 156 157
		    'eshell-exec-visual)
	      eshell-interpreter-alist)))

158 159 160 161 162
(defun eshell-visual-command-p (command args)
  "Returns non-nil when given a visual command.
If either COMMAND or a subcommand in ARGS (e.g. git log) is a
visual command, returns non-nil."
  (let ((command (file-name-nondirectory command)))
Aidan Gauland's avatar
Aidan Gauland committed
163 164 165 166 167 168 169
    (and (eshell-interactive-output-p)
         (or (member command eshell-visual-commands)
             (member (car args)
                     (cdr (assoc command eshell-visual-subcommands)))
             (cl-intersection args
                              (cdr (assoc command eshell-visual-options))
                              :test 'string=)))))
170

Gerd Moellmann's avatar
Gerd Moellmann committed
171 172 173 174 175
(defun eshell-exec-visual (&rest args)
  "Run the specified PROGRAM in a terminal emulation buffer.
ARGS are passed to the program.  At the moment, no piping of input is
allowed."
  (let* (eshell-interpreter-alist
176
	 (interp (eshell-find-interpreter (car args) (cdr args)))
Gerd Moellmann's avatar
Gerd Moellmann committed
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
	 (program (car interp))
	 (args (eshell-flatten-list
		(eshell-stringify-list (append (cdr interp)
					       (cdr args)))))
	 (term-buf
	  (generate-new-buffer
	   (concat "*" (file-name-nondirectory program) "*")))
	 (eshell-buf (current-buffer)))
    (save-current-buffer
      (switch-to-buffer term-buf)
      (term-mode)
      (set (make-local-variable 'term-term-name) eshell-term-name)
      (make-local-variable 'eshell-parent-buffer)
      (setq eshell-parent-buffer eshell-buf)
      (term-exec term-buf program program nil args)
      (let ((proc (get-buffer-process term-buf)))
	(if (and proc (eq 'run (process-status proc)))
	    (set-process-sentinel proc 'eshell-term-sentinel)
	  (error "Failed to invoke visual command")))
      (term-char-mode)
      (if eshell-escape-control-x
	  (term-set-escape-char ?\C-x))))
  nil)

201
;; Process sentinels receive two arguments.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
(defun eshell-term-sentinel (proc msg)
  "Clean up the buffer visiting PROC.
If `eshell-destroy-buffer-when-process-dies' is non-nil, destroy
the buffer."
  (term-sentinel proc msg) ;; First call the normal term sentinel.
  (when eshell-destroy-buffer-when-process-dies
    (let ((proc-buf (process-buffer proc)))
      (when (and proc-buf (buffer-live-p proc-buf)
                 (not (eq 'run (process-status proc)))
                 (= (process-exit-status proc) 0))
        (if (eq (current-buffer) proc-buf)
            (let ((buf (and (boundp 'eshell-parent-buffer)
                            eshell-parent-buffer
                            (buffer-live-p eshell-parent-buffer)
                            eshell-parent-buffer)))
              (if buf
                  (switch-to-buffer buf))))
        (kill-buffer proc-buf)))))
Gerd Moellmann's avatar
Gerd Moellmann committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241

;; jww (1999-09-17): The code below will allow Eshell to send input
;; characters directly to the currently running interactive process.
;; However, since this would introduce other problems that would need
;; solutions, I'm going to let it wait until after 2.1.

; (defvar eshell-term-raw-map nil
;   "Keyboard map for sending characters directly to the inferior process.")
; (defvar eshell-term-escape-char nil
;   "Escape character for char-sub-mode of term mode.
; Do not change it directly;  use term-set-escape-char instead.")
; (defvar eshell-term-raw-escape-map nil)

; (defun eshell-term-send-raw-string (chars)
;   (goto-char eshell-last-output-end)
;   (process-send-string (eshell-interactive-process) chars))

; (defun eshell-term-send-raw ()
;   "Send the last character typed through the terminal-emulator
; without any interpretation."
;   (interactive)
;   ;; Convert `return' to C-m, etc.
242 243 244 245
;   (if (and (symbolp last-input-event)
;	   (get last-input-event 'ascii-character))
;       (setq last-input-event (get last-input-event 'ascii-character)))
;   (eshell-term-send-raw-string (make-string 1 last-input-event)))
Gerd Moellmann's avatar
Gerd Moellmann committed
246 247 248

; (defun eshell-term-send-raw-meta ()
;   (interactive)
249
;   (if (symbolp last-input-event)
Gerd Moellmann's avatar
Gerd Moellmann committed
250
;       ;; Convert `return' to C-m, etc.
251
;       (let ((tmp (get last-input-event 'event-symbol-elements)))
Gerd Moellmann's avatar
Gerd Moellmann committed
252
;	(if tmp
253 254
;	    (setq last-input-event (car tmp)))
;	(if (symbolp last-input-event)
Gerd Moellmann's avatar
Gerd Moellmann committed
255
;	    (progn
256 257 258 259 260 261 262
;	      (setq tmp (get last-input-event 'ascii-character))
;	      (if tmp (setq last-input-event tmp))))))
;   (eshell-term-send-raw-string (if (and (numberp last-input-event)
;					(> last-input-event 127)
;					(< last-input-event 256))
;				   (make-string 1 last-input-event)
;				 (format "\e%c" last-input-event))))
Gerd Moellmann's avatar
Gerd Moellmann committed
263 264 265 266 267 268 269

; (defun eshell-term-mouse-paste (click arg)
;   "Insert the last stretch of killed text at the position clicked on."
;   (interactive "e\nP")
;   (if (boundp 'xemacs-logo)
;       (eshell-term-send-raw-string
;        (or (condition-case () (x-get-selection) (error ()))
270
;	   (error "No selection available")))
Gerd Moellmann's avatar
Gerd Moellmann committed
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
;     ;; Give temporary modes such as isearch a chance to turn off.
;     (run-hooks 'mouse-leave-buffer-hook)
;     (setq this-command 'yank)
;     (eshell-term-send-raw-string
;      (current-kill (cond ((listp arg) 0)
;			 ((eq arg '-) -1)
;			 (t (1- arg)))))))

; ;; Which would be better:  "\e[A" or "\eOA"? readline accepts either.
; ;; For my configuration it's definitely better \eOA but YMMV. -mm
; ;; For example: vi works with \eOA while elm wants \e[A ...
; (defun eshell-term-send-up    () (interactive) (eshell-term-send-raw-string "\eOA"))
; (defun eshell-term-send-down  () (interactive) (eshell-term-send-raw-string "\eOB"))
; (defun eshell-term-send-right () (interactive) (eshell-term-send-raw-string "\eOC"))
; (defun eshell-term-send-left  () (interactive) (eshell-term-send-raw-string "\eOD"))
; (defun eshell-term-send-home  () (interactive) (eshell-term-send-raw-string "\e[1~"))
; (defun eshell-term-send-end   () (interactive) (eshell-term-send-raw-string "\e[4~"))
; (defun eshell-term-send-prior () (interactive) (eshell-term-send-raw-string "\e[5~"))
; (defun eshell-term-send-next  () (interactive) (eshell-term-send-raw-string "\e[6~"))
; (defun eshell-term-send-del   () (interactive) (eshell-term-send-raw-string "\C-?"))
; (defun eshell-term-send-backspace  () (interactive) (eshell-term-send-raw-string "\C-H"))

; (defun eshell-term-set-escape-char (c)
;   "Change term-escape-char and keymaps that depend on it."
;   (if eshell-term-escape-char
;       (define-key eshell-term-raw-map eshell-term-escape-char 'eshell-term-send-raw))
;   (setq c (make-string 1 c))
;   (define-key eshell-term-raw-map c eshell-term-raw-escape-map)
;   ;; Define standard bindings in eshell-term-raw-escape-map
;   (define-key eshell-term-raw-escape-map "\C-x"
;     (lookup-key (current-global-map) "\C-x"))
;   (define-key eshell-term-raw-escape-map "\C-v"
;     (lookup-key (current-global-map) "\C-v"))
;   (define-key eshell-term-raw-escape-map "\C-u"
;     (lookup-key (current-global-map) "\C-u"))
;   (define-key eshell-term-raw-escape-map c 'eshell-term-send-raw))

; (defun eshell-term-char-mode ()
;   "Switch to char (\"raw\") sub-mode of term mode.
; Each character you type is sent directly to the inferior without
; intervention from Emacs, except for the escape character (usually C-c)."
;   (interactive)
;   (if (not eshell-term-raw-map)
;       (let* ((map (make-keymap))
;	     (esc-map (make-keymap))
;	     (i 0))
;	(while (< i 128)
;	  (define-key map (make-string 1 i) 'eshell-term-send-raw)
;	  (define-key esc-map (make-string 1 i) 'eshell-term-send-raw-meta)
;	  (setq i (1+ i)))
;	(define-key map "\e" esc-map)
;	(setq eshell-term-raw-map map)
;	(setq eshell-term-raw-escape-map
;	      (copy-keymap (lookup-key (current-global-map) "\C-x")))
;	(if (boundp 'xemacs-logo)
;	    (define-key eshell-term-raw-map [button2] 'eshell-term-mouse-paste)
;	  (define-key eshell-term-raw-map [mouse-2] 'eshell-term-mouse-paste))
;	(define-key eshell-term-raw-map [up] 'eshell-term-send-up)
;	(define-key eshell-term-raw-map [down] 'eshell-term-send-down)
;	(define-key eshell-term-raw-map [right] 'eshell-term-send-right)
;	(define-key eshell-term-raw-map [left] 'eshell-term-send-left)
;	(define-key eshell-term-raw-map [delete] 'eshell-term-send-del)
;	(define-key eshell-term-raw-map [backspace] 'eshell-term-send-backspace)
;	(define-key eshell-term-raw-map [home] 'eshell-term-send-home)
;	(define-key eshell-term-raw-map [end] 'eshell-term-send-end)
;	(define-key eshell-term-raw-map [prior] 'eshell-term-send-prior)
;	(define-key eshell-term-raw-map [next] 'eshell-term-send-next)
;	(eshell-term-set-escape-char ?\C-c))))

; (defun eshell-term-line-mode  ()
;   "Switch to line (\"cooked\") sub-mode of eshell-term mode."
;  (use-local-map term-old-mode-map))

344
(provide 'em-term)
Gerd Moellmann's avatar
Gerd Moellmann committed
345

346 347 348 349
;; Local Variables:
;; generated-autoload-file: "esh-groups.el"
;; End:

Gerd Moellmann's avatar
Gerd Moellmann committed
350
;;; em-term.el ends here