mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-12-28 10:56:36 +00:00
*** empty log message ***
This commit is contained in:
parent
022499fab9
commit
affbf64775
@ -1,5 +1,10 @@
|
||||
2000-06-23 Gerd Moellmann <gerd@gnu.org>
|
||||
|
||||
* Makefile.in (DONTCOMPILE): Add eshell/esh-maint.el.
|
||||
|
||||
* eshell/esh-cmd.el (eshell-rewrite-for-command): Use cdr and
|
||||
cddr instead of cdddr.
|
||||
|
||||
* eshell/esh-util.el (eshell-sublist): Use eshell-copy-list
|
||||
instead of copy-list.
|
||||
|
||||
|
270
lisp/eshell/em-alias.el
Normal file
270
lisp/eshell/em-alias.el
Normal file
@ -0,0 +1,270 @@
|
||||
;;; em-alias --- creation and management of command aliases
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-alias)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-alias nil
|
||||
"Command aliases allow for easy definition of alternate commands."
|
||||
:tag "Command aliases"
|
||||
:link '(info-link "(eshell.info)Command aliases")
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Command aliases greatly simplify the definition of new commands.
|
||||
;; They exist as an alternative to alias functions, which are
|
||||
;; otherwise quite superior, being more flexible and natural to the
|
||||
;; Emacs Lisp environment (if somewhat trickier to define; [Alias
|
||||
;; functions]).
|
||||
;;
|
||||
;;;_* Creating aliases
|
||||
;;
|
||||
;; The user interface is simple: type 'alias' followed by the command
|
||||
;; name followed by the definition. Argument references are made
|
||||
;; using '$1', '$2', etc., or '$*'. For example:
|
||||
;;
|
||||
;; alias ll 'ls -l $*'
|
||||
;;
|
||||
;; This will cause the command 'll NEWS' to be replaced by 'ls -l
|
||||
;; NEWS'. This is then passed back to the command parser for
|
||||
;; reparsing.{Only the command text specified in the alias definition
|
||||
;; will be reparsed. Argument references (such as '$*') are handled
|
||||
;; using variable values, which means that the expansion will not be
|
||||
;; reparsed, but used directly.}
|
||||
;;
|
||||
;; To delete an alias, specify its name without a definition:
|
||||
;;
|
||||
;; alias ll
|
||||
;;
|
||||
;; Aliases are written to disk immediately after being defined or
|
||||
;; deleted. The filename in which they are kept is defined by the
|
||||
;; following variable:
|
||||
|
||||
(defcustom eshell-aliases-file (concat eshell-directory-name "alias")
|
||||
"*The file in which aliases are kept.
|
||||
Whenever an alias is defined by the user, using the `alias' command,
|
||||
it will be written to this file. Thus, alias definitions (and
|
||||
deletions) are always permanent. This approach was chosen for the
|
||||
sake of simplicity, since that's pretty much the only benefit to be
|
||||
gained by using this module."
|
||||
:type 'file
|
||||
:group 'eshell-alias)
|
||||
|
||||
;;;
|
||||
;; The format of this file is quite basic. It specifies the alias
|
||||
;; definitions in almost exactly the same way that the user entered
|
||||
;; them, minus any argument quoting (since interpolation is not done
|
||||
;; when the file is read). Hence, it is possible to add new aliases
|
||||
;; to the alias file directly, using a text editor rather than the
|
||||
;; `alias' command. Or, this method can be used for editing aliases
|
||||
;; that have already defined.
|
||||
;;
|
||||
;; Here is an example of a few different aliases, and they would
|
||||
;; appear in the aliases file:
|
||||
;;
|
||||
;; alias clean rm -fr **/.#*~
|
||||
;; alias commit cvs commit -m changes $*
|
||||
;; alias ll ls -l $*
|
||||
;; alias info (info)
|
||||
;; alias reindex glimpseindex -o ~/Mail
|
||||
;; alias compact for i in ~/Mail/**/*~*.bz2(Lk+50) { bzip2 -9v $i }
|
||||
;;
|
||||
;;;_* Auto-correction of bad commands
|
||||
;;
|
||||
;; When a user enters the same unknown command many times during a
|
||||
;; session, it is likely that they are experiencing a spelling
|
||||
;; difficulty associated with a certain command. To combat this,
|
||||
;; Eshell will offer to automatically define an alias for that
|
||||
;; mispelled command, once a given tolerance threshold has been
|
||||
;; reached.
|
||||
|
||||
(defcustom eshell-bad-command-tolerance 3
|
||||
"*The number of failed commands to ignore before creating an alias."
|
||||
:type 'integer
|
||||
:link '(custom-manual "(eshell.info)Auto-correction of bad commands")
|
||||
:group 'eshell-alias)
|
||||
|
||||
;;;
|
||||
;; Whenever the same bad command name is encountered this many times,
|
||||
;; the user will be prompted in the minibuffer to provide an alias
|
||||
;; name. An alias definition will then be created which will result
|
||||
;; in an equal call to the correct name. In this way, Eshell
|
||||
;; gradually learns about the commands that the user mistypes
|
||||
;; frequently, and will automatically correct them!
|
||||
;;
|
||||
;; Note that a '$*' is automatically appended at the end of the alias
|
||||
;; definition, so that entering it is unnecessary when specifying the
|
||||
;; corrected command name.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defcustom eshell-alias-load-hook '(eshell-alias-initialize)
|
||||
"*A hook that gets run when `eshell-alias' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-alias)
|
||||
|
||||
(defvar eshell-command-aliases-list nil
|
||||
"A list of command aliases currently defined by the user.
|
||||
Each element of this alias is a list of the form:
|
||||
|
||||
(NAME DEFINITION)
|
||||
|
||||
Where NAME is the textual name of the alias, and DEFINITION is the
|
||||
command string to replace that command with.
|
||||
|
||||
Note: this list should not be modified in your '.emacs' file. Rather,
|
||||
any desired alias definitions should be declared using the `alias'
|
||||
command, which will automatically write them to the file named by
|
||||
`eshell-aliases-file'.")
|
||||
|
||||
(put 'eshell-command-aliases-list 'risky-local-variable t)
|
||||
|
||||
(defvar eshell-failed-commands-alist nil
|
||||
"An alist of command name failures.")
|
||||
|
||||
(defun eshell-alias-initialize ()
|
||||
"Initialize the alias handling code."
|
||||
(make-local-variable 'eshell-failed-commands-alist)
|
||||
(make-local-hook 'eshell-alternate-command-hook)
|
||||
(add-hook 'eshell-alternate-command-hook 'eshell-fix-bad-commands t t)
|
||||
(eshell-read-aliases-list)
|
||||
(make-local-hook 'eshell-named-command-hook)
|
||||
(add-hook 'eshell-named-command-hook 'eshell-maybe-replace-by-alias t t))
|
||||
|
||||
(defun eshell/alias (&optional alias &rest definition)
|
||||
"Define an ALIAS in the user's alias list using DEFINITION."
|
||||
(if (not alias)
|
||||
(eshell-for alias eshell-command-aliases-list
|
||||
(eshell-print (apply 'format "alias %s %s\n" alias)))
|
||||
(if (not definition)
|
||||
(setq eshell-command-aliases-list
|
||||
(delq (assoc alias eshell-command-aliases-list)
|
||||
eshell-command-aliases-list))
|
||||
(and (stringp definition)
|
||||
(set-text-properties 0 (length definition) nil definition))
|
||||
(let ((def (assoc alias eshell-command-aliases-list))
|
||||
(alias-def (list alias
|
||||
(eshell-flatten-and-stringify definition))))
|
||||
(if def
|
||||
(setq eshell-command-aliases-list
|
||||
(delq def eshell-command-aliases-list)))
|
||||
(setq eshell-command-aliases-list
|
||||
(cons alias-def eshell-command-aliases-list))))
|
||||
(eshell-write-aliases-list))
|
||||
nil)
|
||||
|
||||
(defun pcomplete/eshell-mode/alias ()
|
||||
"Completion function for Eshell's `alias' command."
|
||||
(pcomplete-here (eshell-alias-completions pcomplete-stub)))
|
||||
|
||||
(defun eshell-read-aliases-list ()
|
||||
"Read in an aliases list from `eshell-aliases-file'."
|
||||
(let ((file eshell-aliases-file))
|
||||
(when (file-readable-p file)
|
||||
(setq eshell-command-aliases-list
|
||||
(with-temp-buffer
|
||||
(let (eshell-command-aliases-list)
|
||||
(insert-file-contents file)
|
||||
(while (not (eobp))
|
||||
(if (re-search-forward
|
||||
"^alias\\s-+\\(\\S-+\\)\\s-+\\(.+\\)")
|
||||
(setq eshell-command-aliases-list
|
||||
(cons (list (match-string 1)
|
||||
(match-string 2))
|
||||
eshell-command-aliases-list)))
|
||||
(forward-line 1))
|
||||
eshell-command-aliases-list))))))
|
||||
|
||||
(defun eshell-write-aliases-list ()
|
||||
"Write out the current aliases into `eshell-aliases-file'."
|
||||
(if (file-writable-p (file-name-directory eshell-aliases-file))
|
||||
(let ((eshell-current-handles
|
||||
(eshell-create-handles eshell-aliases-file 'overwrite)))
|
||||
(eshell/alias)
|
||||
(eshell-close-handles 0))))
|
||||
|
||||
(defsubst eshell-lookup-alias (name)
|
||||
"Check whether NAME is aliased. Return the alias if there is one."
|
||||
(assoc name eshell-command-aliases-list))
|
||||
|
||||
(defvar eshell-prevent-alias-expansion nil)
|
||||
|
||||
(defun eshell-maybe-replace-by-alias (command args)
|
||||
"If COMMAND has an alias definition, call that instead using RAGS."
|
||||
(unless (and eshell-prevent-alias-expansion
|
||||
(member command eshell-prevent-alias-expansion))
|
||||
(let ((alias (eshell-lookup-alias command)))
|
||||
(if alias
|
||||
(throw 'eshell-replace-command
|
||||
(list
|
||||
'let
|
||||
(list
|
||||
(list 'eshell-command-name
|
||||
(list 'quote eshell-last-command-name))
|
||||
(list 'eshell-command-arguments
|
||||
(list 'quote eshell-last-arguments))
|
||||
(list 'eshell-prevent-alias-expansion
|
||||
(list 'quote
|
||||
(cons command
|
||||
eshell-prevent-alias-expansion))))
|
||||
(eshell-parse-command (nth 1 alias))))))))
|
||||
|
||||
(defun eshell-alias-completions (name)
|
||||
"Find all possible completions for NAME.
|
||||
These are all the command aliases which begin with NAME."
|
||||
(let (completions)
|
||||
(eshell-for alias eshell-command-aliases-list
|
||||
(if (string-match (concat "^" name) (car alias))
|
||||
(setq completions (cons (car alias) completions))))
|
||||
completions))
|
||||
|
||||
(defun eshell-fix-bad-commands (name)
|
||||
"If the user repeatedly a bad command NAME, make an alias for them."
|
||||
(ignore
|
||||
(unless (file-name-directory name)
|
||||
(let ((entry (assoc name eshell-failed-commands-alist)))
|
||||
(if (not entry)
|
||||
(setq eshell-failed-commands-alist
|
||||
(cons (cons name 1) eshell-failed-commands-alist))
|
||||
(if (< (cdr entry) eshell-bad-command-tolerance)
|
||||
(setcdr entry (1+ (cdr entry)))
|
||||
(let ((alias (concat
|
||||
(read-string
|
||||
(format "Define alias for \"%s\": " name))
|
||||
" $*")))
|
||||
(eshell/alias name alias)
|
||||
(throw 'eshell-replace-command
|
||||
(list
|
||||
'let
|
||||
(list
|
||||
(list 'eshell-command-name
|
||||
(list 'quote name))
|
||||
(list 'eshell-command-arguments
|
||||
(list 'quote eshell-last-arguments))
|
||||
(list 'eshell-prevent-alias-expansion
|
||||
(list 'quote
|
||||
(cons name
|
||||
eshell-prevent-alias-expansion))))
|
||||
(eshell-parse-command alias))))))))))
|
||||
|
||||
;;; em-alias.el ends here
|
90
lisp/eshell/em-banner.el
Normal file
90
lisp/eshell/em-banner.el
Normal file
@ -0,0 +1,90 @@
|
||||
;;; em-banner --- sample module that displays a login banner
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-banner)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-banner nil
|
||||
"This sample module displays a welcome banner at login.
|
||||
It exists so that others wishing to create their own Eshell extension
|
||||
modules may have a simple template to begin with."
|
||||
:tag "Login banner"
|
||||
:link '(info-link "(eshell.info)Login banner")
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; There is nothing to be done or configured in order to use this
|
||||
;; module, other than to select it by customizing the variable
|
||||
;; `eshell-modules-list'. It will then display a version information
|
||||
;; message whenever Eshell is loaded.
|
||||
;;
|
||||
;; This code is only an example of a how to write a well-formed
|
||||
;; extension module for Eshell. The better way to display login text
|
||||
;; is to use the `eshell-script' module, and to echo the desired
|
||||
;; strings from the user's `eshell-login-script' file.
|
||||
;;
|
||||
;; There is one configuration variable, which demonstrates how to
|
||||
;; properly define a customization variable in an extension module.
|
||||
;; In this case, it allows the user to change the string which
|
||||
;; displays at login time.
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-banner-message "Welcome to the Emacs shell\n\n"
|
||||
"*The banner message to be displayed when Eshell is loaded.
|
||||
This can be any sexp, and should end with at least two newlines."
|
||||
:type 'sexp
|
||||
:group 'eshell-banner)
|
||||
|
||||
(put 'eshell-banner-message 'risky-local-variable t)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'esh-util)
|
||||
|
||||
(defcustom eshell-banner-load-hook '(eshell-banner-initialize)
|
||||
"*A list of functions to run when `eshell-banner' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-banner)
|
||||
|
||||
(defun eshell-banner-initialize ()
|
||||
"Output a welcome banner on initialization."
|
||||
;; it's important to use `eshell-interactive-print' rather than
|
||||
;; `insert', because `insert' doesn't know how to interact with the
|
||||
;; I/O code used by Eshell
|
||||
(unless eshell-non-interactive-p
|
||||
(assert eshell-mode)
|
||||
(assert eshell-banner-message)
|
||||
(let ((msg (eval eshell-banner-message)))
|
||||
(assert msg)
|
||||
(eshell-interactive-print msg))))
|
||||
|
||||
(eshell-deftest banner banner-displayed
|
||||
"Startup banner is displayed at point-min"
|
||||
(assert eshell-banner-message)
|
||||
(let ((msg (eval eshell-banner-message)))
|
||||
(assert msg)
|
||||
(goto-char (point-min))
|
||||
(looking-at msg)))
|
||||
|
||||
;;; em-banner.el ends here
|
183
lisp/eshell/em-basic.el
Normal file
183
lisp/eshell/em-basic.el
Normal file
@ -0,0 +1,183 @@
|
||||
;;; em-basic --- basic shell builtin commands
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-basic)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-basic nil
|
||||
"The \"basic\" code provides a set of convenience functions which
|
||||
are traditionally considered shell builtins. Since all of the
|
||||
functionality provided by them is accessible through Lisp, they are
|
||||
not really builtins at all, but offer a command-oriented way to do the
|
||||
same thing."
|
||||
:tag "Basic shell commands"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; There are very few basic Eshell commands -- so-called builtins.
|
||||
;; They are: echo, umask, and version.
|
||||
;;
|
||||
;;;_* `echo'
|
||||
;;
|
||||
;; The `echo' command repeats its arguments to the screen. It is
|
||||
;; optional whether this is done in a Lisp-friendly fashion (so that
|
||||
;; the value of echo is useful to a Lisp command using the result of
|
||||
;; echo as an argument), or whether it should try to act like a normal
|
||||
;; shell echo, and always result in a flat string being returned.
|
||||
|
||||
(defcustom eshell-plain-echo-behavior nil
|
||||
"*If non-nil, `echo' tries to behave like an ordinary shell echo.
|
||||
This comes at some detriment to Lisp functionality. However, the Lisp
|
||||
equivalent of `echo' can always be achieved by using `identity'."
|
||||
:type 'boolean
|
||||
:group 'eshell-basic)
|
||||
|
||||
;;;
|
||||
;; An example of the difference is the following:
|
||||
;;
|
||||
;; echo Hello world
|
||||
;;
|
||||
;; If `eshell-plain-echo-behavior' is non-nil, this will yield the
|
||||
;; string "Hello world". If Lisp behavior is enabled, however, it
|
||||
;; will yield a list whose two elements are the strings "Hello" and
|
||||
;; "world". The way to write an equivalent expression for both would
|
||||
;; be:
|
||||
;;
|
||||
;; echo "Hello world"
|
||||
;;
|
||||
;; This always returns a single string.
|
||||
;;
|
||||
;;;_* `umask'
|
||||
;;
|
||||
;; The umask command changes the default file permissions for newly
|
||||
;; created files. It uses the same syntax as bash.
|
||||
;;
|
||||
;;;_* `version'
|
||||
;;
|
||||
;; This command reports the version number for Eshell and all its
|
||||
;; dependent module, including the date when those modules were last
|
||||
;; modified.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'esh-opt)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-echo (args &optional output-newline)
|
||||
"Implementation code for a Lisp version of `echo'.
|
||||
It returns a formatted value that should be passed to `eshell-print'
|
||||
or `eshell-printn' for display."
|
||||
(if eshell-plain-echo-behavior
|
||||
(concat (apply 'eshell-flatten-and-stringify args) "\n")
|
||||
(let ((value
|
||||
(cond
|
||||
((= (length args) 0) "")
|
||||
((= (length args) 1)
|
||||
(car args))
|
||||
(t
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (arg)
|
||||
(if (stringp arg)
|
||||
(set-text-properties 0 (length arg) nil arg))
|
||||
arg))
|
||||
args)))))
|
||||
(if output-newline
|
||||
(cond
|
||||
((stringp value)
|
||||
(concat value "\n"))
|
||||
((listp value)
|
||||
(append value (list "\n")))
|
||||
(t
|
||||
(concat (eshell-stringify value) "\n")))
|
||||
value))))
|
||||
|
||||
(defun eshell/echo (&rest args)
|
||||
"Implementation of `echo'. See `eshell-plain-echo-behavior'."
|
||||
(eshell-eval-using-options
|
||||
"echo" args
|
||||
'((?n nil nil output-newline "terminate with a newline")
|
||||
(?h "help" nil nil "output this help screen")
|
||||
:preserve-args
|
||||
:usage "[-n] [object]")
|
||||
(eshell-echo args output-newline)))
|
||||
|
||||
(defun eshell/printnl (&rest args)
|
||||
"Print out each of the argument, separated by newlines."
|
||||
(let ((elems (eshell-flatten-list args)))
|
||||
(while elems
|
||||
(eshell-printn (eshell-echo (list (car elems))))
|
||||
(setq elems (cdr elems)))))
|
||||
|
||||
(defun eshell/listify (&rest args)
|
||||
"Return the argument(s) as a single list."
|
||||
(if (> (length args) 1)
|
||||
args
|
||||
(if (listp (car args))
|
||||
(car args)
|
||||
(list (car args)))))
|
||||
|
||||
(defun eshell/umask (&rest args)
|
||||
"Shell-like implementation of `umask'."
|
||||
(eshell-eval-using-options
|
||||
"umask" args
|
||||
'((?S "symbolic" nil symbolic-p "display umask symbolically")
|
||||
(?h "help" nil nil "display this usage message")
|
||||
:usage "[-S] [mode]")
|
||||
(if (or (not args) symbolic-p)
|
||||
(let ((modstr
|
||||
(concat "000"
|
||||
(format "%o"
|
||||
(logand (lognot (default-file-modes))
|
||||
511)))))
|
||||
(setq modstr (substring modstr (- (length modstr) 3)))
|
||||
(when symbolic-p
|
||||
(let ((mode (default-file-modes)))
|
||||
(setq modstr
|
||||
(format
|
||||
"u=%s,g=%s,o=%s"
|
||||
(concat (and (= (logand mode 64) 64) "r")
|
||||
(and (= (logand mode 128) 128) "w")
|
||||
(and (= (logand mode 256) 256) "x"))
|
||||
(concat (and (= (logand mode 8) 8) "r")
|
||||
(and (= (logand mode 16) 16) "w")
|
||||
(and (= (logand mode 32) 32) "x"))
|
||||
(concat (and (= (logand mode 1) 1) "r")
|
||||
(and (= (logand mode 2) 2) "w")
|
||||
(and (= (logand mode 4) 4) "x"))))))
|
||||
(eshell-printn modstr))
|
||||
(setcar args (eshell-convert (car args)))
|
||||
(if (numberp (car args))
|
||||
(set-default-file-modes
|
||||
(- 511 (car (read-from-string
|
||||
(concat "?\\" (number-to-string (car args)))))))
|
||||
(error "setting umask symbolically is not yet implemented"))
|
||||
(eshell-print
|
||||
"Warning: umask changed for all new files created by Emacs.\n"))
|
||||
nil))
|
||||
|
||||
(eval-when-compile
|
||||
(defvar print-func))
|
||||
|
||||
;;; em-basic.el ends here
|
443
lisp/eshell/em-cmpl.el
Normal file
443
lisp/eshell/em-cmpl.el
Normal file
@ -0,0 +1,443 @@
|
||||
;;; em-cmpl --- completion using the TAB key
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-cmpl)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-cmpl nil
|
||||
"This module provides a programmable completion function bound to
|
||||
the TAB key, which allows for completing command names, file names,
|
||||
variable names, arguments, etc."
|
||||
:tag "Argument completion"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Eshell, by using the pcomplete package, provides a full
|
||||
;; programmable completion facility that is comparable to shells like
|
||||
;; tcsh or zsh.
|
||||
;;
|
||||
;; Completions are context-sensitive, which means that pressing <TAB>
|
||||
;; after the command 'rmdir' will result in a list of directories,
|
||||
;; while doing so after 'rm' will result in a list of all file
|
||||
;; entries.
|
||||
;;
|
||||
;; Many builtin completion rules are provided, for commands such as
|
||||
;; `cvs', or RedHat's `rpm' utility. Adding new completion rules is
|
||||
;; no more difficult than writing a plain Lisp functions, and they can
|
||||
;; be debugged, profiled, and compiled using exactly the same
|
||||
;; facilities (since in fact, they *are* just Lisp functions). See
|
||||
;; the definition of the function `pcomplete/make' for an example of
|
||||
;; how to write a completion function.
|
||||
;;
|
||||
;; The completion facility is very easy to use. Just press TAB. If
|
||||
;; there are a large number of possible completions, a buffer will
|
||||
;; appearing showing a list of them. Completions may be selected from
|
||||
;; that buffer using the mouse. If no completion is selected, and the
|
||||
;; user starts doing something else, the display buffer will
|
||||
;; automatically disappear.
|
||||
;;
|
||||
;; If the list of possible completions is very small, Eshell will
|
||||
;; "cycle" through them, selecting a different entry each time <TAB>
|
||||
;; is pressed. <S-TAB> may be used to cycle in the opposite
|
||||
;; direction.
|
||||
;;
|
||||
;; Glob patterns can also be cycled. For example, entering 'echo
|
||||
;; x*<tab>' will cycle through all the filenames beginning with 'x'.
|
||||
;; This is done because the glob list is treated as though it were a
|
||||
;; list of possible completions. Pressing <C-c SPC> will insert all
|
||||
;; of the matching glob patterns at point.
|
||||
;;
|
||||
;; If a Lisp form is being entered, <TAB> will complete the Lisp
|
||||
;; symbol name, in exactly the same way that <M-TAB> does in Emacs
|
||||
;; Lisp mode.
|
||||
;;
|
||||
;; The list of possible completions can be viewed at any point by
|
||||
;; pressing <M-?>.
|
||||
;;
|
||||
;; Finally, context-related help can be accessed by pressing <C-c i>.
|
||||
;; This only works well if the completion function has provided Eshell
|
||||
;; with sufficient pointers to locate the relevant help text.
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-cmpl-load-hook '(eshell-cmpl-initialize)
|
||||
"*A list of functions to run when `eshell-cmpl' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-show-lisp-completions nil
|
||||
"*If non-nil, include Lisp functions in the command completion list.
|
||||
If this variable is nil, Lisp completion can still be done in command
|
||||
position by using M-TAB instead of TAB."
|
||||
:type 'boolean
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-show-lisp-alternatives t
|
||||
"*If non-nil, and no other completions found, show Lisp functions.
|
||||
Setting this variable means nothing if `eshell-show-lisp-completions'
|
||||
is non-nil."
|
||||
:type 'boolean
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-no-completion-during-jobs t
|
||||
"*If non-nil, don't allow completion while a process is running."
|
||||
:type 'boolean
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-command-completions-alist
|
||||
'(("acroread" . "\\.pdf\\'")
|
||||
("xpdf" . "\\.pdf\\'")
|
||||
("ar" . "\\.[ao]\\'")
|
||||
("gcc" . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
|
||||
("g++" . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
|
||||
("cc" . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
|
||||
("CC" . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
|
||||
("acc" . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
|
||||
("bcc" . "\\.[Cc]\\([Cc]\\|[Pp][Pp]\\)?\\'")
|
||||
("objdump" . "\\(\\`[^.]*\\|\\.[ao]\\)\\'")
|
||||
("nm" . "\\(\\`[^.]*\\|\\.[ao]\\)\\'")
|
||||
("gdb" . "\\`\\([^.]*\\|a\\.out\\)\\'")
|
||||
("dbx" . "\\`\\([^.]*\\|a\\.out\\)\\'")
|
||||
("sdb" . "\\`\\([^.]*\\|a\\.out\\)\\'")
|
||||
("adb" . "\\`\\([^.]*\\|a\\.out\\)\\'"))
|
||||
"*An alist that defines simple argument type correlations.
|
||||
This is provided for common commands, as a simplistic alternative
|
||||
to writing a completion function."
|
||||
:type '(repeat (cons string regexp))
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-file-ignore "~\\'"
|
||||
(documentation-property 'pcomplete-file-ignore
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-file-ignore 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-dir-ignore
|
||||
(format "\\`\\(\\.\\.?\\|CVS\\)%c\\'" directory-sep-char)
|
||||
(documentation-property 'pcomplete-dir-ignore
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-dir-ignore 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-ignore-case (eshell-under-windows-p)
|
||||
(documentation-property 'pcomplete-ignore-case
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-ignore-case 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-autolist nil
|
||||
(documentation-property 'pcomplete-autolist
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-autolist 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-suffix-list (list directory-sep-char ?:)
|
||||
(documentation-property 'pcomplete-suffix-list
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-suffix-list 'custom-type)
|
||||
:group 'pcomplete)
|
||||
|
||||
(defcustom eshell-cmpl-recexact nil
|
||||
(documentation-property 'pcomplete-recexact
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-recexact 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-man-function 'man
|
||||
(documentation-property 'pcomplete-man-function
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-man-function 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-compare-entry-function 'file-newer-than-file-p
|
||||
(documentation-property 'pcomplete-compare-entry-function
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-compare-entry-function 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-expand-before-complete nil
|
||||
(documentation-property 'pcomplete-expand-before-complete
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-expand-before-complete 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-cycle-completions t
|
||||
(documentation-property 'pcomplete-cycle-completions
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-cycle-completions 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-cycle-cutoff-length 5
|
||||
(documentation-property 'pcomplete-cycle-cutoff-length
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-cycle-cutoff-length 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-restore-window-delay 1
|
||||
(documentation-property 'pcomplete-restore-window-delay
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-restore-window-delay 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-command-completion-function
|
||||
(function
|
||||
(lambda ()
|
||||
(pcomplete-here (eshell-complete-commands-list))))
|
||||
(documentation-property 'pcomplete-command-completion-function
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-command-completion-function 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-cmpl-command-name-function
|
||||
'eshell-completion-command-name
|
||||
(documentation-property 'pcomplete-command-name-function
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-command-name-function 'custom-type)
|
||||
:group 'eshell-cmpl)
|
||||
|
||||
(defcustom eshell-default-completion-function
|
||||
(function
|
||||
(lambda ()
|
||||
(while (pcomplete-here
|
||||
(pcomplete-dirs-or-entries
|
||||
(cdr (assoc (funcall eshell-cmpl-command-name-function)
|
||||
eshell-command-completions-alist)))))))
|
||||
(documentation-property 'pcomplete-default-completion-function
|
||||
'variable-documentation)
|
||||
:type (get 'pcomplete-default-completion-function 'custom-type)
|
||||
:group 'pcomplete)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-cmpl-initialize ()
|
||||
"Initialize the completions module."
|
||||
(unless (fboundp 'pcomplete)
|
||||
(load "pcmpl-auto" t t))
|
||||
(set (make-local-variable 'pcomplete-command-completion-function)
|
||||
eshell-command-completion-function)
|
||||
(set (make-local-variable 'pcomplete-command-name-function)
|
||||
eshell-cmpl-command-name-function)
|
||||
(set (make-local-variable 'pcomplete-default-completion-function)
|
||||
eshell-default-completion-function)
|
||||
(set (make-local-variable 'pcomplete-parse-arguments-function)
|
||||
'eshell-complete-parse-arguments)
|
||||
(set (make-local-variable 'pcomplete-file-ignore)
|
||||
eshell-cmpl-file-ignore)
|
||||
(set (make-local-variable 'pcomplete-dir-ignore)
|
||||
eshell-cmpl-dir-ignore)
|
||||
(set (make-local-variable 'pcomplete-ignore-case)
|
||||
eshell-cmpl-ignore-case)
|
||||
(set (make-local-variable 'pcomplete-autolist)
|
||||
eshell-cmpl-autolist)
|
||||
(set (make-local-variable 'pcomplete-suffix-list)
|
||||
eshell-cmpl-suffix-list)
|
||||
(set (make-local-variable 'pcomplete-recexact)
|
||||
eshell-cmpl-recexact)
|
||||
(set (make-local-variable 'pcomplete-man-function)
|
||||
eshell-cmpl-man-function)
|
||||
(set (make-local-variable 'pcomplete-compare-entry-function)
|
||||
eshell-cmpl-compare-entry-function)
|
||||
(set (make-local-variable 'pcomplete-expand-before-complete)
|
||||
eshell-cmpl-expand-before-complete)
|
||||
(set (make-local-variable 'pcomplete-cycle-completions)
|
||||
eshell-cmpl-cycle-completions)
|
||||
(set (make-local-variable 'pcomplete-cycle-cutoff-length)
|
||||
eshell-cmpl-cycle-cutoff-length)
|
||||
(set (make-local-variable 'pcomplete-restore-window-delay)
|
||||
eshell-cmpl-restore-window-delay)
|
||||
;; `pcomplete-arg-quote-list' should only be set after all the
|
||||
;; load-hooks for any other extension modules have been run, which
|
||||
;; is true at the time `eshell-mode-hook' is run
|
||||
(make-local-hook 'eshell-mode-hook)
|
||||
(add-hook 'eshell-mode-hook
|
||||
(function
|
||||
(lambda ()
|
||||
(set (make-local-variable 'pcomplete-arg-quote-list)
|
||||
eshell-special-chars-outside-quoting))) nil t)
|
||||
(make-local-hook 'pcomplete-quote-arg-hook)
|
||||
(add-hook 'pcomplete-quote-arg-hook 'eshell-quote-backslash nil t)
|
||||
(define-key eshell-mode-map [(meta tab)] 'lisp-complete-symbol)
|
||||
(define-key eshell-mode-map [(meta control ?i)] 'lisp-complete-symbol)
|
||||
(define-key eshell-command-map [(meta ?h)] 'eshell-completion-help)
|
||||
(define-key eshell-command-map [tab] 'pcomplete-expand-and-complete)
|
||||
(define-key eshell-command-map [(control ?i)]
|
||||
'pcomplete-expand-and-complete)
|
||||
(define-key eshell-command-map [space] 'pcomplete-expand)
|
||||
(define-key eshell-command-map [? ] 'pcomplete-expand)
|
||||
(define-key eshell-mode-map [tab] 'pcomplete)
|
||||
(define-key eshell-mode-map [(control ?i)] 'pcomplete)
|
||||
;; jww (1999-10-19): Will this work on anything but X?
|
||||
(if (eshell-under-xemacs-p)
|
||||
(define-key eshell-mode-map [iso-left-tab] 'pcomplete-reverse)
|
||||
(define-key eshell-mode-map [(shift iso-lefttab)] 'pcomplete-reverse)
|
||||
(define-key eshell-mode-map [(shift control ?i)] 'pcomplete-reverse))
|
||||
(define-key eshell-mode-map [(meta ??)] 'pcomplete-list))
|
||||
|
||||
(defun eshell-completion-command-name ()
|
||||
"Return the command name, possibly sans globbing."
|
||||
(let ((cmd (file-name-nondirectory (pcomplete-arg 'first))))
|
||||
(setq cmd (if (and (> (length cmd) 0)
|
||||
(eq (aref cmd 0) ?*))
|
||||
(substring cmd 1)
|
||||
cmd))
|
||||
(if (eshell-under-windows-p)
|
||||
(file-name-sans-extension cmd)
|
||||
cmd)))
|
||||
|
||||
(defun eshell-completion-help ()
|
||||
(interactive)
|
||||
(if (= (point) eshell-last-output-end)
|
||||
(describe-prefix-bindings)
|
||||
(call-interactively 'pcomplete-help)))
|
||||
|
||||
(defun eshell-complete-parse-arguments ()
|
||||
"Parse the command line arguments for `pcomplete-argument'."
|
||||
(when (and eshell-no-completion-during-jobs
|
||||
(eshell-interactive-process))
|
||||
(insert-and-inherit "\t")
|
||||
(throw 'pcompleted t))
|
||||
(let ((end (point-marker))
|
||||
(begin (save-excursion (eshell-bol) (point)))
|
||||
(posns (list t))
|
||||
args delim)
|
||||
(when (memq this-command '(pcomplete-expand
|
||||
pcomplete-expand-and-complete))
|
||||
(run-hook-with-args 'eshell-expand-input-functions begin end)
|
||||
(if (= begin end)
|
||||
(end-of-line))
|
||||
(setq end (point-marker)))
|
||||
(if (setq delim
|
||||
(catch 'eshell-incomplete
|
||||
(ignore
|
||||
(setq args (eshell-parse-arguments begin end)))))
|
||||
(cond ((memq (car delim) '(?\{ ?\<))
|
||||
(setq begin (1+ (cadr delim))
|
||||
args (eshell-parse-arguments begin end)))
|
||||
((eq (car delim) ?\()
|
||||
(lisp-complete-symbol)
|
||||
(throw 'pcompleted t))
|
||||
(t
|
||||
(insert-and-inherit "\t")
|
||||
(throw 'pcompleted t))))
|
||||
(when (get-text-property (1- end) 'comment)
|
||||
(insert-and-inherit "\t")
|
||||
(throw 'pcompleted t))
|
||||
(let ((pos begin))
|
||||
(while (< pos end)
|
||||
(if (get-text-property pos 'arg-begin)
|
||||
(nconc posns (list pos)))
|
||||
(setq pos (1+ pos))))
|
||||
(setq posns (cdr posns))
|
||||
(assert (= (length args) (length posns)))
|
||||
(let ((a args)
|
||||
(i 0)
|
||||
l final)
|
||||
(while a
|
||||
(if (and (consp (car a))
|
||||
(eq (caar a) 'eshell-operator))
|
||||
(setq l i))
|
||||
(setq a (cdr a) i (1+ i)))
|
||||
(and l
|
||||
(setq args (nthcdr (1+ l) args)
|
||||
posns (nthcdr (1+ l) posns))))
|
||||
(assert (= (length args) (length posns)))
|
||||
(when (and args (eq (char-syntax (char-before end)) ? ))
|
||||
(nconc args (list ""))
|
||||
(nconc posns (list (point))))
|
||||
(cons (mapcar
|
||||
(function
|
||||
(lambda (arg)
|
||||
(let ((val
|
||||
(if (listp arg)
|
||||
(let ((result
|
||||
(eshell-do-eval
|
||||
(list 'eshell-commands arg) t)))
|
||||
(assert (eq (car result) 'quote))
|
||||
(cadr result))
|
||||
arg)))
|
||||
(if (numberp val)
|
||||
(setq val (number-to-string val)))
|
||||
(or val ""))))
|
||||
args)
|
||||
posns)))
|
||||
|
||||
(defun eshell-complete-commands-list ()
|
||||
"Generate list of applicable, visible commands."
|
||||
(let ((filename (pcomplete-arg)) glob-name)
|
||||
(if (file-name-directory filename)
|
||||
(pcomplete-executables)
|
||||
(if (and (> (length filename) 0)
|
||||
(eq (aref filename 0) ?*))
|
||||
(setq filename (substring filename 1)
|
||||
pcomplete-stub filename
|
||||
glob-name t))
|
||||
(let* ((paths (split-string (getenv "PATH") path-separator))
|
||||
(cwd (file-name-as-directory
|
||||
(expand-file-name default-directory)))
|
||||
(path "") (comps-in-path ())
|
||||
(file "") (filepath "") (completions ()))
|
||||
;; Go thru each path in the search path, finding completions.
|
||||
(while paths
|
||||
(setq path (file-name-as-directory
|
||||
(expand-file-name (or (car paths) ".")))
|
||||
comps-in-path
|
||||
(and (file-accessible-directory-p path)
|
||||
(file-name-all-completions filename path)))
|
||||
;; Go thru each completion found, to see whether it should
|
||||
;; be used.
|
||||
(while comps-in-path
|
||||
(setq file (car comps-in-path)
|
||||
filepath (concat path file))
|
||||
(if (and (not (member file completions)) ;
|
||||
(or (string-equal path cwd)
|
||||
(not (file-directory-p filepath)))
|
||||
(file-executable-p filepath))
|
||||
(setq completions (cons file completions)))
|
||||
(setq comps-in-path (cdr comps-in-path)))
|
||||
(setq paths (cdr paths)))
|
||||
;; Add aliases which are currently visible, and Lisp functions.
|
||||
(pcomplete-uniqify-list
|
||||
(if glob-name
|
||||
completions
|
||||
(setq completions
|
||||
(append (and (eshell-using-module 'eshell-alias)
|
||||
(funcall (symbol-function 'eshell-alias-completions)
|
||||
filename))
|
||||
(eshell-winnow-list
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (name)
|
||||
(substring name 7)))
|
||||
(all-completions (concat "eshell/" filename)
|
||||
obarray 'functionp))
|
||||
nil '(eshell-find-alias-function))
|
||||
completions))
|
||||
(append (and (or eshell-show-lisp-completions
|
||||
(and eshell-show-lisp-alternatives
|
||||
(null completions)))
|
||||
(all-completions filename obarray 'functionp))
|
||||
completions)))))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-cmpl.el ends here
|
563
lisp/eshell/em-dirs.el
Normal file
563
lisp/eshell/em-dirs.el
Normal file
@ -0,0 +1,563 @@
|
||||
;;; em-dirs --- directory navigation commands
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-dirs)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-dirs nil
|
||||
"Directory navigation involves changing directories, examining the
|
||||
current directory, maintaining a directory stack, and also keeping
|
||||
track of a history of the last directory locations the user was in.
|
||||
Emacs does provide standard Lisp definitions of `pwd' and `cd', but
|
||||
they lack somewhat in feel from the typical shell equivalents."
|
||||
:tag "Directory navigation"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; The only special feature that Eshell offers in the last-dir-ring.
|
||||
;; To view the ring, enter:
|
||||
;;
|
||||
;; cd =
|
||||
;;
|
||||
;; Changing to an index within the ring is done using:
|
||||
;;
|
||||
;; cd - ; same as cd -0
|
||||
;; cd -4
|
||||
;;
|
||||
;; Or, it is possible to change the first member in the ring which
|
||||
;; matches a regexp:
|
||||
;;
|
||||
;; cd =bcc ; change to the last directory visited containing "bcc"
|
||||
;;
|
||||
;; This ring is maintained automatically, and is persisted across
|
||||
;; Eshell sessions. It is a separate mechanism from `pushd' and
|
||||
;; `popd', and the two may be used at the same time.
|
||||
|
||||
(require 'ring)
|
||||
(require 'esh-opt)
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-dirs-load-hook '(eshell-dirs-initialize)
|
||||
"*A hook that gets run when `eshell-dirs' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-pwd-convert-function (if (eshell-under-windows-p)
|
||||
'expand-file-name
|
||||
'identity)
|
||||
"*The function used to normalize the value of Eshell's `pwd'.
|
||||
The value returned by `pwd' is also used when recording the
|
||||
last-visited directory in the last-dir-ring, so it will affect the
|
||||
form of the list used by 'cd ='."
|
||||
:type '(radio (function-item file-truename)
|
||||
(function-item expand-file-name)
|
||||
(function-item identity)
|
||||
(function :tag "Other"))
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-ask-to-save-last-dir 'always
|
||||
"*Determine if the last-dir-ring should be automatically saved.
|
||||
The last-dir-ring is always preserved when exiting an Eshell buffer.
|
||||
However, when Emacs is being shut down, this variable determines
|
||||
whether to prompt the user, or just save the ring.
|
||||
If set to nil, it means never ask whether to save the last-dir-ring.
|
||||
If set to t, always ask if any Eshell buffers are open at exit time.
|
||||
If set to `always', the list-dir-ring will always be saved, silently."
|
||||
:type '(choice (const :tag "Never" nil)
|
||||
(const :tag "Ask" t)
|
||||
(const :tag "Always save" always))
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-cd-shows-directory nil
|
||||
"*If non-nil, using `cd' will report the directory it changes to."
|
||||
:type 'boolean
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-cd-on-directory t
|
||||
"*If non-nil, do a cd if a directory is in command position."
|
||||
:type 'boolean
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-directory-change-hook nil
|
||||
"*A hook to run when the current directory changes."
|
||||
:type 'hook
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-list-files-after-cd nil
|
||||
"*If non-nil, call \"ls\" with any remaining args after doing a cd.
|
||||
This is provided for convenience, since the same effect is easily
|
||||
achieved by adding a function to `eshell-directory-change-hook' that
|
||||
calls \"ls\" and references `eshell-last-arguments'."
|
||||
:type 'boolean
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-pushd-tohome nil
|
||||
"*If non-nil, make pushd with no arg behave as 'pushd ~' (like `cd').
|
||||
This mirrors the optional behavior of tcsh."
|
||||
:type 'boolean
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-pushd-dextract nil
|
||||
"*If non-nil, make \"pushd +n\" pop the nth dir to the stack top.
|
||||
This mirrors the optional behavior of tcsh."
|
||||
:type 'boolean
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-pushd-dunique nil
|
||||
"*If non-nil, make pushd only add unique directories to the stack.
|
||||
This mirrors the optional behavior of tcsh."
|
||||
:type 'boolean
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-dirtrack-verbose t
|
||||
"*If non-nil, show the directory stack following directory change.
|
||||
This is effective only if directory tracking is enabled."
|
||||
:type 'boolean
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-last-dir-ring-file-name
|
||||
(concat eshell-directory-name "lastdir")
|
||||
"*If non-nil, name of the file to read/write the last-dir-ring.
|
||||
See also `eshell-read-last-dir-ring' and `eshell-write-last-dir-ring'.
|
||||
If it is nil, the last-dir-ring will not be written to disk."
|
||||
:type 'file
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-last-dir-ring-size 32
|
||||
"*If non-nil, the size of the directory history ring.
|
||||
This ring is added to every time `cd' or `pushd' is used. It simply
|
||||
stores the most recent directory locations Eshell has been in. To
|
||||
return to the most recent entry, use 'cd -' (equivalent to 'cd -0').
|
||||
To return to an older entry, use 'cd -N', where N is an integer less
|
||||
than `eshell-last-dir-ring-size'. To return to the last directory
|
||||
matching a particular regexp, use 'cd =REGEXP'. To display the
|
||||
directory history list, use 'cd ='.
|
||||
|
||||
This mechanism is very similar to that provided by `pushd', except
|
||||
it's far more automatic. `pushd' allows the user to decide which
|
||||
directories gets pushed, and its size is unlimited.
|
||||
|
||||
`eshell-last-dir-ring' is meant for users who don't use `pushd'
|
||||
explicity very much, but every once in a while would like to return to
|
||||
a previously visited directory without having to type in the whole
|
||||
thing again."
|
||||
:type 'integer
|
||||
:group 'eshell-dirs)
|
||||
|
||||
(defcustom eshell-last-dir-unique t
|
||||
"*If non-nil, `eshell-last-dir-ring' contains only unique entries."
|
||||
:type 'boolean
|
||||
:group 'eshell-dirs)
|
||||
|
||||
;;; Internal Variables:
|
||||
|
||||
(defvar eshell-dirstack nil
|
||||
"List of directories saved by pushd in the Eshell buffer.
|
||||
Thus, this does not include the current directory.")
|
||||
|
||||
(defvar eshell-last-dir-ring nil
|
||||
"The last directory that eshell was in.")
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-dirs-initialize ()
|
||||
"Initialize the builtin functions for Eshell."
|
||||
(make-local-variable 'eshell-variable-aliases-list)
|
||||
(setq eshell-variable-aliases-list
|
||||
(append
|
||||
eshell-variable-aliases-list
|
||||
'(("-" (lambda (indices)
|
||||
(if (not indices)
|
||||
(unless (ring-empty-p eshell-last-dir-ring)
|
||||
(expand-file-name
|
||||
(ring-ref eshell-last-dir-ring 0)))
|
||||
(expand-file-name
|
||||
(eshell-apply-indices eshell-last-dir-ring indices)))))
|
||||
("+" "PWD")
|
||||
("PWD" (lambda (indices)
|
||||
(expand-file-name (eshell/pwd))) t)
|
||||
("OLDPWD" (lambda (indices)
|
||||
(unless (ring-empty-p eshell-last-dir-ring)
|
||||
(expand-file-name
|
||||
(ring-ref eshell-last-dir-ring 0)))) t))))
|
||||
|
||||
(when eshell-cd-on-directory
|
||||
(make-local-variable 'eshell-interpreter-alist)
|
||||
(setq eshell-interpreter-alist
|
||||
(cons (cons 'eshell-lone-directory-p
|
||||
'eshell-dirs-substitute-cd)
|
||||
eshell-interpreter-alist)))
|
||||
|
||||
(make-local-hook 'eshell-parse-argument-hook)
|
||||
(add-hook 'eshell-parse-argument-hook
|
||||
'eshell-parse-user-reference nil t)
|
||||
(if (eshell-under-windows-p)
|
||||
(add-hook 'eshell-parse-argument-hook
|
||||
'eshell-parse-drive-letter nil t))
|
||||
|
||||
(when (eshell-using-module 'eshell-cmpl)
|
||||
(make-local-hook 'pcomplete-try-first-hook)
|
||||
(add-hook 'pcomplete-try-first-hook
|
||||
'eshell-complete-user-reference nil t))
|
||||
|
||||
(make-local-variable 'eshell-dirstack)
|
||||
(make-local-variable 'eshell-last-dir-ring)
|
||||
|
||||
(if eshell-last-dir-ring-file-name
|
||||
(eshell-read-last-dir-ring))
|
||||
(unless eshell-last-dir-ring
|
||||
(setq eshell-last-dir-ring (make-ring eshell-last-dir-ring-size)))
|
||||
|
||||
(make-local-hook 'eshell-exit-hook)
|
||||
(add-hook 'eshell-exit-hook 'eshell-write-last-dir-ring nil t)
|
||||
|
||||
(add-hook 'kill-emacs-hook 'eshell-save-some-last-dir))
|
||||
|
||||
(defun eshell-save-some-last-dir ()
|
||||
"Save the list-dir-ring for any open Eshell buffers."
|
||||
(eshell-for buf (buffer-list)
|
||||
(if (buffer-live-p buf)
|
||||
(with-current-buffer buf
|
||||
(if (and eshell-mode
|
||||
eshell-ask-to-save-last-dir
|
||||
(or (eq eshell-ask-to-save-last-dir 'always)
|
||||
(y-or-n-p
|
||||
(format "Save last dir ring for Eshell buffer `%s'? "
|
||||
(buffer-name buf)))))
|
||||
(eshell-write-last-dir-ring))))))
|
||||
|
||||
(defun eshell-lone-directory-p (file)
|
||||
"Test whether FILE is just a directory name, and not a command name."
|
||||
(and (file-directory-p file)
|
||||
(or (file-name-directory file)
|
||||
(not (eshell-search-path file)))))
|
||||
|
||||
(defun eshell-dirs-substitute-cd (&rest args)
|
||||
"Substitute the given command for a call to `cd' on that name."
|
||||
(if (> (length args) 1)
|
||||
(error "%s: command not found" (car args))
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command "cd" args))))
|
||||
|
||||
(defun eshell-parse-user-reference ()
|
||||
"An argument beginning with ~ is a filename to be expanded."
|
||||
(when (and (not eshell-current-argument)
|
||||
(eq (char-after) ?~))
|
||||
(add-to-list 'eshell-current-modifiers 'expand-file-name)
|
||||
(forward-char)
|
||||
(char-to-string (char-before))))
|
||||
|
||||
(defun eshell-parse-drive-letter ()
|
||||
"An argument beginning X:[^/] is a drive letter reference."
|
||||
(when (and (not eshell-current-argument)
|
||||
(looking-at "\\([A-Za-z]:\\)\\([^/\\\\]\\|\\'\\)"))
|
||||
(goto-char (match-end 1))
|
||||
(let* ((letter (match-string 1))
|
||||
(regexp (concat "\\`" letter))
|
||||
(path (eshell-find-previous-directory regexp)))
|
||||
(concat (or path letter)
|
||||
(char-to-string directory-sep-char)))))
|
||||
|
||||
(defun eshell-complete-user-reference ()
|
||||
"If there is a user reference, complete it."
|
||||
(let ((arg (pcomplete-actual-arg)))
|
||||
(when (string-match "\\`~[a-z]*\\'" arg)
|
||||
(setq pcomplete-stub (substring arg 1)
|
||||
pcomplete-last-completion-raw t)
|
||||
(throw 'pcomplete-completions
|
||||
(progn
|
||||
(eshell-read-user-names)
|
||||
(pcomplete-uniqify-list
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (user)
|
||||
(file-name-as-directory (cdr user))))
|
||||
eshell-user-names)))))))
|
||||
|
||||
(defun eshell/pwd (&rest args) ; ignored
|
||||
"Change output from `pwd` to be cleaner."
|
||||
(let* ((path default-directory)
|
||||
(len (length path)))
|
||||
(if (and (> len 1)
|
||||
(eq (aref path (1- len)) directory-sep-char)
|
||||
(not (and (eshell-under-windows-p)
|
||||
(string-match "\\`[A-Za-z]:[\\\\/]\\'" path))))
|
||||
(setq path (substring path 0 (1- (length path)))))
|
||||
(if eshell-pwd-convert-function
|
||||
(setq path (funcall eshell-pwd-convert-function path)))
|
||||
path))
|
||||
|
||||
(defun eshell-expand-multiple-dots (path)
|
||||
"Convert '...' to '../..', '....' to '../../..', etc..
|
||||
|
||||
With the following piece of advice, you can make this functionality
|
||||
available in most of Emacs, with the exception of filename completion
|
||||
in the minibuffer:
|
||||
|
||||
(defadvice expand-file-name
|
||||
(before translate-multiple-dots
|
||||
(filename &optional directory) activate)
|
||||
(setq filename (eshell-expand-multiple-dots filename)))"
|
||||
(while (string-match "\\.\\.\\(\\.+\\)" path)
|
||||
(let* ((extra-dots (match-string 1 path))
|
||||
(len (length extra-dots))
|
||||
replace-text)
|
||||
(while (> len 0)
|
||||
(setq replace-text
|
||||
(concat replace-text
|
||||
(char-to-string directory-sep-char) "..")
|
||||
len (1- len)))
|
||||
(setq path
|
||||
(replace-match replace-text t t path 1))))
|
||||
path)
|
||||
|
||||
(defun eshell-find-previous-directory (regexp)
|
||||
"Find the most recent last-dir matching REGEXP."
|
||||
(let ((index 0)
|
||||
(len (ring-length eshell-last-dir-ring))
|
||||
oldpath)
|
||||
(if (> (length regexp) 0)
|
||||
(while (< index len)
|
||||
(setq oldpath (ring-ref eshell-last-dir-ring index))
|
||||
(if (string-match regexp oldpath)
|
||||
(setq index len)
|
||||
(setq oldpath nil
|
||||
index (1+ index)))))
|
||||
oldpath))
|
||||
|
||||
(eval-when-compile
|
||||
(defvar dired-directory))
|
||||
|
||||
(defun eshell/cd (&rest args) ; all but first ignored
|
||||
"Alias to extend the behavior of `cd'."
|
||||
(let ((path (car args))
|
||||
(subpath (car (cdr args)))
|
||||
handled)
|
||||
(if (numberp path)
|
||||
(setq path (number-to-string path)))
|
||||
(if (numberp subpath)
|
||||
(setq subpath (number-to-string subpath)))
|
||||
(cond
|
||||
(subpath
|
||||
(let ((curdir (eshell/pwd)))
|
||||
(if (string-match path curdir)
|
||||
(setq path (replace-match subpath nil nil curdir))
|
||||
(error "Path substring '%s' not found" path))))
|
||||
((and path (string-match "^-\\([0-9]*\\)$" path))
|
||||
(let ((index (match-string 1 path)))
|
||||
(setq path
|
||||
(ring-remove eshell-last-dir-ring
|
||||
(if index
|
||||
(string-to-int index)
|
||||
0)))))
|
||||
((and path (string-match "^=\\(.*\\)$" path))
|
||||
(let ((oldpath (eshell-find-previous-directory
|
||||
(match-string 1 path))))
|
||||
(if oldpath
|
||||
(setq path oldpath)
|
||||
(let ((len (ring-length eshell-last-dir-ring))
|
||||
(index 0))
|
||||
(if (= len 0)
|
||||
(error "Directory ring empty"))
|
||||
(while (< index len)
|
||||
(eshell-printn
|
||||
(concat (number-to-string index) ": "
|
||||
(ring-ref eshell-last-dir-ring index)))
|
||||
(setq index (1+ index)))
|
||||
(setq handled t)))))
|
||||
(path
|
||||
(setq path (eshell-expand-multiple-dots path))))
|
||||
(unless handled
|
||||
(setq dired-directory (or path "~"))
|
||||
(let ((curdir (eshell/pwd)))
|
||||
(unless (equal curdir dired-directory)
|
||||
(eshell-add-to-dir-ring curdir))
|
||||
(let ((result (cd dired-directory)))
|
||||
(and eshell-cd-shows-directory
|
||||
(eshell-printn result)))
|
||||
(run-hooks 'eshell-directory-change-hook)
|
||||
(if eshell-list-files-after-cd
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command "ls" (cdr args))))
|
||||
nil))))
|
||||
|
||||
(defun eshell-add-to-dir-ring (path)
|
||||
"Add PATH to the last-dir-ring, if applicable."
|
||||
(unless (and (not (ring-empty-p eshell-last-dir-ring))
|
||||
(equal path (ring-ref eshell-last-dir-ring 0)))
|
||||
(if eshell-last-dir-unique
|
||||
(let ((index 0)
|
||||
(len (ring-length eshell-last-dir-ring)))
|
||||
(while (< index len)
|
||||
(if (equal (ring-ref eshell-last-dir-ring index) path)
|
||||
(ring-remove eshell-last-dir-ring index)
|
||||
(setq index (1+ index))))))
|
||||
(ring-insert eshell-last-dir-ring path)))
|
||||
|
||||
;;; pushd [+n | dir]
|
||||
(defun eshell/pushd (&rest args) ; all but first ignored
|
||||
"Implementation of pushd in Lisp."
|
||||
(let ((path (car args)))
|
||||
(cond
|
||||
((null path)
|
||||
;; no arg -- swap pwd and car of stack unless eshell-pushd-tohome
|
||||
(cond (eshell-pushd-tohome
|
||||
(eshell/pushd "~"))
|
||||
(eshell-dirstack
|
||||
(let ((old (eshell/pwd)))
|
||||
(eshell/cd (car eshell-dirstack))
|
||||
(setq eshell-dirstack (cons old (cdr eshell-dirstack)))
|
||||
(eshell/dirs t)))
|
||||
(t
|
||||
(error "pushd: No other directory"))))
|
||||
((string-match "^\\+\\([0-9]\\)" path)
|
||||
;; pushd +n
|
||||
(setq path (string-to-number (match-string 1 path)))
|
||||
(cond ((> path (length eshell-dirstack))
|
||||
(error "Directory stack not that deep"))
|
||||
((= path 0)
|
||||
(error "Couldn't cd"))
|
||||
(eshell-pushd-dextract
|
||||
(let ((dir (nth (1- path) eshell-dirstack)))
|
||||
(eshell/popd path)
|
||||
(eshell/pushd (eshell/pwd))
|
||||
(eshell/cd dir)
|
||||
(eshell/dirs t)))
|
||||
(t
|
||||
(let* ((ds (cons (eshell/pwd) eshell-dirstack))
|
||||
(dslen (length ds))
|
||||
(front (nthcdr path ds))
|
||||
(back (nreverse (nthcdr (- dslen path) (reverse ds))))
|
||||
(new-ds (append front back)))
|
||||
(eshell/cd (car new-ds))
|
||||
(setq eshell-dirstack (cdr new-ds))
|
||||
(eshell/dirs t)))))
|
||||
(t
|
||||
;; pushd <dir>
|
||||
(let ((old-wd (eshell/pwd)))
|
||||
(eshell/cd path)
|
||||
(if (or (null eshell-pushd-dunique)
|
||||
(not (member old-wd eshell-dirstack)))
|
||||
(setq eshell-dirstack (cons old-wd eshell-dirstack)))
|
||||
(eshell/dirs t)))))
|
||||
nil)
|
||||
|
||||
;;; popd [+n]
|
||||
(defun eshell/popd (&rest args)
|
||||
"Implementation of popd in Lisp."
|
||||
(let ((ref (or (car args) "+0")))
|
||||
(unless (and (stringp ref)
|
||||
(string-match "\\`\\([+-][0-9]+\\)\\'" ref))
|
||||
(error "popd: bad arg `%s'" ref))
|
||||
(setq ref (string-to-number (match-string 1 ref)))
|
||||
(cond ((= ref 0)
|
||||
(unless eshell-dirstack
|
||||
(error "popd: Directory stack empty"))
|
||||
(eshell/cd (car eshell-dirstack))
|
||||
(setq eshell-dirstack (cdr eshell-dirstack))
|
||||
(eshell/dirs t))
|
||||
((<= (abs ref) (length eshell-dirstack))
|
||||
(let* ((ds (cons nil eshell-dirstack))
|
||||
(cell (nthcdr (if (> ref 0)
|
||||
(1- ref)
|
||||
(+ (length eshell-dirstack) ref)) ds))
|
||||
(dir (cadr cell)))
|
||||
(eshell/cd dir)
|
||||
(setcdr cell (cdr (cdr cell)))
|
||||
(setq eshell-dirstack (cdr ds))
|
||||
(eshell/dirs t)))
|
||||
(t
|
||||
(error "Couldn't popd"))))
|
||||
nil)
|
||||
|
||||
(defun eshell/dirs (&optional if-verbose)
|
||||
"Implementation of dirs in Lisp."
|
||||
(when (or (not if-verbose) eshell-dirtrack-verbose)
|
||||
(let* ((msg "")
|
||||
(ds (cons (eshell/pwd) eshell-dirstack))
|
||||
(home (expand-file-name "~/"))
|
||||
(homelen (length home)))
|
||||
(while ds
|
||||
(let ((dir (car ds)))
|
||||
(and (>= (length dir) homelen)
|
||||
(string= home (substring dir 0 homelen))
|
||||
(setq dir (concat "~/" (substring dir homelen))))
|
||||
(setq msg (concat msg (directory-file-name dir) " "))
|
||||
(setq ds (cdr ds))))
|
||||
msg)))
|
||||
|
||||
(defun eshell-read-last-dir-ring ()
|
||||
"Sets the buffer's `eshell-last-dir-ring' from a history file."
|
||||
(let ((file eshell-last-dir-ring-file-name))
|
||||
(cond
|
||||
((or (null file)
|
||||
(equal file "")
|
||||
(not (file-readable-p file)))
|
||||
nil)
|
||||
(t
|
||||
(let* ((count 0)
|
||||
(size eshell-last-dir-ring-size)
|
||||
(ring (make-ring size)))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
;; Save restriction in case file is already visited...
|
||||
;; Watch for those date stamps in history files!
|
||||
(goto-char (point-max))
|
||||
(while (and (< count size)
|
||||
(re-search-backward "^\\([^\n].*\\)$" nil t))
|
||||
(ring-insert-at-beginning ring (match-string 1))
|
||||
(setq count (1+ count)))
|
||||
;; never allow the top element to equal the current
|
||||
;; directory
|
||||
(while (and (not (ring-empty-p ring))
|
||||
(equal (ring-ref ring 0) (eshell/pwd)))
|
||||
(ring-remove ring 0)))
|
||||
(setq eshell-last-dir-ring ring))))))
|
||||
|
||||
(defun eshell-write-last-dir-ring ()
|
||||
"Writes the buffer's `eshell-last-dir-ring' to a history file."
|
||||
(let ((file eshell-last-dir-ring-file-name))
|
||||
(cond
|
||||
((or (null file)
|
||||
(equal file "")
|
||||
(null eshell-last-dir-ring)
|
||||
(ring-empty-p eshell-last-dir-ring))
|
||||
nil)
|
||||
((not (file-writable-p file))
|
||||
(message "Cannot write last-dir-ring file %s" file))
|
||||
(t
|
||||
(let* ((ring eshell-last-dir-ring)
|
||||
(index (ring-length ring)))
|
||||
(with-temp-buffer
|
||||
(while (> index 0)
|
||||
(setq index (1- index))
|
||||
(insert (ring-ref ring index) ?\n))
|
||||
(insert (eshell/pwd) ?\n)
|
||||
(eshell-with-private-file-modes
|
||||
(write-region (point-min) (point-max) file nil
|
||||
'no-message))))))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-dirs.el ends here
|
357
lisp/eshell/em-glob.el
Normal file
357
lisp/eshell/em-glob.el
Normal file
@ -0,0 +1,357 @@
|
||||
;;; em-glob --- extended file name globbing
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-glob)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-glob nil
|
||||
"This module provides extended globbing syntax, similar what is used
|
||||
by zsh for filename generation."
|
||||
:tag "Extended filename globbing"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; The globbing code used by Eshell closely follows the syntax used by
|
||||
;; zsh. Basically, here is a summary of examples:
|
||||
;;
|
||||
;; echo a* ; anything starting with 'a'
|
||||
;; echo a#b ; zero or more 'a's, then 'b'
|
||||
;; echo a##b ; one or more 'a's, then 'b'
|
||||
;; echo a? ; a followed by any character
|
||||
;; echo a*~ab ; 'a', then anything, but not 'ab'
|
||||
;; echo c*~*~ ; all files beginning with 'c', except backups (*~)
|
||||
;;
|
||||
;; Recursive globbing is also supported:
|
||||
;;
|
||||
;; echo **/*.c ; all '.c' files at or under current directory
|
||||
;; echo ***/*.c ; same as above, but traverse symbolic links
|
||||
;;
|
||||
;; Using argument predication, the recursive globbing syntax is
|
||||
;; sufficient to replace the use of 'find <expr> | xargs <cmd>' in
|
||||
;; most cases. For example, to change the readership of all files
|
||||
;; belonging to 'johnw' in the '/tmp' directory or lower, use:
|
||||
;;
|
||||
;; chmod go-r /tmp/**/*(u'johnw')
|
||||
;;
|
||||
;; The glob above matches all of the files beneath '/tmp' that are
|
||||
;; owned by the user 'johnw'. See [Value modifiers and predicates],
|
||||
;; for more information about argument predication.
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-glob-load-hook '(eshell-glob-initialize)
|
||||
"*A list of functions to run when `eshell-glob' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-glob)
|
||||
|
||||
(defcustom eshell-glob-include-dot-files nil
|
||||
"*If non-nil, glob patterns will match files beginning with a dot."
|
||||
:type 'boolean
|
||||
:group 'eshell-glob)
|
||||
|
||||
(defcustom eshell-glob-include-dot-dot t
|
||||
"*If non-nil, glob patterns that match dots will match . and .."
|
||||
:type 'boolean
|
||||
:group 'eshell-glob)
|
||||
|
||||
(defcustom eshell-glob-case-insensitive (eshell-under-windows-p)
|
||||
"*If non-nil, glob pattern matching will ignore case."
|
||||
:type 'boolean
|
||||
:group 'eshell-glob)
|
||||
|
||||
(defcustom eshell-glob-show-progress t
|
||||
"*If non-nil, display progress messages during a recursive glob."
|
||||
:type 'boolean
|
||||
:group 'eshell-glob)
|
||||
|
||||
(defcustom eshell-error-if-no-glob nil
|
||||
"*If non-nil, it is an error for a glob pattern not to match.
|
||||
This mimcs the behavior of zsh if non-nil, but bash if nil."
|
||||
:type 'boolean
|
||||
:group 'eshell-glob)
|
||||
|
||||
(defcustom eshell-glob-chars-list '(?\] ?\[ ?* ?? ?~ ?\( ?\) ?| ?#)
|
||||
"*List of additional characters used in extended globbing."
|
||||
:type '(repeat character)
|
||||
:group 'eshell-glob)
|
||||
|
||||
(defcustom eshell-glob-translate-alist
|
||||
'((?\] . "]")
|
||||
(?\[ . "[")
|
||||
(?? . ".")
|
||||
(?* . ".*")
|
||||
(?~ . "~")
|
||||
(?\( . "\\(")
|
||||
(?\) . "\\)")
|
||||
(?\| . "\\|")
|
||||
(?# . (lambda (str pos)
|
||||
(if (and (< (1+ pos) (length str))
|
||||
(memq (aref str (1+ pos)) '(?* ?# ?+ ??)))
|
||||
(cons (if (eq (aref str (1+ pos)) ??)
|
||||
"?"
|
||||
(if (eq (aref str (1+ pos)) ?*)
|
||||
"*" "+")) (+ pos 2))
|
||||
(cons "*" (1+ pos))))))
|
||||
"*An alist for translation of extended globbing characters."
|
||||
:type '(repeat (cons character (choice regexp function)))
|
||||
:group 'eshell-glob)
|
||||
|
||||
;;; Internal Variables:
|
||||
|
||||
(defvar eshell-glob-chars-regexp nil)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-glob-initialize ()
|
||||
"Initialize the extended globbing code."
|
||||
;; it's important that `eshell-glob-chars-list' come first
|
||||
(set (make-local-variable 'eshell-special-chars-outside-quoting)
|
||||
(append eshell-glob-chars-list eshell-special-chars-outside-quoting))
|
||||
(set (make-local-variable 'eshell-glob-chars-regexp)
|
||||
(format "[%s]+" (apply 'string eshell-glob-chars-list)))
|
||||
(make-local-hook 'eshell-parse-argument-hook)
|
||||
(add-hook 'eshell-parse-argument-hook 'eshell-parse-glob-chars t t)
|
||||
(make-local-hook 'eshell-pre-rewrite-command-hook)
|
||||
(add-hook 'eshell-pre-rewrite-command-hook
|
||||
'eshell-no-command-globbing nil t))
|
||||
|
||||
(defun eshell-no-command-globbing (terms)
|
||||
"Don't glob the command argument. Reflect this by modifying TERMS."
|
||||
(ignore
|
||||
(when (and (listp (car terms))
|
||||
(eq (caar terms) 'eshell-extended-glob))
|
||||
(setcar terms (cadr (car terms))))))
|
||||
|
||||
(defun eshell-add-glob-modifier ()
|
||||
"Add `eshell-extended-glob' to the argument modifier list."
|
||||
(when (memq 'expand-file-name eshell-current-modifiers)
|
||||
(setq eshell-current-modifiers
|
||||
(delq 'expand-file-name eshell-current-modifiers))
|
||||
;; if this is a glob pattern than needs to be expanded, then it
|
||||
;; will need to expand each member of the resulting glob list
|
||||
(add-to-list 'eshell-current-modifiers
|
||||
'(lambda (list)
|
||||
(if (listp list)
|
||||
(mapcar 'expand-file-name list)
|
||||
(expand-file-name list)))))
|
||||
(add-to-list 'eshell-current-modifiers 'eshell-extended-glob))
|
||||
|
||||
(defun eshell-parse-glob-chars ()
|
||||
"Parse a globbing delimiter.
|
||||
The character is not advanced for ordinary globbing characters, so
|
||||
that other function may have a chance to override the globbing
|
||||
interpretation."
|
||||
(when (memq (char-after) eshell-glob-chars-list)
|
||||
(if (not (memq (char-after) '(?\( ?\[)))
|
||||
(ignore (eshell-add-glob-modifier))
|
||||
(let ((here (point)))
|
||||
(forward-char)
|
||||
(let* ((delim (char-before))
|
||||
(end (eshell-find-delimiter
|
||||
delim (if (eq delim ?\[) ?\] ?\)))))
|
||||
(if (not end)
|
||||
(throw 'eshell-incomplete delim)
|
||||
(if (and (eshell-using-module 'eshell-pred)
|
||||
(eshell-arg-delimiter (1+ end)))
|
||||
(ignore (goto-char here))
|
||||
(eshell-add-glob-modifier)
|
||||
(prog1
|
||||
(buffer-substring-no-properties (1- (point)) (1+ end))
|
||||
(goto-char (1+ end))))))))))
|
||||
|
||||
(defun eshell-glob-regexp (pattern)
|
||||
"Convert glob-pattern PATTERN to a regular expression.
|
||||
The basic syntax is:
|
||||
|
||||
glob regexp meaning
|
||||
---- ------ -------
|
||||
? . matches any single character
|
||||
* .* matches any group of characters (or none)
|
||||
# * matches zero or more occurrences of preceding
|
||||
## + matches one or more occurrences of preceding
|
||||
(x) \(x\) makes 'x' a regular expression group
|
||||
| \| boolean OR within an expression group
|
||||
[a-b] [a-b] matches a character or range
|
||||
[^a] [^a] excludes a character or range
|
||||
|
||||
If any characters in PATTERN have the text property `eshell-escaped'
|
||||
set to true, then these characters will match themselves in the
|
||||
resulting regular expression."
|
||||
(let ((matched-in-pattern 0) ; How much of PATTERN handled
|
||||
regexp)
|
||||
(while (string-match eshell-glob-chars-regexp
|
||||
pattern matched-in-pattern)
|
||||
(let* ((op-begin (match-beginning 0))
|
||||
(op-char (aref pattern op-begin)))
|
||||
(setq regexp
|
||||
(concat regexp
|
||||
(regexp-quote
|
||||
(substring pattern matched-in-pattern op-begin))))
|
||||
(if (get-text-property op-begin 'escaped pattern)
|
||||
(setq regexp (concat regexp
|
||||
(regexp-quote (char-to-string op-char)))
|
||||
matched-in-pattern (1+ op-begin))
|
||||
(let ((xlat (assq op-char eshell-glob-translate-alist)))
|
||||
(if (not xlat)
|
||||
(error "Unrecognized globbing character '%c'" op-char)
|
||||
(if (stringp (cdr xlat))
|
||||
(setq regexp (concat regexp (cdr xlat))
|
||||
matched-in-pattern (1+ op-begin))
|
||||
(let ((result (funcall (cdr xlat) pattern op-begin)))
|
||||
(setq regexp (concat regexp (car result))
|
||||
matched-in-pattern (cdr result)))))))))
|
||||
(concat "\\`"
|
||||
regexp
|
||||
(regexp-quote (substring pattern matched-in-pattern))
|
||||
"\\'")))
|
||||
|
||||
(defun eshell-extended-glob (glob)
|
||||
"Return a list of files generated from GLOB, perhaps looking for DIRS-ONLY.
|
||||
This function almost fully supports zsh style filename generation
|
||||
syntax. Things that are not supported are:
|
||||
|
||||
^foo for matching everything but foo
|
||||
(foo~bar) tilde within a parenthesis group
|
||||
foo<1-10> numeric ranges
|
||||
foo~x(a|b) (a|b) will be interpreted as a predicate/modifier list
|
||||
|
||||
Mainly they are not supported because file matching is done with Emacs
|
||||
regular expressions, and these cannot support the above constructs.
|
||||
|
||||
If this routine fails, it returns nil. Otherwise, it returns a list
|
||||
the form:
|
||||
|
||||
(INCLUDE-REGEXP EXCLUDE-REGEXP (PRED-FUNC-LIST) (MOD-FUNC-LIST))"
|
||||
(let ((paths (eshell-split-path glob))
|
||||
matches message-shown)
|
||||
(unwind-protect
|
||||
(if (and (cdr paths)
|
||||
(file-name-absolute-p (car paths)))
|
||||
(eshell-glob-entries (file-name-as-directory (car paths))
|
||||
(cdr paths))
|
||||
(eshell-glob-entries (file-name-as-directory ".") paths))
|
||||
(if message-shown
|
||||
(message nil)))
|
||||
(or (and matches (nreverse matches))
|
||||
(if eshell-error-if-no-glob
|
||||
(error "No matches found: %s" glob)
|
||||
glob))))
|
||||
|
||||
(eval-when-compile
|
||||
(defvar matches)
|
||||
(defvar message-shown))
|
||||
|
||||
;; jww (1999-11-18): this function assumes that directory-sep-char is
|
||||
;; a forward slash (/)
|
||||
|
||||
(defun eshell-glob-entries (path globs &optional recurse-p)
|
||||
"Glob the entries in PATHS, possibly recursing if RECURSE-P is non-nil."
|
||||
(let* ((entries (ignore-errors
|
||||
(file-name-all-completions "" path)))
|
||||
(case-fold-search eshell-glob-case-insensitive)
|
||||
(glob (car globs))
|
||||
(len (length glob))
|
||||
dirs rdirs
|
||||
incl excl
|
||||
name isdir pathname)
|
||||
(while (cond
|
||||
((and (= len 3) (equal glob "**/"))
|
||||
(setq recurse-p 2
|
||||
globs (cdr globs)
|
||||
glob (car globs)
|
||||
len (length glob)))
|
||||
((and (= len 4) (equal glob "***/"))
|
||||
(setq recurse-p 3
|
||||
globs (cdr globs)
|
||||
glob (car globs)
|
||||
len (length glob)))))
|
||||
(if (and recurse-p (not glob))
|
||||
(error "'**' cannot end a globbing pattern"))
|
||||
(let ((index 1))
|
||||
(setq incl glob)
|
||||
(while (and (eq incl glob)
|
||||
(setq index (string-match "~" glob index)))
|
||||
(if (or (get-text-property index 'escaped glob)
|
||||
(or (= (1+ index) len)))
|
||||
(setq index (1+ index))
|
||||
(setq incl (substring glob 0 index)
|
||||
excl (substring glob (1+ index))))))
|
||||
;; can't use `directory-file-name' because it strips away text
|
||||
;; properties in the string
|
||||
(let ((len (1- (length incl))))
|
||||
(if (eq (aref incl len) directory-sep-char)
|
||||
(setq incl (substring incl 0 len)))
|
||||
(when excl
|
||||
(setq len (1- (length excl)))
|
||||
(if (eq (aref excl len) directory-sep-char)
|
||||
(setq excl (substring excl 0 len)))))
|
||||
(setq incl (eshell-glob-regexp incl)
|
||||
excl (and excl (eshell-glob-regexp excl)))
|
||||
(if (or eshell-glob-include-dot-files
|
||||
(eq (aref glob 0) ?.))
|
||||
(unless (or eshell-glob-include-dot-dot
|
||||
(cdr globs))
|
||||
(setq excl (if excl
|
||||
(concat "\\(\\`\\.\\.?\\'\\|" excl "\\)")
|
||||
"\\`\\.\\.?\\'")))
|
||||
(setq excl (if excl
|
||||
(concat "\\(\\`\\.\\|" excl "\\)")
|
||||
"\\`\\.")))
|
||||
(when (and recurse-p eshell-glob-show-progress)
|
||||
(message "Building file list...%d so far: %s"
|
||||
(length matches) path)
|
||||
(setq message-shown t))
|
||||
(if (equal path "./") (setq path ""))
|
||||
(while entries
|
||||
(setq name (car entries)
|
||||
len (length name)
|
||||
isdir (eq (aref name (1- len)) directory-sep-char))
|
||||
(if (let ((fname (directory-file-name name)))
|
||||
(and (not (and excl (string-match excl fname)))
|
||||
(string-match incl fname)))
|
||||
(if (cdr globs)
|
||||
(if isdir
|
||||
(setq dirs (cons (concat path name) dirs)))
|
||||
(setq matches (cons (concat path name) matches))))
|
||||
(if (and recurse-p isdir
|
||||
(or (> len 3)
|
||||
(not (or (and (= len 2) (equal name "./"))
|
||||
(and (= len 3) (equal name "../")))))
|
||||
(setq pathname (concat path name))
|
||||
(not (and (= recurse-p 2)
|
||||
(file-symlink-p
|
||||
(directory-file-name pathname)))))
|
||||
(setq rdirs (cons pathname rdirs)))
|
||||
(setq entries (cdr entries)))
|
||||
(setq dirs (nreverse dirs)
|
||||
rdirs (nreverse rdirs))
|
||||
(while dirs
|
||||
(eshell-glob-entries (car dirs) (cdr globs))
|
||||
(setq dirs (cdr dirs)))
|
||||
(while rdirs
|
||||
(eshell-glob-entries (car rdirs) globs recurse-p)
|
||||
(setq rdirs (cdr rdirs)))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-glob.el ends here
|
966
lisp/eshell/em-hist.el
Normal file
966
lisp/eshell/em-hist.el
Normal file
@ -0,0 +1,966 @@
|
||||
;;; em-hist --- history list management
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-hist)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-hist nil
|
||||
"This module provides command history management."
|
||||
:tag "History list management"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Eshell's history facility imitates the syntax used by bash
|
||||
;; ([(bash)History Interaction]). Thus:
|
||||
;;
|
||||
;; !ls ; repeat the last command beginning with 'ls'
|
||||
;; !?ls ; repeat the last command containing ls
|
||||
;; echo !ls:2 ; echo the second arg of the last 'ls' command
|
||||
;; !ls<tab> ; complete against all possible words in this
|
||||
;; ; position, by looking at the history list
|
||||
;; !ls<C-c SPC> ; expand any matching history input at point
|
||||
;;
|
||||
;; Also, most of `comint-mode's keybindings are accepted:
|
||||
;;
|
||||
;; M-r ; search backward for a previous command by regexp
|
||||
;; M-s ; search forward for a previous command by regexp
|
||||
;; M-p ; access the last command entered, repeatable
|
||||
;; M-n ; access the first command entered, repeatable
|
||||
;;
|
||||
;; C-c M-r ; using current input, find a matching command thus, with
|
||||
;; ; 'ls' as the current input, it will go back to the same
|
||||
;; ; command that '!ls' would have selected
|
||||
;; C-c M-s ; same, but in reverse order
|
||||
;;
|
||||
;; Note that some of these keybindings are only available if the
|
||||
;; `eshell-rebind' is not in use, in which case M-p does what C-c M-r
|
||||
;; normally would do, and C-p is used instead of M-p. It may seem
|
||||
;; confusing, but the intention is to make the most useful
|
||||
;; functionality the most easily accessible. If `eshell-rebind' is
|
||||
;; not being used, history navigation will use comint's keybindings;
|
||||
;; if it is, history navigation tries to use similar keybindings to
|
||||
;; bash. This is all configurable, of course.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ring)
|
||||
(require 'esh-opt)
|
||||
(require 'em-pred)
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-hist-load-hook '(eshell-hist-initialize)
|
||||
"*A list of functions to call when loading `eshell-hist'."
|
||||
:type 'hook
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-hist-unload-hook
|
||||
(list
|
||||
(function
|
||||
(lambda ()
|
||||
(remove-hook 'kill-emacs-hook 'eshell-save-some-history))))
|
||||
"*A hook that gets run when `eshell-hist' is unloaded."
|
||||
:type 'hook
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-history-file-name
|
||||
(concat eshell-directory-name "history")
|
||||
"*If non-nil, name of the file to read/write input history.
|
||||
See also `eshell-read-history' and `eshell-write-history'.
|
||||
If it is nil, Eshell will use the value of HISTFILE."
|
||||
:type 'file
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-history-size 128
|
||||
"*Size of the input history ring. If nil, use envvar HISTSIZE."
|
||||
:type 'integer
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-hist-ignoredups nil
|
||||
"*If non-nil, don't add input matching the last on the input ring.
|
||||
This mirrors the optional behavior of bash."
|
||||
:type 'boolean
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-ask-to-save-history t
|
||||
"*Determine if history should be automatically saved.
|
||||
History is always preserved after sanely exiting an Eshell buffer.
|
||||
However, when Emacs is being shut down, this variable determines
|
||||
whether to prompt the user.
|
||||
If set to nil, it means never ask whether history should be saved.
|
||||
If set to t, always ask if any Eshell buffers are open at exit time.
|
||||
If set to `always', history will always be saved, silently."
|
||||
:type '(choice (const :tag "Never" nil)
|
||||
(const :tag "Ask" t)
|
||||
(const :tag "Always save" always))
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-input-filter
|
||||
(function
|
||||
(lambda (str)
|
||||
(not (string-match "\\`\\s-*\\'" str))))
|
||||
"*Predicate for filtering additions to input history.
|
||||
Takes one argument, the input. If non-nil, the input may be saved on
|
||||
the input history list. Default is to save anything that isn't all
|
||||
whitespace."
|
||||
:type 'function
|
||||
:group 'eshell-hist)
|
||||
|
||||
(put 'eshell-input-filter 'risky-local-variable t)
|
||||
|
||||
(defcustom eshell-hist-match-partial t
|
||||
"*If non-nil, movement through history is constrained by current input.
|
||||
Otherwise, typing <M-p> and <M-n> will always go to the next history
|
||||
element, regardless of any text on the command line. In that case,
|
||||
<C-c M-r> and <C-c M-s> still offer that functionality."
|
||||
:type 'boolean
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-hist-move-to-end t
|
||||
"*If non-nil, move to the end of the buffer before cycling history."
|
||||
:type 'boolean
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-hist-event-designator
|
||||
"^!\\(!\\|-?[0-9]+\\|\\??[^:^$%*?]+\\??\\|#\\)"
|
||||
"*The regexp used to identifier history event designators."
|
||||
:type 'regexp
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-hist-word-designator
|
||||
"^:?\\([0-9]+\\|[$^%*]\\)?\\(\\*\\|-[0-9]*\\|[$^%*]\\)?"
|
||||
"*The regexp used to identify history word designators."
|
||||
:type 'regexp
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-hist-modifier
|
||||
"^\\(:\\([hretpqx&g]\\|s/\\([^/]*\\)/\\([^/]*\\)/\\)\\)*"
|
||||
"*The regexp used to identity history modifiers."
|
||||
:type 'regexp
|
||||
:group 'eshell-hist)
|
||||
|
||||
(defcustom eshell-hist-rebind-keys-alist
|
||||
'(([(control ?p)] . eshell-previous-input)
|
||||
([(control ?n)] . eshell-next-input)
|
||||
([(control up)] . eshell-previous-input)
|
||||
([(control down)] . eshell-next-input)
|
||||
([(control ?r)] . eshell-isearch-backward)
|
||||
([(control ?s)] . eshell-isearch-forward)
|
||||
([(meta ?r)] . eshell-previous-matching-input)
|
||||
([(meta ?s)] . eshell-next-matching-input)
|
||||
([(meta ?p)] . eshell-previous-matching-input-from-input)
|
||||
([(meta ?n)] . eshell-next-matching-input-from-input)
|
||||
([up] . eshell-previous-matching-input-from-input)
|
||||
([down] . eshell-next-matching-input-from-input))
|
||||
"*History keys to bind differently if point is in input text."
|
||||
:type '(repeat (cons (vector :tag "Keys to bind"
|
||||
(repeat :inline t sexp))
|
||||
(function :tag "Command")))
|
||||
:group 'eshell-hist)
|
||||
|
||||
;;; Internal Variables:
|
||||
|
||||
(defvar eshell-history-ring nil)
|
||||
(defvar eshell-history-index nil)
|
||||
(defvar eshell-matching-input-from-input-string "")
|
||||
(defvar eshell-save-history-index nil)
|
||||
|
||||
(defvar eshell-isearch-map nil)
|
||||
|
||||
(unless eshell-isearch-map
|
||||
(setq eshell-isearch-map (copy-keymap isearch-mode-map))
|
||||
(define-key eshell-isearch-map [(control ?m)] 'eshell-isearch-return)
|
||||
(define-key eshell-isearch-map [return] 'eshell-isearch-return)
|
||||
(define-key eshell-isearch-map [(control ?r)] 'eshell-isearch-repeat-backward)
|
||||
(define-key eshell-isearch-map [(control ?s)] 'eshell-isearch-repeat-forward)
|
||||
(define-key eshell-isearch-map [(control ?g)] 'eshell-isearch-abort)
|
||||
(define-key eshell-isearch-map [backspace] 'eshell-isearch-delete-char)
|
||||
(define-key eshell-isearch-map [delete] 'eshell-isearch-delete-char)
|
||||
(defvar eshell-isearch-cancel-map)
|
||||
(define-prefix-command 'eshell-isearch-cancel-map)
|
||||
(define-key eshell-isearch-map [(control ?c)] 'eshell-isearch-cancel-map)
|
||||
(define-key eshell-isearch-cancel-map [(control ?c)] 'eshell-isearch-cancel))
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-hist-initialize ()
|
||||
"Initialize the history management code for one Eshell buffer."
|
||||
(make-local-hook 'eshell-expand-input-functions)
|
||||
(add-hook 'eshell-expand-input-functions
|
||||
'eshell-expand-history-references nil t)
|
||||
|
||||
(when (eshell-using-module 'eshell-cmpl)
|
||||
(make-local-hook 'pcomplete-try-first-hook)
|
||||
(add-hook 'pcomplete-try-first-hook
|
||||
'eshell-complete-history-reference nil t))
|
||||
|
||||
(if (eshell-using-module 'eshell-rebind)
|
||||
(let ((rebind-alist (symbol-value 'eshell-rebind-keys-alist)))
|
||||
(make-local-variable 'eshell-rebind-keys-alist)
|
||||
(set 'eshell-rebind-keys-alist
|
||||
(append rebind-alist eshell-hist-rebind-keys-alist))
|
||||
(set (make-local-variable 'search-invisible) t)
|
||||
(set (make-local-variable 'search-exit-option) t)
|
||||
(make-local-hook 'isearch-mode-hook)
|
||||
(add-hook 'isearch-mode-hook
|
||||
(function
|
||||
(lambda ()
|
||||
(if (>= (point) eshell-last-output-end)
|
||||
(setq overriding-terminal-local-map
|
||||
eshell-isearch-map)))) nil t)
|
||||
(make-local-hook 'isearch-mode-end-hook)
|
||||
(add-hook 'isearch-mode-end-hook
|
||||
(function
|
||||
(lambda ()
|
||||
(setq overriding-terminal-local-map nil))) nil t))
|
||||
(define-key eshell-mode-map [up] 'eshell-previous-matching-input-from-input)
|
||||
(define-key eshell-mode-map [down] 'eshell-next-matching-input-from-input)
|
||||
(define-key eshell-mode-map [(control up)] 'eshell-previous-input)
|
||||
(define-key eshell-mode-map [(control down)] 'eshell-next-input)
|
||||
(define-key eshell-mode-map [(meta ?r)] 'eshell-previous-matching-input)
|
||||
(define-key eshell-mode-map [(meta ?s)] 'eshell-next-matching-input)
|
||||
(define-key eshell-command-map [(meta ?r)]
|
||||
'eshell-previous-matching-input-from-input)
|
||||
(define-key eshell-command-map [(meta ?s)]
|
||||
'eshell-next-matching-input-from-input)
|
||||
(if eshell-hist-match-partial
|
||||
(progn
|
||||
(define-key eshell-mode-map [(meta ?p)]
|
||||
'eshell-previous-matching-input-from-input)
|
||||
(define-key eshell-mode-map [(meta ?n)]
|
||||
'eshell-next-matching-input-from-input)
|
||||
(define-key eshell-command-map [(meta ?p)] 'eshell-previous-input)
|
||||
(define-key eshell-command-map [(meta ?n)] 'eshell-next-input))
|
||||
(define-key eshell-mode-map [(meta ?p)] 'eshell-previous-input)
|
||||
(define-key eshell-mode-map [(meta ?n)] 'eshell-next-input)
|
||||
(define-key eshell-command-map [(meta ?p)]
|
||||
'eshell-previous-matching-input-from-input)
|
||||
(define-key eshell-command-map [(meta ?n)]
|
||||
'eshell-next-matching-input-from-input)))
|
||||
|
||||
(make-local-variable 'eshell-history-size)
|
||||
(or eshell-history-size
|
||||
(setq eshell-history-size (getenv "HISTSIZE")))
|
||||
|
||||
(make-local-variable 'eshell-history-file-name)
|
||||
(or eshell-history-file-name
|
||||
(setq eshell-history-file-name (getenv "HISTFILE")))
|
||||
|
||||
(make-local-variable 'eshell-history-index)
|
||||
(make-local-variable 'eshell-save-history-index)
|
||||
(make-local-variable 'eshell-history-ring)
|
||||
(if eshell-history-file-name
|
||||
(eshell-read-history nil t))
|
||||
(unless eshell-history-ring
|
||||
(setq eshell-history-ring (make-ring eshell-history-size)))
|
||||
|
||||
(make-local-hook 'eshell-exit-hook)
|
||||
(add-hook 'eshell-exit-hook 'eshell-write-history nil t)
|
||||
|
||||
(add-hook 'kill-emacs-hook 'eshell-save-some-history)
|
||||
|
||||
(make-local-variable 'eshell-input-filter-functions)
|
||||
(add-hook 'eshell-input-filter-functions 'eshell-add-to-history nil t)
|
||||
|
||||
(define-key eshell-command-map [(control ?l)] 'eshell-list-history)
|
||||
(define-key eshell-command-map [(control ?x)] 'eshell-get-next-from-history))
|
||||
|
||||
(defun eshell-save-some-history ()
|
||||
"Save the history for any open Eshell buffers."
|
||||
(eshell-for buf (buffer-list)
|
||||
(if (buffer-live-p buf)
|
||||
(with-current-buffer buf
|
||||
(if (and eshell-mode
|
||||
eshell-history-file-name
|
||||
eshell-ask-to-save-history
|
||||
(or (eq eshell-ask-to-save-history 'always)
|
||||
(y-or-n-p
|
||||
(format "Save input history for Eshell buffer `%s'? "
|
||||
(buffer-name buf)))))
|
||||
(eshell-write-history))))))
|
||||
|
||||
(defun eshell/history (&rest args)
|
||||
"List in help buffer the buffer's input history."
|
||||
(eshell-init-print-buffer)
|
||||
(eshell-eval-using-options
|
||||
"history" args
|
||||
'((?r "read" nil read-history
|
||||
"read from history file to current history list")
|
||||
(?w "write" nil write-history
|
||||
"write current history list to history file")
|
||||
(?a "append" nil append-history
|
||||
"append current history list to history file")
|
||||
(?h "help" nil nil "display this usage message")
|
||||
:usage "[n] [-rwa [filename]]"
|
||||
:post-usage
|
||||
"When Eshell is started, history is read from `eshell-history-file-name'.
|
||||
This is also the location where history info will be saved by this command,
|
||||
unless a different file is specified on the command line.")
|
||||
(and (or (not (ring-p eshell-history-ring))
|
||||
(ring-empty-p eshell-history-ring))
|
||||
(error "No history"))
|
||||
(let (length command file)
|
||||
(when (and args (string-match "^[0-9]+$" (car args)))
|
||||
(setq length (min (eshell-convert (car args))
|
||||
(ring-length eshell-history-ring))
|
||||
args (cdr args)))
|
||||
(and length
|
||||
(or read-history write-history append-history)
|
||||
(error "history: extra arguments"))
|
||||
(when (and args (stringp (car args)))
|
||||
(setq file (car args)
|
||||
args (cdr args)))
|
||||
(cond
|
||||
(read-history (eshell-read-history file))
|
||||
(write-history (eshell-write-history file))
|
||||
(append-history (eshell-write-history file t))
|
||||
(t
|
||||
(let* ((history nil)
|
||||
(index (1- (or length (ring-length eshell-history-ring))))
|
||||
(ref (- (ring-length eshell-history-ring) index)))
|
||||
;; We have to build up a list ourselves from the ring vector.
|
||||
(while (>= index 0)
|
||||
(eshell-buffered-print
|
||||
(format "%5d %s\n" ref (eshell-get-history index)))
|
||||
(setq index (1- index)
|
||||
ref (1+ ref)))))))
|
||||
(eshell-flush)
|
||||
nil))
|
||||
|
||||
(defun eshell-put-history (input &optional ring at-beginning)
|
||||
"Put a new input line into the history ring."
|
||||
(unless ring (setq ring eshell-history-ring))
|
||||
(subst-char-in-string ?\n ?\177 input t)
|
||||
(if at-beginning
|
||||
(ring-insert-at-beginning ring input)
|
||||
(ring-insert ring input)))
|
||||
|
||||
(defun eshell-get-history (index &optional ring)
|
||||
"Get an input line from the history ring."
|
||||
(unless ring (setq ring eshell-history-ring))
|
||||
(let ((input (concat (ring-ref ring index))))
|
||||
(subst-char-in-string ?\177 ?\n input t)
|
||||
input))
|
||||
|
||||
(defun eshell-add-to-history ()
|
||||
"Add INPUT to the history ring.
|
||||
The input is entered into the input history ring, if the value of
|
||||
variable `eshell-input-filter' returns non-nil when called on the
|
||||
input."
|
||||
(when (> (1- eshell-last-input-end) eshell-last-input-start)
|
||||
(let ((input (buffer-substring eshell-last-input-start
|
||||
(1- eshell-last-input-end))))
|
||||
(if (and (funcall eshell-input-filter input)
|
||||
(or (null eshell-hist-ignoredups)
|
||||
(not (ring-p eshell-history-ring))
|
||||
(ring-empty-p eshell-history-ring)
|
||||
(not (string-equal (eshell-get-history 0) input))))
|
||||
(eshell-put-history input))
|
||||
(setq eshell-save-history-index eshell-history-ring)
|
||||
(setq eshell-history-index nil))))
|
||||
|
||||
(defun eshell-read-history (&optional filename silent)
|
||||
"Sets the buffer's `eshell-history-ring' from a history file.
|
||||
The name of the file is given by the variable
|
||||
`eshell-history-file-name'. The history ring is of size
|
||||
`eshell-history-size', regardless of file size. If
|
||||
`eshell-history-file-name' is nil this function does nothing.
|
||||
|
||||
If the optional argument SILENT is non-nil, we say nothing about a
|
||||
failure to read the history file.
|
||||
|
||||
This function is useful for major mode commands and mode hooks.
|
||||
|
||||
The structure of the history file should be one input command per
|
||||
line, with the most recent command last. See also
|
||||
`eshell-hist-ignoredups' and `eshell-write-history'."
|
||||
(let ((file (or filename eshell-history-file-name)))
|
||||
(cond
|
||||
((or (null file)
|
||||
(equal file ""))
|
||||
nil)
|
||||
((not (file-readable-p file))
|
||||
(or silent
|
||||
(message "Cannot read history file %s" file)))
|
||||
(t
|
||||
(let* ((count 0)
|
||||
(size eshell-history-size)
|
||||
(ring (make-ring size))
|
||||
(ignore-dups eshell-hist-ignoredups))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
;; Save restriction in case file is already visited...
|
||||
;; Watch for those date stamps in history files!
|
||||
(goto-char (point-max))
|
||||
(while (and (< count size)
|
||||
(re-search-backward "^[ \t]*\\([^#\n].*\\)[ \t]*$"
|
||||
nil t))
|
||||
(let ((history (match-string 1)))
|
||||
(if (or (null ignore-dups)
|
||||
(ring-empty-p ring)
|
||||
(not (string-equal (ring-ref ring 0) history)))
|
||||
(ring-insert-at-beginning ring history)))
|
||||
(setq count (1+ count))))
|
||||
(setq eshell-history-ring ring
|
||||
eshell-history-index nil))))))
|
||||
|
||||
(defun eshell-write-history (&optional filename append)
|
||||
"Writes the buffer's `eshell-history-ring' to a history file.
|
||||
The name of the file is given by the variable
|
||||
`eshell-history-file-name'. The original contents of the file are
|
||||
lost if `eshell-history-ring' is not empty. If
|
||||
`eshell-history-file-name' is nil this function does nothing.
|
||||
|
||||
Useful within process sentinels.
|
||||
|
||||
See also `eshell-read-history'."
|
||||
(let ((file (or filename eshell-history-file-name)))
|
||||
(cond
|
||||
((or (null file)
|
||||
(equal file "")
|
||||
(null eshell-history-ring)
|
||||
(ring-empty-p eshell-history-ring))
|
||||
nil)
|
||||
((not (file-writable-p file))
|
||||
(message "Cannot write history file %s" file))
|
||||
(t
|
||||
(let* ((ring eshell-history-ring)
|
||||
(index (ring-length ring)))
|
||||
;; Write it all out into a buffer first. Much faster, but
|
||||
;; messier, than writing it one line at a time.
|
||||
(with-temp-buffer
|
||||
(while (> index 0)
|
||||
(setq index (1- index))
|
||||
(insert (ring-ref ring index) ?\n))
|
||||
(eshell-with-private-file-modes
|
||||
(write-region (point-min) (point-max) file append
|
||||
'no-message))))))))
|
||||
|
||||
(defun eshell-list-history ()
|
||||
"List in help buffer the buffer's input history."
|
||||
(interactive)
|
||||
(let (prefix prelen)
|
||||
(save-excursion
|
||||
(if (re-search-backward "!\\(.+\\)" (line-beginning-position) t)
|
||||
(setq prefix (match-string 1)
|
||||
prelen (length prefix))))
|
||||
(if (or (not (ring-p eshell-history-ring))
|
||||
(ring-empty-p eshell-history-ring))
|
||||
(message "No history")
|
||||
(let ((history nil)
|
||||
(history-buffer " *Input History*")
|
||||
(index (1- (ring-length eshell-history-ring)))
|
||||
(conf (current-window-configuration)))
|
||||
;; We have to build up a list ourselves from the ring vector.
|
||||
(while (>= index 0)
|
||||
(let ((hist (eshell-get-history index)))
|
||||
(if (or (not prefix)
|
||||
(and (>= (length hist) prelen)
|
||||
(string= (substring hist 0 prelen) prefix)))
|
||||
(setq history (cons hist history))))
|
||||
(setq index (1- index)))
|
||||
;; Change "completion" to "history reference"
|
||||
;; to make the display accurate.
|
||||
(with-output-to-temp-buffer history-buffer
|
||||
(display-completion-list history)
|
||||
(set-buffer history-buffer)
|
||||
(forward-line 3)
|
||||
(while (search-backward "completion" nil 'move)
|
||||
(replace-match "history reference")))
|
||||
(eshell-redisplay)
|
||||
(message "Hit space to flush")
|
||||
(let ((ch (read-event)))
|
||||
(if (eq ch ?\ )
|
||||
(set-window-configuration conf)
|
||||
(setq unread-command-events (list ch))))))))
|
||||
|
||||
(defun eshell-hist-word-reference (ref)
|
||||
"Return the word designator index referred to by REF."
|
||||
(cond
|
||||
((string-match "^[0-9]+$" ref)
|
||||
(string-to-number ref))
|
||||
((string= "^" ref) 1)
|
||||
((string= "$" ref) nil)
|
||||
((string= "%" ref)
|
||||
(error "`%' history word designator not yet implemented"))))
|
||||
|
||||
(defun eshell-hist-parse-arguments (&optional silent b e)
|
||||
"Parse current command arguments in a history-code-friendly way."
|
||||
(let ((end (or e (point)))
|
||||
(begin (or b (save-excursion (eshell-bol) (point))))
|
||||
(posb (list t))
|
||||
(pose (list t))
|
||||
(textargs (list t))
|
||||
hist args)
|
||||
(unless (catch 'eshell-incomplete
|
||||
(ignore
|
||||
(setq args (eshell-parse-arguments begin end))))
|
||||
(save-excursion
|
||||
(goto-char begin)
|
||||
(while (< (point) end)
|
||||
(if (get-text-property (point) 'arg-begin)
|
||||
(nconc posb (list (point))))
|
||||
(if (get-text-property (point) 'arg-end)
|
||||
(nconc pose
|
||||
(list (if (= (1+ (point)) end)
|
||||
(1+ (point))
|
||||
(point)))))
|
||||
(forward-char))
|
||||
(setq posb (cdr posb)
|
||||
pose (cdr pose))
|
||||
(assert (= (length posb) (length args)))
|
||||
(assert (<= (length posb) (length pose))))
|
||||
(setq hist (buffer-substring-no-properties begin end))
|
||||
(let ((b posb) (e pose))
|
||||
(while b
|
||||
(nconc textargs
|
||||
(list (substring hist (- (car b) begin)
|
||||
(- (car e) begin))))
|
||||
(setq b (cdr b)
|
||||
e (cdr e))))
|
||||
(setq textargs (cdr textargs))
|
||||
(assert (= (length textargs) (length args)))
|
||||
(list textargs posb pose))))
|
||||
|
||||
(defun eshell-expand-history-references (beg end)
|
||||
"Parse and expand any history references in current input."
|
||||
(let ((result (eshell-hist-parse-arguments t beg end)))
|
||||
(when result
|
||||
(let ((textargs (nreverse (nth 0 result)))
|
||||
(posb (nreverse (nth 1 result)))
|
||||
(pose (nreverse (nth 2 result))))
|
||||
(save-excursion
|
||||
(while textargs
|
||||
(let ((str (eshell-history-reference (car textargs))))
|
||||
(unless (eq str (car textargs))
|
||||
(goto-char (car posb))
|
||||
(insert-and-inherit str)
|
||||
(delete-char (- (car pose) (car posb)))))
|
||||
(setq textargs (cdr textargs)
|
||||
posb (cdr posb)
|
||||
pose (cdr pose))))))))
|
||||
|
||||
(defun eshell-complete-history-reference ()
|
||||
"Complete a history reference, by completing the event designator."
|
||||
(let ((arg (pcomplete-actual-arg)))
|
||||
(when (string-match "\\`![^:^$*%]*\\'" arg)
|
||||
(setq pcomplete-stub (substring arg 1)
|
||||
pcomplete-last-completion-raw t)
|
||||
(throw 'pcomplete-completions
|
||||
(let ((history nil)
|
||||
(index (1- (ring-length eshell-history-ring)))
|
||||
(stublen (length pcomplete-stub)))
|
||||
;; We have to build up a list ourselves from the ring
|
||||
;; vector.
|
||||
(while (>= index 0)
|
||||
(let ((hist (eshell-get-history index)))
|
||||
(if (and (>= (length hist) stublen)
|
||||
(string= (substring hist 0 stublen)
|
||||
pcomplete-stub)
|
||||
(string-match "^\\([^:^$*% \t\n]+\\)" hist))
|
||||
(setq history (cons (match-string 1 hist)
|
||||
history))))
|
||||
(setq index (1- index)))
|
||||
(let ((fhist (list t)))
|
||||
;; uniqify the list, but preserve the order
|
||||
(while history
|
||||
(unless (member (car history) fhist)
|
||||
(nconc fhist (list (car history))))
|
||||
(setq history (cdr history)))
|
||||
(cdr fhist)))))))
|
||||
|
||||
(defun eshell-history-reference (reference)
|
||||
"Expand directory stack REFERENCE.
|
||||
The syntax used here was taken from the Bash info manual.
|
||||
Returns the resultant reference, or the same string REFERENCE if none
|
||||
matched."
|
||||
;; `^string1^string2^'
|
||||
;; Quick Substitution. Repeat the last command, replacing
|
||||
;; STRING1 with STRING2. Equivalent to `!!:s/string1/string2/'
|
||||
(if (and (eshell-using-module 'eshell-pred)
|
||||
(string-match "\\^\\([^^]+\\)\\^\\([^^]+\\)\\^?\\s-*$"
|
||||
reference))
|
||||
(setq reference (format "!!:s/%s/%s/"
|
||||
(match-string 1 reference)
|
||||
(match-string 2 reference))))
|
||||
;; `!'
|
||||
;; Start a history substitution, except when followed by a
|
||||
;; space, tab, the end of the line, = or (.
|
||||
(if (not (string-match "^![^ \t\n=\(]" reference))
|
||||
reference
|
||||
(setq eshell-history-index nil)
|
||||
(let ((event (eshell-hist-parse-event-designator reference)))
|
||||
(unless event
|
||||
(error "Could not find history event `%s'" reference))
|
||||
(setq eshell-history-index (car event)
|
||||
reference (substring reference (cdr event))
|
||||
event (eshell-get-history eshell-history-index))
|
||||
(if (not (string-match "^[:^$*%]" reference))
|
||||
event
|
||||
(let ((word (eshell-hist-parse-word-designator
|
||||
event reference)))
|
||||
(unless word
|
||||
(error "Unable to honor word designator `%s'" reference))
|
||||
(unless (string-match "^[:^$*%][[$^*%0-9-]" reference)
|
||||
(setcdr word 0))
|
||||
(setq event (car word)
|
||||
reference (substring reference (cdr word)))
|
||||
(if (not (and (eshell-using-module 'eshell-pred)
|
||||
(string-match "^:" reference)))
|
||||
event
|
||||
(eshell-hist-parse-modifier event reference)))))))
|
||||
|
||||
(defun eshell-hist-parse-event-designator (reference)
|
||||
"Parse a history event designator beginning in REFERENCE."
|
||||
(let* ((index (string-match eshell-hist-event-designator reference))
|
||||
(end (and index (match-end 0))))
|
||||
(unless index
|
||||
(error "Invalid history event designator `%s'" reference))
|
||||
(let* ((event (match-string 1 reference))
|
||||
(pos
|
||||
(cond
|
||||
((string= event "!") (ring-length eshell-history-ring))
|
||||
((string= event "#") (error "!# not yet implemented"))
|
||||
((string-match "^-?[0-9]+$" event)
|
||||
(let ((num (string-to-number event)))
|
||||
(if (>= num 0)
|
||||
(- (ring-length eshell-history-ring) num)
|
||||
(1- (abs num)))))
|
||||
((string-match "^\\(\\??\\)\\([^?]+\\)\\??$" event)
|
||||
(let ((pref (if (> (length (match-string 1 event)) 0)
|
||||
"" "^"))
|
||||
(str (match-string 2 event)))
|
||||
(save-match-data
|
||||
(eshell-previous-matching-input-string-position
|
||||
(concat pref (regexp-quote str)) 1))))
|
||||
(t
|
||||
(error "Failed to parse event designator `%s'" event)))))
|
||||
(and pos (cons pos end)))))
|
||||
|
||||
(defun eshell-hist-parse-word-designator (hist reference)
|
||||
"Parse a history word designator beginning for HIST in REFERENCE."
|
||||
(let* ((index (string-match eshell-hist-word-designator reference))
|
||||
(end (and index (match-end 0))))
|
||||
(unless (memq (aref reference 0) '(?: ?^ ?$ ?* ?%))
|
||||
(error "Invalid history word designator `%s'" reference))
|
||||
(let ((nth (match-string 1 reference))
|
||||
(mth (match-string 2 reference))
|
||||
(here (point))
|
||||
textargs)
|
||||
(insert hist)
|
||||
(setq textargs (car (eshell-hist-parse-arguments nil here (point))))
|
||||
(delete-region here (point))
|
||||
(if (string= nth "*")
|
||||
(if mth
|
||||
(error "Invalid history word designator `%s'"
|
||||
reference)
|
||||
(setq nth 1 mth "-$")))
|
||||
(if (not mth)
|
||||
(if nth
|
||||
(setq mth nth)
|
||||
(setq nth 0 mth "$"))
|
||||
(if (string= mth "-")
|
||||
(setq mth (- (length textargs) 2))
|
||||
(if (string= mth "*")
|
||||
(setq mth "$")
|
||||
(if (not (and (> (length mth) 1)
|
||||
(eq (aref mth 0) ?-)))
|
||||
(error "Invalid history word designator `%s'"
|
||||
reference)
|
||||
(setq mth (substring mth 1))))))
|
||||
(unless (numberp nth)
|
||||
(setq nth (eshell-hist-word-reference nth)))
|
||||
(unless (numberp mth)
|
||||
(setq mth (eshell-hist-word-reference mth)))
|
||||
(cons (mapconcat 'identity (eshell-sublist textargs nth mth) "")
|
||||
end))))
|
||||
|
||||
(defun eshell-hist-parse-modifier (hist reference)
|
||||
"Parse a history modifier beginning for HIST in REFERENCE."
|
||||
(let ((here (point)))
|
||||
(insert reference)
|
||||
(prog1
|
||||
(save-restriction
|
||||
(narrow-to-region here (point))
|
||||
(goto-char (point-min))
|
||||
(let ((modifiers (cdr (eshell-parse-modifiers))))
|
||||
(eshell-for mod modifiers
|
||||
(setq hist (funcall mod hist)))
|
||||
hist))
|
||||
(delete-region here (point)))))
|
||||
|
||||
(defun eshell-get-next-from-history ()
|
||||
"After fetching a line from input history, this fetches the next.
|
||||
In other words, this recalls the input line after the line you
|
||||
recalled last. You can use this to repeat a sequence of input lines."
|
||||
(interactive)
|
||||
(if eshell-save-history-index
|
||||
(progn
|
||||
(setq eshell-history-index (1+ eshell-save-history-index))
|
||||
(eshell-next-input 1))
|
||||
(message "No previous history command")))
|
||||
|
||||
(defun eshell-search-arg (arg)
|
||||
;; First make sure there is a ring and that we are after the process
|
||||
;; mark
|
||||
(if (and eshell-hist-move-to-end
|
||||
(< (point) eshell-last-output-end))
|
||||
(goto-char eshell-last-output-end))
|
||||
(cond ((or (null eshell-history-ring)
|
||||
(ring-empty-p eshell-history-ring))
|
||||
(error "Empty input ring"))
|
||||
((zerop arg)
|
||||
;; arg of zero resets search from beginning, and uses arg of
|
||||
;; 1
|
||||
(setq eshell-history-index nil)
|
||||
1)
|
||||
(t
|
||||
arg)))
|
||||
|
||||
(defun eshell-search-start (arg)
|
||||
"Index to start a directional search, starting at `eshell-history-index'."
|
||||
(if eshell-history-index
|
||||
;; If a search is running, offset by 1 in direction of arg
|
||||
(mod (+ eshell-history-index (if (> arg 0) 1 -1))
|
||||
(ring-length eshell-history-ring))
|
||||
;; For a new search, start from beginning or end, as appropriate
|
||||
(if (>= arg 0)
|
||||
0 ; First elt for forward search
|
||||
;; Last elt for backward search
|
||||
(1- (ring-length eshell-history-ring)))))
|
||||
|
||||
(defun eshell-previous-input-string (arg)
|
||||
"Return the string ARG places along the input ring.
|
||||
Moves relative to `eshell-history-index'."
|
||||
(eshell-get-history (if eshell-history-index
|
||||
(mod (+ arg eshell-history-index)
|
||||
(ring-length eshell-history-ring))
|
||||
arg)))
|
||||
|
||||
(defun eshell-previous-input (arg)
|
||||
"Cycle backwards through input history."
|
||||
(interactive "*p")
|
||||
(eshell-previous-matching-input "." arg))
|
||||
|
||||
(defun eshell-next-input (arg)
|
||||
"Cycle forwards through input history."
|
||||
(interactive "*p")
|
||||
(eshell-previous-input (- arg)))
|
||||
|
||||
(defun eshell-previous-matching-input-string (regexp arg)
|
||||
"Return the string matching REGEXP ARG places along the input ring.
|
||||
Moves relative to `eshell-history-index'."
|
||||
(let* ((pos (eshell-previous-matching-input-string-position regexp arg)))
|
||||
(if pos (eshell-get-history pos))))
|
||||
|
||||
(defun eshell-previous-matching-input-string-position
|
||||
(regexp arg &optional start)
|
||||
"Return the index matching REGEXP ARG places along the input ring.
|
||||
Moves relative to START, or `eshell-history-index'."
|
||||
(if (or (not (ring-p eshell-history-ring))
|
||||
(ring-empty-p eshell-history-ring))
|
||||
(error "No history"))
|
||||
(let* ((len (ring-length eshell-history-ring))
|
||||
(motion (if (> arg 0) 1 -1))
|
||||
(n (mod (- (or start (eshell-search-start arg)) motion) len))
|
||||
(tried-each-ring-item nil)
|
||||
(case-fold-search (eshell-under-windows-p))
|
||||
(prev nil))
|
||||
;; Do the whole search as many times as the argument says.
|
||||
(while (and (/= arg 0) (not tried-each-ring-item))
|
||||
;; Step once.
|
||||
(setq prev n
|
||||
n (mod (+ n motion) len))
|
||||
;; If we haven't reached a match, step some more.
|
||||
(while (and (< n len) (not tried-each-ring-item)
|
||||
(not (string-match regexp (eshell-get-history n))))
|
||||
(setq n (mod (+ n motion) len)
|
||||
;; If we have gone all the way around in this search.
|
||||
tried-each-ring-item (= n prev)))
|
||||
(setq arg (if (> arg 0) (1- arg) (1+ arg))))
|
||||
;; Now that we know which ring element to use, if we found it,
|
||||
;; return that.
|
||||
(if (string-match regexp (eshell-get-history n))
|
||||
n)))
|
||||
|
||||
(defun eshell-previous-matching-input (regexp arg)
|
||||
"Search backwards through input history for match for REGEXP.
|
||||
\(Previous history elements are earlier commands.)
|
||||
With prefix argument N, search for Nth previous match.
|
||||
If N is negative, find the next or Nth next match."
|
||||
(interactive (eshell-regexp-arg "Previous input matching (regexp): "))
|
||||
(setq arg (eshell-search-arg arg))
|
||||
(let ((pos (eshell-previous-matching-input-string-position regexp arg)))
|
||||
;; Has a match been found?
|
||||
(if (null pos)
|
||||
(error "Not found")
|
||||
(setq eshell-history-index pos)
|
||||
(message "History item: %d" (- (ring-length eshell-history-ring) pos))
|
||||
;; Can't use kill-region as it sets this-command
|
||||
(delete-region (save-excursion (eshell-bol) (point)) (point))
|
||||
(insert-and-inherit (eshell-get-history pos)))))
|
||||
|
||||
(defun eshell-next-matching-input (regexp arg)
|
||||
"Search forwards through input history for match for REGEXP.
|
||||
\(Later history elements are more recent commands.)
|
||||
With prefix argument N, search for Nth following match.
|
||||
If N is negative, find the previous or Nth previous match."
|
||||
(interactive (eshell-regexp-arg "Next input matching (regexp): "))
|
||||
(eshell-previous-matching-input regexp (- arg)))
|
||||
|
||||
(defun eshell-previous-matching-input-from-input (arg)
|
||||
"Search backwards through input history for match for current input.
|
||||
\(Previous history elements are earlier commands.)
|
||||
With prefix argument N, search for Nth previous match.
|
||||
If N is negative, search forwards for the -Nth following match."
|
||||
(interactive "p")
|
||||
(if (not (memq last-command '(eshell-previous-matching-input-from-input
|
||||
eshell-next-matching-input-from-input)))
|
||||
;; Starting a new search
|
||||
(setq eshell-matching-input-from-input-string
|
||||
(buffer-substring (save-excursion (eshell-bol) (point))
|
||||
(point))
|
||||
eshell-history-index nil))
|
||||
(eshell-previous-matching-input
|
||||
(concat "^" (regexp-quote eshell-matching-input-from-input-string))
|
||||
arg))
|
||||
|
||||
(defun eshell-next-matching-input-from-input (arg)
|
||||
"Search forwards through input history for match for current input.
|
||||
\(Following history elements are more recent commands.)
|
||||
With prefix argument N, search for Nth following match.
|
||||
If N is negative, search backwards for the -Nth previous match."
|
||||
(interactive "p")
|
||||
(eshell-previous-matching-input-from-input (- arg)))
|
||||
|
||||
(defun eshell-test-imatch ()
|
||||
"If isearch match good, put point at the beginning and return non-nil."
|
||||
(if (get-text-property (point) 'history)
|
||||
(progn (beginning-of-line) t)
|
||||
(let ((before (point)))
|
||||
(eshell-bol)
|
||||
(if (and (not (bolp))
|
||||
(<= (point) before))
|
||||
t
|
||||
(if isearch-forward
|
||||
(progn
|
||||
(end-of-line)
|
||||
(forward-char))
|
||||
(beginning-of-line)
|
||||
(backward-char))))))
|
||||
|
||||
(defun eshell-return-to-prompt ()
|
||||
"Once a search string matches, insert it at the end and go there."
|
||||
(setq isearch-other-end nil)
|
||||
(let ((found (eshell-test-imatch)) before)
|
||||
(while (and (not found)
|
||||
(setq before
|
||||
(funcall (if isearch-forward
|
||||
're-search-forward
|
||||
're-search-backward)
|
||||
isearch-string nil t)))
|
||||
(setq found (eshell-test-imatch)))
|
||||
(if (not found)
|
||||
(progn
|
||||
(goto-char eshell-last-output-end)
|
||||
(delete-region (point) (point-max)))
|
||||
(setq before (point))
|
||||
(let ((text (buffer-substring-no-properties
|
||||
(point) (line-end-position)))
|
||||
(orig (marker-position eshell-last-output-end)))
|
||||
(goto-char eshell-last-output-end)
|
||||
(delete-region (point) (point-max))
|
||||
(when (and text (> (length text) 0))
|
||||
(subst-char-in-string ?\177 ?\n text t)
|
||||
(insert text)
|
||||
(put-text-property (1- (point)) (point)
|
||||
'last-search-pos before)
|
||||
(set-marker eshell-last-output-end orig)
|
||||
(goto-char eshell-last-output-end))))))
|
||||
|
||||
(defun eshell-prepare-for-search ()
|
||||
"Make sure the old history file is at the beginning of the buffer."
|
||||
(unless (get-text-property (point-min) 'history)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let ((end (copy-marker (point) t)))
|
||||
(insert-file-contents eshell-history-file-name)
|
||||
(set-text-properties (point-min) end
|
||||
'(history t invisible t))))))
|
||||
|
||||
(defun eshell-isearch-backward (&optional invert)
|
||||
"Do incremental regexp search backward through past commands."
|
||||
(interactive)
|
||||
(let ((inhibit-read-only t) end)
|
||||
(eshell-prepare-for-search)
|
||||
(goto-char (point-max))
|
||||
(set-marker eshell-last-output-end (point))
|
||||
(delete-region (point) (point-max)))
|
||||
(isearch-mode invert t 'eshell-return-to-prompt))
|
||||
|
||||
(defun eshell-isearch-repeat-backward (&optional invert)
|
||||
"Do incremental regexp search backward through past commands."
|
||||
(interactive)
|
||||
(let ((old-pos (get-text-property (1- (point-max))
|
||||
'last-search-pos)))
|
||||
(when old-pos
|
||||
(goto-char old-pos)
|
||||
(if invert
|
||||
(end-of-line)
|
||||
(backward-char)))
|
||||
(setq isearch-forward invert)
|
||||
(isearch-search-and-update)))
|
||||
|
||||
(defun eshell-isearch-forward ()
|
||||
"Do incremental regexp search backward through past commands."
|
||||
(interactive)
|
||||
(eshell-isearch-backward t))
|
||||
|
||||
(defun eshell-isearch-repeat-forward ()
|
||||
"Do incremental regexp search backward through past commands."
|
||||
(interactive)
|
||||
(eshell-isearch-repeat-backward t))
|
||||
|
||||
(defun eshell-isearch-cancel ()
|
||||
(interactive)
|
||||
(goto-char eshell-last-output-end)
|
||||
(delete-region (point) (point-max))
|
||||
(call-interactively 'isearch-cancel))
|
||||
|
||||
(defun eshell-isearch-abort ()
|
||||
(interactive)
|
||||
(goto-char eshell-last-output-end)
|
||||
(delete-region (point) (point-max))
|
||||
(call-interactively 'isearch-abort))
|
||||
|
||||
(defun eshell-isearch-delete-char ()
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(isearch-delete-char)))
|
||||
|
||||
(defun eshell-isearch-return ()
|
||||
(interactive)
|
||||
(isearch-done)
|
||||
(eshell-send-input))
|
||||
|
||||
;;; em-hist.el ends here
|
863
lisp/eshell/em-ls.el
Normal file
863
lisp/eshell/em-ls.el
Normal file
@ -0,0 +1,863 @@
|
||||
;;; em-ls --- implementation of ls in Lisp
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-ls)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-ls nil
|
||||
"This module implements the \"ls\" utility fully in Lisp. If it is
|
||||
passed any unrecognized command switches, it will revert to the
|
||||
operating system's version. This version of \"ls\" uses text
|
||||
properties to colorize its output based on the setting of
|
||||
`eshell-ls-use-colors'."
|
||||
:tag "Implementation of `ls' in Lisp"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Most of the command switches recognized by GNU's ls utility are
|
||||
;; supported ([(fileutils)ls invocation]).
|
||||
|
||||
(require 'esh-util)
|
||||
(require 'esh-opt)
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defvar eshell-ls-orig-insert-directory
|
||||
(symbol-function 'insert-directory)
|
||||
"Preserve the original definition of `insert-directory'.")
|
||||
|
||||
(defcustom eshell-ls-unload-hook
|
||||
(list
|
||||
(function
|
||||
(lambda ()
|
||||
(fset 'insert-directory eshell-ls-orig-insert-directory))))
|
||||
"*When unloading `eshell-ls', restore the definition of `insert-directory'."
|
||||
:type 'hook
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defcustom eshell-ls-use-in-dired nil
|
||||
"*If non-nil, use `eshell-ls' to read directories in dired."
|
||||
:set (lambda (symbol value)
|
||||
(if value
|
||||
(unless (and (boundp 'eshell-ls-use-in-dired)
|
||||
eshell-ls-use-in-dired)
|
||||
(fset 'insert-directory 'eshell-ls-insert-directory))
|
||||
(when (and (boundp 'eshell-ls-insert-directory)
|
||||
eshell-ls-use-in-dired)
|
||||
(fset 'insert-directory eshell-ls-orig-insert-directory)))
|
||||
(setq eshell-ls-use-in-dired value))
|
||||
:type 'boolean
|
||||
:require 'em-ls
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defcustom eshell-ls-default-blocksize 1024
|
||||
"*The default blocksize to use when display file sizes with -s."
|
||||
:type 'integer
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defcustom eshell-ls-exclude-regexp "\\`\\."
|
||||
"*Unless -a is specified, files matching this regexp will not be shown."
|
||||
:type 'regexp
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defcustom eshell-ls-use-colors t
|
||||
"*If non-nil, use colors in file listings."
|
||||
:type 'boolean
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-directory-face
|
||||
'((((class color) (background light)) (:foreground "Blue" :bold t))
|
||||
(((class color) (background dark)) (:foreground "SkyBlue" :bold t))
|
||||
(t (:bold t)))
|
||||
"*The face used for highlight directories."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-symlink-face
|
||||
'((((class color) (background light)) (:foreground "Dark Cyan" :bold t))
|
||||
(((class color) (background dark)) (:foreground "Cyan" :bold t)))
|
||||
"*The face used for highlight symbolic links."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-executable-face
|
||||
'((((class color) (background light)) (:foreground "ForestGreen" :bold t))
|
||||
(((class color) (background dark)) (:foreground "Green" :bold t)))
|
||||
"*The face used for highlighting executables (not directories, though)."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-readonly-face
|
||||
'((((class color) (background light)) (:foreground "Brown"))
|
||||
(((class color) (background dark)) (:foreground "Pink")))
|
||||
"*The face used for highlighting read-only files."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-unreadable-face
|
||||
'((((class color) (background light)) (:foreground "Grey30"))
|
||||
(((class color) (background dark)) (:foreground "DarkGrey")))
|
||||
"*The face used for highlighting unreadable files."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-special-face
|
||||
'((((class color) (background light)) (:foreground "Magenta" :bold t))
|
||||
(((class color) (background dark)) (:foreground "Magenta" :bold t)))
|
||||
"*The face used for highlighting non-regular files."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-missing-face
|
||||
'((((class color) (background light)) (:foreground "Red" :bold t))
|
||||
(((class color) (background dark)) (:foreground "Red" :bold t)))
|
||||
"*The face used for highlighting non-existant file names."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defcustom eshell-ls-archive-regexp
|
||||
(concat "\\.\\(t\\(a[rz]\\|gz\\)\\|arj\\|lzh\\|"
|
||||
"zip\\|[zZ]\\|gz\\|bz2\\|deb\\|rpm\\)\\'")
|
||||
"*A regular expression that matches names of file archives.
|
||||
This typically includes both traditional archives and compressed
|
||||
files."
|
||||
:type 'regexp
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-archive-face
|
||||
'((((class color) (background light)) (:foreground "Orchid" :bold t))
|
||||
(((class color) (background dark)) (:foreground "Orchid" :bold t)))
|
||||
"*The face used for highlighting archived and compressed file names."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defcustom eshell-ls-backup-regexp
|
||||
"\\(\\`\\.?#\\|\\(\\.bak\\|~\\)\\'\\)"
|
||||
"*A regular expression that matches names of backup files."
|
||||
:type 'regexp
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-backup-face
|
||||
'((((class color) (background light)) (:foreground "OrangeRed"))
|
||||
(((class color) (background dark)) (:foreground "LightSalmon")))
|
||||
"*The face used for highlighting backup file names."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defcustom eshell-ls-product-regexp
|
||||
"\\.\\(elc\\|o\\(bj\\)?\\|a\\||lib\\|res\\)\\'"
|
||||
"*A regular expression that matches names of product files.
|
||||
Products are files that get generated from a source file, and hence
|
||||
ought to be recreatable if they are deleted."
|
||||
:type 'regexp
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-product-face
|
||||
'((((class color) (background light)) (:foreground "OrangeRed"))
|
||||
(((class color) (background dark)) (:foreground "LightSalmon")))
|
||||
"*The face used for highlighting files that are build products."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defcustom eshell-ls-clutter-regexp
|
||||
"\\(^texput\\.log\\|^core\\)\\'"
|
||||
"*A regular expression that matches names of junk files.
|
||||
These are mainly files that get created for various reasons, but don't
|
||||
really need to stick around for very long."
|
||||
:type 'regexp
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defface eshell-ls-clutter-face
|
||||
'((((class color) (background light)) (:foreground "OrangeRed" :bold t))
|
||||
(((class color) (background dark)) (:foreground "OrangeRed" :bold t)))
|
||||
"*The face used for highlighting junk file names."
|
||||
:group 'eshell-ls)
|
||||
|
||||
(defsubst eshell-ls-filetype-p (attrs type)
|
||||
"Test whether ATTRS specifies a directory."
|
||||
(if (nth 8 attrs)
|
||||
(eq (aref (nth 8 attrs) 0) type)))
|
||||
|
||||
(defmacro eshell-ls-applicable (attrs index func file)
|
||||
"Test whether, for ATTRS, the user UID can do what corresponds to INDEX.
|
||||
This is really just for efficiency, to avoid having to stat the file
|
||||
yet again."
|
||||
`(if (= (user-uid) (nth 2 ,attrs))
|
||||
(not (eq (aref (nth 8 ,attrs) ,index) ?-))
|
||||
(,(eval func) ,file)))
|
||||
|
||||
(defcustom eshell-ls-highlight-alist nil
|
||||
"*This alist correlates test functions to color.
|
||||
The format of the members of this alist is
|
||||
|
||||
(TEST-SEXP . FACE)
|
||||
|
||||
If TEST-SEXP evals to non-nil, that face will be used to highlight the
|
||||
name of the file. The first match wins. `file' and `attrs' are in
|
||||
scope during the evaluation of TEST-SEXP."
|
||||
:type '(repeat (cons function face))
|
||||
:group 'eshell-ls)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-ls-insert-directory
|
||||
(file switches &optional wildcard full-directory-p)
|
||||
"Insert directory listing for FILE, formatted according to SWITCHES.
|
||||
Leaves point after the inserted text.
|
||||
SWITCHES may be a string of options, or a list of strings.
|
||||
Optional third arg WILDCARD means treat FILE as shell wildcard.
|
||||
Optional fourth arg FULL-DIRECTORY-P means file is a directory and
|
||||
switches do not contain `d', so that a full listing is expected.
|
||||
|
||||
This version of the function uses `eshell/ls'. If any of the switches
|
||||
passed are not recognized, the operating system's version will be used
|
||||
instead."
|
||||
(let ((handler (find-file-name-handler file 'insert-directory)))
|
||||
(if handler
|
||||
(funcall handler 'insert-directory file switches
|
||||
wildcard full-directory-p)
|
||||
(if (stringp switches)
|
||||
(setq switches (split-string switches)))
|
||||
(let (eshell-current-handles
|
||||
eshell-current-subjob-p)
|
||||
;; use the fancy highlighting in `eshell-ls' rather than font-lock
|
||||
(when (and eshell-ls-use-colors
|
||||
(featurep 'font-lock))
|
||||
(font-lock-mode -1)
|
||||
(if (boundp 'font-lock-buffers)
|
||||
(set 'font-lock-buffers
|
||||
(delq (current-buffer)
|
||||
(symbol-value 'font-lock-buffers)))))
|
||||
(let ((insert-func 'insert)
|
||||
(error-func 'insert)
|
||||
(flush-func 'ignore))
|
||||
(eshell-do-ls (append switches (list file))))))))
|
||||
|
||||
(defsubst eshell/ls (&rest args)
|
||||
"An alias version of `eshell-do-ls'."
|
||||
(let ((insert-func 'eshell-buffered-print)
|
||||
(error-func 'eshell-error)
|
||||
(flush-func 'eshell-flush))
|
||||
(eshell-do-ls args)))
|
||||
|
||||
(eval-when-compile
|
||||
(defvar block-size)
|
||||
(defvar dereference-links)
|
||||
(defvar dir-literal)
|
||||
(defvar error-func)
|
||||
(defvar flush-func)
|
||||
(defvar human-readable)
|
||||
(defvar ignore-pattern)
|
||||
(defvar insert-func)
|
||||
(defvar listing-style)
|
||||
(defvar numeric-uid-gid)
|
||||
(defvar reverse-list)
|
||||
(defvar show-all)
|
||||
(defvar show-recursive)
|
||||
(defvar show-size)
|
||||
(defvar sort-method))
|
||||
|
||||
(defun eshell-do-ls (&rest args)
|
||||
"Implementation of \"ls\" in Lisp, passing ARGS."
|
||||
(funcall flush-func -1)
|
||||
;; process the command arguments, and begin listing files
|
||||
(eshell-eval-using-options
|
||||
"ls" args
|
||||
`((?a "all" nil show-all
|
||||
"show all files in directory")
|
||||
(?c nil by-ctime sort-method
|
||||
"sort by modification time")
|
||||
(?d "directory" nil dir-literal
|
||||
"list directory entries instead of contents")
|
||||
(?k "kilobytes" 1024 block-size
|
||||
"using 1024 as the block size")
|
||||
(?h "human-readable" 1024 human-readable
|
||||
"print sizes in human readable format")
|
||||
(?H "si" 1000 human-readable
|
||||
"likewise, but use powers of 1000 not 1024")
|
||||
(?I "ignore" t ignore-pattern
|
||||
"do not list implied entries matching pattern")
|
||||
(?l nil long-listing listing-style
|
||||
"use a long listing format")
|
||||
(?n "numeric-uid-gid" nil numeric-uid-gid
|
||||
"list numeric UIDs and GIDs instead of names")
|
||||
(?r "reverse" nil reverse-list
|
||||
"reverse order while sorting")
|
||||
(?s "size" nil show-size
|
||||
"print size of each file, in blocks")
|
||||
(?t nil by-mtime sort-method
|
||||
"sort by modification time")
|
||||
(?u nil by-atime sort-method
|
||||
"sort by last access time")
|
||||
(?x nil by-lines listing-style
|
||||
"list entries by lines instead of by columns")
|
||||
(?C nil by-columns listing-style
|
||||
"list entries by columns")
|
||||
(?L "deference" nil dereference-links
|
||||
"list entries pointed to by symbolic links")
|
||||
(?R "recursive" nil show-recursive
|
||||
"list subdirectories recursively")
|
||||
(?S nil by-size sort-method
|
||||
"sort by file size")
|
||||
(?U nil unsorted sort-method
|
||||
"do not sort; list entries in directory order")
|
||||
(?X nil by-extension sort-method
|
||||
"sort alphabetically by entry extension")
|
||||
(?1 nil single-column listing-style
|
||||
"list one file per line")
|
||||
(nil "help" nil nil
|
||||
"show this usage display")
|
||||
:external "ls"
|
||||
:usage "[OPTION]... [FILE]...
|
||||
List information about the FILEs (the current directory by default).
|
||||
Sort entries alphabetically across.")
|
||||
;; setup some defaults, based on what the user selected
|
||||
(unless block-size
|
||||
(setq block-size eshell-ls-default-blocksize))
|
||||
(unless listing-style
|
||||
(setq listing-style 'by-columns))
|
||||
(unless args
|
||||
(setq args (list ".")))
|
||||
(let ((eshell-ls-exclude-regexp eshell-ls-exclude-regexp))
|
||||
(when ignore-pattern
|
||||
(unless (eshell-using-module 'eshell-glob)
|
||||
(error (concat "-I option requires that `eshell-glob'"
|
||||
" be a member of `eshell-modules-list'")))
|
||||
(set-text-properties 0 (length ignore-pattern) nil ignore-pattern)
|
||||
(if eshell-ls-exclude-regexp
|
||||
(setq eshell-ls-exclude-regexp
|
||||
(concat "\\(" eshell-ls-exclude-regexp "\\|"
|
||||
(eshell-glob-regexp ignore-pattern) "\\)"))
|
||||
(setq eshell-ls-exclude-regexp (eshell-glob-regexp ignore-pattern))))
|
||||
;; list the files!
|
||||
(eshell-ls-entries
|
||||
(mapcar (function
|
||||
(lambda (arg)
|
||||
(cons (if (and (eshell-under-windows-p)
|
||||
(file-name-absolute-p arg))
|
||||
(expand-file-name arg)
|
||||
arg)
|
||||
(file-attributes arg)))) args)
|
||||
t (expand-file-name default-directory)))
|
||||
(funcall flush-func)))
|
||||
|
||||
(defsubst eshell-ls-printable-size (filesize &optional by-blocksize)
|
||||
"Return a printable FILESIZE."
|
||||
(eshell-printable-size filesize human-readable
|
||||
(and by-blocksize block-size)
|
||||
eshell-ls-use-colors))
|
||||
|
||||
(defsubst eshell-ls-size-string (attrs size-width)
|
||||
"Return the size string for ATTRS length, using SIZE-WIDTH."
|
||||
(let* ((str (eshell-ls-printable-size (nth 7 attrs) t))
|
||||
(len (length str)))
|
||||
(if (< len size-width)
|
||||
(concat (make-string (- size-width len) ? ) str)
|
||||
str)))
|
||||
|
||||
(defun eshell-ls-annotate (fileinfo)
|
||||
"Given a FILEINFO object, return a resolved, decorated FILEINFO.
|
||||
This means resolving any symbolic links, determining what face the
|
||||
name should be displayed as, etc. Think of it as cooking a FILEINFO."
|
||||
(if (not (and (stringp (cadr fileinfo))
|
||||
(or dereference-links
|
||||
(eq listing-style 'long-listing))))
|
||||
(setcar fileinfo (eshell-ls-decorated-name fileinfo))
|
||||
(let (dir attr)
|
||||
(unless (file-name-absolute-p (cadr fileinfo))
|
||||
(setq dir (file-truename
|
||||
(file-name-directory
|
||||
(expand-file-name (car fileinfo))))))
|
||||
(setq attr
|
||||
(file-attributes
|
||||
(let ((target (if dir
|
||||
(expand-file-name (cadr fileinfo) dir)
|
||||
(cadr fileinfo))))
|
||||
(if dereference-links
|
||||
(file-truename target)
|
||||
target))))
|
||||
(if (or dereference-links
|
||||
(string-match "^\\.\\.?$" (car fileinfo)))
|
||||
(progn
|
||||
(setcdr fileinfo attr)
|
||||
(setcar fileinfo (eshell-ls-decorated-name fileinfo)))
|
||||
(assert (eq listing-style 'long-listing))
|
||||
(setcar fileinfo
|
||||
(concat (eshell-ls-decorated-name fileinfo) " -> "
|
||||
(eshell-ls-decorated-name
|
||||
(cons (cadr fileinfo) attr)))))))
|
||||
fileinfo)
|
||||
|
||||
(defun eshell-ls-file (fileinfo &optional size-width copy-fileinfo)
|
||||
"Output FILE in long format.
|
||||
FILE may be a string, or a cons cell whose car is the filename and
|
||||
whose cdr is the list of file attributes."
|
||||
(if (not (cdr fileinfo))
|
||||
(funcall error-func (format "%s: No such file or directory\n"
|
||||
(car fileinfo)))
|
||||
(setq fileinfo
|
||||
(eshell-ls-annotate (if copy-fileinfo
|
||||
(cons (car fileinfo)
|
||||
(cdr fileinfo))
|
||||
fileinfo)))
|
||||
(let ((file (car fileinfo))
|
||||
(attrs (cdr fileinfo)))
|
||||
(if (not (eq listing-style 'long-listing))
|
||||
(if show-size
|
||||
(funcall insert-func (eshell-ls-size-string attrs size-width)
|
||||
" " file "\n")
|
||||
(funcall insert-func file "\n"))
|
||||
(let ((line
|
||||
(concat
|
||||
(if show-size
|
||||
(concat (eshell-ls-size-string attrs size-width) " "))
|
||||
(format
|
||||
"%s%4d %-8s %-8s "
|
||||
(or (nth 8 attrs) "??????????")
|
||||
(or (nth 1 attrs) 0)
|
||||
(or (and (not numeric-uid-gid)
|
||||
(nth 2 attrs)
|
||||
(eshell-substring
|
||||
(user-login-name (nth 2 attrs)) 8))
|
||||
(nth 2 attrs)
|
||||
"")
|
||||
(or (and (not numeric-uid-gid)
|
||||
(nth 3 attrs)
|
||||
(eshell-substring
|
||||
(eshell-group-name (nth 3 attrs)) 8))
|
||||
(nth 3 attrs)
|
||||
""))
|
||||
(let* ((str (eshell-ls-printable-size (nth 7 attrs)))
|
||||
(len (length str)))
|
||||
(if (< len 8)
|
||||
(concat (make-string (- 8 len) ? ) str)
|
||||
str))
|
||||
" " (format-time-string
|
||||
(concat
|
||||
"%b %e "
|
||||
(if (= (nth 5 (decode-time (current-time)))
|
||||
(nth 5 (decode-time
|
||||
(nth (cond
|
||||
((eq sort-method 'by-atime) 4)
|
||||
((eq sort-method 'by-ctime) 6)
|
||||
(t 5)) attrs))))
|
||||
"%H:%M"
|
||||
" %Y")) (nth (cond
|
||||
((eq sort-method 'by-atime) 4)
|
||||
((eq sort-method 'by-ctime) 6)
|
||||
(t 5)) attrs)) " ")))
|
||||
(funcall insert-func line file "\n"))))))
|
||||
|
||||
(defun eshell-ls-dir (dirinfo &optional insert-name root-dir size-width)
|
||||
"Output the entries in DIRINFO.
|
||||
If INSERT-NAME is non-nil, the name of DIRINFO will be output. If
|
||||
ROOT-DIR is also non-nil, and a directory name, DIRINFO will be output
|
||||
relative to that directory."
|
||||
(let ((dir (car dirinfo)))
|
||||
(if (not (cdr dirinfo))
|
||||
(funcall error-func (format "%s: No such file or directory\n" dir))
|
||||
(if dir-literal
|
||||
(eshell-ls-file dirinfo size-width)
|
||||
(if insert-name
|
||||
(funcall insert-func
|
||||
(eshell-ls-decorated-name
|
||||
(cons (concat
|
||||
(if root-dir
|
||||
(file-relative-name dir root-dir)
|
||||
(expand-file-name dir)))
|
||||
(cdr dirinfo))) ":\n"))
|
||||
(let ((entries
|
||||
(eshell-directory-files-and-attributes dir nil nil t)))
|
||||
(unless show-all
|
||||
(while (and entries
|
||||
(string-match eshell-ls-exclude-regexp
|
||||
(caar entries)))
|
||||
(setq entries (cdr entries)))
|
||||
(let ((e entries))
|
||||
(while (cdr e)
|
||||
(if (string-match eshell-ls-exclude-regexp (car (cadr e)))
|
||||
(setcdr e (cddr e))
|
||||
(setq e (cdr e))))))
|
||||
(when (or (eq listing-style 'long-listing) show-size)
|
||||
(let ((total 0.0))
|
||||
(setq size-width 0)
|
||||
(eshell-for e entries
|
||||
(if (nth 7 (cdr e))
|
||||
(setq total (+ total (nth 7 (cdr e)))
|
||||
size-width
|
||||
(max size-width
|
||||
(length (eshell-ls-printable-size
|
||||
(nth 7 (cdr e)) t))))))
|
||||
(funcall insert-func "total "
|
||||
(eshell-ls-printable-size total t) "\n")))
|
||||
(let ((default-directory (expand-file-name dir)))
|
||||
(if show-recursive
|
||||
(eshell-ls-entries
|
||||
(let ((e entries) (good-entries (list t)))
|
||||
(while e
|
||||
(unless (let ((len (length (caar e))))
|
||||
(and (eq (aref (caar e) 0) ?.)
|
||||
(or (= len 1)
|
||||
(and (= len 2)
|
||||
(eq (aref (caar e) 1) ?.)))))
|
||||
(nconc good-entries (list (car e))))
|
||||
(setq e (cdr e)))
|
||||
(cdr good-entries))
|
||||
nil root-dir)
|
||||
(eshell-ls-files (eshell-ls-sort-entries entries)
|
||||
size-width))))))))
|
||||
|
||||
(defsubst eshell-ls-compare-entries (l r inx func)
|
||||
"Compare the time of two files, L and R, the attribute indexed by INX."
|
||||
(let ((lt (nth inx (cdr l)))
|
||||
(rt (nth inx (cdr r))))
|
||||
(if (equal lt rt)
|
||||
(string-lessp (directory-file-name (car l))
|
||||
(directory-file-name (car r)))
|
||||
(funcall func rt lt))))
|
||||
|
||||
(defun eshell-ls-sort-entries (entries)
|
||||
"Sort the given ENTRIES, which may be files, directories or both.
|
||||
In Eshell's implementation of ls, ENTRIES is always reversed."
|
||||
(if (eq sort-method 'unsorted)
|
||||
(nreverse entries)
|
||||
(sort entries
|
||||
(function
|
||||
(lambda (l r)
|
||||
(let ((result
|
||||
(cond
|
||||
((eq sort-method 'by-atime)
|
||||
(eshell-ls-compare-entries
|
||||
l r 4 'eshell-time-less-p))
|
||||
((eq sort-method 'by-mtime)
|
||||
(eshell-ls-compare-entries
|
||||
l r 5 'eshell-time-less-p))
|
||||
((eq sort-method 'by-ctime)
|
||||
(eshell-ls-compare-entries
|
||||
l r 6 'eshell-time-less-p))
|
||||
((eq sort-method 'by-size)
|
||||
(eshell-ls-compare-entries
|
||||
l r 7 '<))
|
||||
((eq sort-method 'by-extension)
|
||||
(let ((lx (file-name-extension
|
||||
(directory-file-name (car l))))
|
||||
(rx (file-name-extension
|
||||
(directory-file-name (car r)))))
|
||||
(cond
|
||||
((or (and (not lx) (not rx))
|
||||
(equal lx rx))
|
||||
(string-lessp (directory-file-name (car l))
|
||||
(directory-file-name (car r))))
|
||||
((not lx) t)
|
||||
((not rx) nil)
|
||||
(t
|
||||
(string-lessp lx rx)))))
|
||||
(t
|
||||
(string-lessp (directory-file-name (car l))
|
||||
(directory-file-name (car r)))))))
|
||||
(if reverse-list
|
||||
(not result)
|
||||
result)))))))
|
||||
|
||||
(defun eshell-ls-files (files &optional size-width copy-fileinfo)
|
||||
"Output a list of FILES.
|
||||
Each member of FILES is either a string or a cons cell of the form
|
||||
\(FILE . ATTRS)."
|
||||
(if (memq listing-style '(long-listing single-column))
|
||||
(eshell-for file files
|
||||
(if file
|
||||
(eshell-ls-file file size-width copy-fileinfo)))
|
||||
(let ((f files)
|
||||
last-f
|
||||
display-files
|
||||
ignore)
|
||||
(while f
|
||||
(if (cdar f)
|
||||
(setq last-f f
|
||||
f (cdr f))
|
||||
(unless ignore
|
||||
(funcall error-func
|
||||
(format "%s: No such file or directory\n" (caar f))))
|
||||
(if (eq f files)
|
||||
(setq files (cdr files)
|
||||
f files)
|
||||
(if (not (cdr f))
|
||||
(progn
|
||||
(setcdr last-f nil)
|
||||
(setq f nil))
|
||||
(setcar f (cadr f))
|
||||
(setcdr f (cddr f))))))
|
||||
(if (not show-size)
|
||||
(setq display-files (mapcar 'eshell-ls-annotate files))
|
||||
(eshell-for file files
|
||||
(let* ((str (eshell-ls-printable-size (nth 7 (cdr file)) t))
|
||||
(len (length str)))
|
||||
(if (< len size-width)
|
||||
(setq str (concat (make-string (- size-width len) ? ) str)))
|
||||
(setq file (eshell-ls-annotate file)
|
||||
display-files (cons (cons (concat str " " (car file))
|
||||
(cdr file))
|
||||
display-files))))
|
||||
(setq display-files (nreverse display-files)))
|
||||
(let* ((col-vals
|
||||
(if (eq listing-style 'by-columns)
|
||||
(eshell-ls-find-column-lengths display-files)
|
||||
(assert (eq listing-style 'by-lines))
|
||||
(eshell-ls-find-column-widths display-files)))
|
||||
(col-widths (car col-vals))
|
||||
(display-files (cdr col-vals))
|
||||
(columns (length col-widths))
|
||||
(col-index 1)
|
||||
need-return)
|
||||
(eshell-for file display-files
|
||||
(let ((name
|
||||
(if (car file)
|
||||
(if show-size
|
||||
(concat (substring (car file) 0 size-width)
|
||||
(eshell-ls-decorated-name
|
||||
(cons (substring (car file) size-width)
|
||||
(cdr file))))
|
||||
(eshell-ls-decorated-name file))
|
||||
"")))
|
||||
(if (< col-index columns)
|
||||
(setq need-return
|
||||
(concat need-return name
|
||||
(make-string
|
||||
(max 0 (- (aref col-widths
|
||||
(1- col-index))
|
||||
(length name))) ? ))
|
||||
col-index (1+ col-index))
|
||||
(funcall insert-func need-return name "\n")
|
||||
(setq col-index 1 need-return nil))))
|
||||
(if need-return
|
||||
(funcall insert-func need-return "\n"))))))
|
||||
|
||||
(defun eshell-ls-entries (entries &optional separate root-dir)
|
||||
"Output PATH's directory ENTRIES, formatted according to OPTIONS.
|
||||
Each member of ENTRIES may either be a string or a cons cell, the car
|
||||
of which is the file name, and the cdr of which is the list of
|
||||
attributes.
|
||||
If SEPARATE is non-nil, directories name will be entirely separated
|
||||
from the filenames. This is the normal behavior, except when doing a
|
||||
recursive listing.
|
||||
ROOT-DIR, if non-nil, specifies the root directory of the listing, to
|
||||
which non-absolute directory names will be made relative if ever they
|
||||
need to be printed."
|
||||
(let (dirs files show-names need-return (size-width 0))
|
||||
(eshell-for entry entries
|
||||
(if (and (not dir-literal)
|
||||
(or (eshell-ls-filetype-p (cdr entry) ?d)
|
||||
(and (eshell-ls-filetype-p (cdr entry) ?l)
|
||||
(file-directory-p (car entry)))))
|
||||
(progn
|
||||
(unless separate
|
||||
(setq files (cons entry files)
|
||||
size-width
|
||||
(if show-size
|
||||
(max size-width
|
||||
(length (eshell-ls-printable-size
|
||||
(nth 7 (cdr entry)) t))))))
|
||||
(setq dirs (cons entry dirs)))
|
||||
(setq files (cons entry files)
|
||||
size-width
|
||||
(if show-size
|
||||
(max size-width
|
||||
(length (eshell-ls-printable-size
|
||||
(nth 7 (cdr entry)) t)))))))
|
||||
(when files
|
||||
(eshell-ls-files (eshell-ls-sort-entries files)
|
||||
size-width show-recursive)
|
||||
(setq need-return t))
|
||||
(setq show-names (or show-recursive
|
||||
(> (+ (length files) (length dirs)) 1)))
|
||||
(eshell-for dir (eshell-ls-sort-entries dirs)
|
||||
(if (and need-return (not dir-literal))
|
||||
(funcall insert-func "\n"))
|
||||
(eshell-ls-dir dir show-names
|
||||
(unless (file-name-absolute-p (car dir))
|
||||
root-dir) size-width)
|
||||
(setq need-return t))))
|
||||
|
||||
(defun eshell-ls-find-column-widths (files)
|
||||
"Find the best fitting column widths for FILES.
|
||||
It will be returned as a vector, whose length is the number of columns
|
||||
to use, and each member of which is the width of that column
|
||||
\(including spacing)."
|
||||
(let* ((numcols 0)
|
||||
(width 0)
|
||||
(widths
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (file)
|
||||
(+ 2 (length (car file)))))
|
||||
files))
|
||||
;; must account for the added space...
|
||||
(max-width (+ (window-width) 2))
|
||||
(best-width 0)
|
||||
col-widths)
|
||||
|
||||
;; determine the largest number of columns in the first row
|
||||
(let ((w widths))
|
||||
(while (and w (< width max-width))
|
||||
(setq width (+ width (car w))
|
||||
numcols (1+ numcols)
|
||||
w (cdr w))))
|
||||
|
||||
;; refine it based on the following rows
|
||||
(while (> numcols 0)
|
||||
(let ((i 0)
|
||||
(colw (make-vector numcols 0))
|
||||
(w widths))
|
||||
(while w
|
||||
(if (= i numcols)
|
||||
(setq i 0))
|
||||
(aset colw i (max (aref colw i) (car w)))
|
||||
(setq w (cdr w) i (1+ i)))
|
||||
(setq i 0 width 0)
|
||||
(while (< i numcols)
|
||||
(setq width (+ width (aref colw i))
|
||||
i (1+ i)))
|
||||
(if (and (< width max-width)
|
||||
(> width best-width))
|
||||
(setq col-widths colw
|
||||
best-width width)))
|
||||
(setq numcols (1- numcols)))
|
||||
|
||||
(cons (or col-widths (vector max-width)) files)))
|
||||
|
||||
(defun eshell-ls-find-column-lengths (files)
|
||||
"Find the best fitting column lengths for FILES.
|
||||
It will be returned as a vector, whose length is the number of columns
|
||||
to use, and each member of which is the width of that column
|
||||
\(including spacing)."
|
||||
(let* ((numcols 1)
|
||||
(width 0)
|
||||
(widths
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (file)
|
||||
(+ 2 (length (car file)))))
|
||||
files))
|
||||
(max-width (+ (window-width) 2))
|
||||
col-widths
|
||||
colw)
|
||||
|
||||
;; refine it based on the following rows
|
||||
(while numcols
|
||||
(let* ((rows (ceiling (/ (length widths)
|
||||
(float numcols))))
|
||||
(w widths)
|
||||
(len (* rows numcols))
|
||||
(index 0)
|
||||
(i 0))
|
||||
(setq width 0)
|
||||
(unless (or (= rows 0)
|
||||
(<= (/ (length widths) (float rows))
|
||||
(float (1- numcols))))
|
||||
(setq colw (make-vector numcols 0))
|
||||
(while (> len 0)
|
||||
(if (= i numcols)
|
||||
(setq i 0 index (1+ index)))
|
||||
(aset colw i
|
||||
(max (aref colw i)
|
||||
(or (nth (+ (* i rows) index) w) 0)))
|
||||
(setq len (1- len) i (1+ i)))
|
||||
(setq i 0)
|
||||
(while (< i numcols)
|
||||
(setq width (+ width (aref colw i))
|
||||
i (1+ i))))
|
||||
(if (>= width max-width)
|
||||
(setq numcols nil)
|
||||
(if colw
|
||||
(setq col-widths colw))
|
||||
(if (>= numcols (length widths))
|
||||
(setq numcols nil)
|
||||
(setq numcols (1+ numcols))))))
|
||||
|
||||
(if (not col-widths)
|
||||
(cons (vector max-width) files)
|
||||
(setq numcols (length col-widths))
|
||||
(let* ((rows (ceiling (/ (length widths)
|
||||
(float numcols))))
|
||||
(len (* rows numcols))
|
||||
(newfiles (make-list len nil))
|
||||
(index 0)
|
||||
(i 0)
|
||||
(j 0))
|
||||
(while (< j len)
|
||||
(if (= i numcols)
|
||||
(setq i 0 index (1+ index)))
|
||||
(setcar (nthcdr j newfiles)
|
||||
(nth (+ (* i rows) index) files))
|
||||
(setq j (1+ j) i (1+ i)))
|
||||
(cons col-widths newfiles)))))
|
||||
|
||||
(defun eshell-ls-decorated-name (file)
|
||||
"Return FILE, possibly decorated.
|
||||
Use TRUENAME for predicate tests, if passed."
|
||||
(if eshell-ls-use-colors
|
||||
(let ((face
|
||||
(cond
|
||||
((not (cdr file))
|
||||
'eshell-ls-missing-face)
|
||||
|
||||
((stringp (cadr file))
|
||||
'eshell-ls-symlink-face)
|
||||
|
||||
((eq (cadr file) t)
|
||||
'eshell-ls-directory-face)
|
||||
|
||||
((not (eshell-ls-filetype-p (cdr file) ?-))
|
||||
'eshell-ls-special-face)
|
||||
|
||||
((and (not (= (user-uid) 0)) ; root can execute anything
|
||||
(eshell-ls-applicable (cdr file) 3
|
||||
'file-executable-p (car file)))
|
||||
'eshell-ls-executable-face)
|
||||
|
||||
((not (eshell-ls-applicable (cdr file) 1
|
||||
'file-readable-p (car file)))
|
||||
'eshell-ls-unreadable-face)
|
||||
|
||||
((string-match eshell-ls-archive-regexp (car file))
|
||||
'eshell-ls-archive-face)
|
||||
|
||||
((string-match eshell-ls-backup-regexp (car file))
|
||||
'eshell-ls-backup-face)
|
||||
|
||||
((string-match eshell-ls-product-regexp (car file))
|
||||
'eshell-ls-product-face)
|
||||
|
||||
((string-match eshell-ls-clutter-regexp (car file))
|
||||
'eshell-ls-clutter-face)
|
||||
|
||||
((not (eshell-ls-applicable (cdr file) 2
|
||||
'file-writable-p (car file)))
|
||||
'eshell-ls-readonly-face)
|
||||
(eshell-ls-highlight-alist
|
||||
(let ((tests eshell-ls-highlight-alist)
|
||||
value)
|
||||
(while tests
|
||||
(if (funcall (caar tests) (car file) (cdr file))
|
||||
(setq value (cdar tests) tests nil)
|
||||
(setq tests (cdr tests))))
|
||||
value)))))
|
||||
(if face
|
||||
(add-text-properties 0 (length (car file))
|
||||
(list 'face face)
|
||||
(car file)))))
|
||||
(car file))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-ls.el ends here
|
602
lisp/eshell/em-pred.el
Normal file
602
lisp/eshell/em-pred.el
Normal file
@ -0,0 +1,602 @@
|
||||
;;; em-pred --- argument predicates and modifiers (ala zsh)
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-pred)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-pred nil
|
||||
"This module allows for predicates to be applied to globbing
|
||||
patterns (similar to zsh), in addition to string modifiers which can
|
||||
be applied either to globbing results, variable references, or just
|
||||
ordinary strings."
|
||||
:tag "Value modifiers and predicates"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Argument predication is used to affect which members of a list are
|
||||
;; selected for use as argument. This is most useful with globbing,
|
||||
;; but can be used on any list argument, to select certain members.
|
||||
;;
|
||||
;; Argument modifiers are used to manipulate argument values. For
|
||||
;; example, sorting lists, upcasing words, substituting characters,
|
||||
;; etc.
|
||||
;;
|
||||
;; Here are some examples of how to use argument predication. Most of
|
||||
;; the predicates and modifiers are modeled after those provided by
|
||||
;; zsh.
|
||||
;;
|
||||
;; ls -ld *(/) ; list all directories
|
||||
;; ls -l *(@u'johnw') ; list all symlinks owned by 'johnw'
|
||||
;; bzip2 -9v **/*(a+30) ; compress everything which hasn't been
|
||||
;; accessed in 30 days
|
||||
;; echo *.c(:o:R) ; a reversed, sorted list of C files
|
||||
;; *(^@:U^u0) ; all non-symlinks not owned by 'root', upcased
|
||||
;; chmod u-x *(U*) : remove exec bit on all executables owned by user
|
||||
;;
|
||||
;; See the zsh docs for more on the syntax ([(zsh.info)Filename
|
||||
;; Generation]).
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-pred-load-hook '(eshell-pred-initialize)
|
||||
"*A list of functions to run when `eshell-pred' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-pred)
|
||||
|
||||
(defcustom eshell-predicate-alist
|
||||
'((?/ . (eshell-pred-file-type ?d)) ; directories
|
||||
(?. . (eshell-pred-file-type ?-)) ; regular files
|
||||
(?s . (eshell-pred-file-type ?s)) ; sockets
|
||||
(?p . (eshell-pred-file-type ?p)) ; named pipes
|
||||
(?@ . (eshell-pred-file-type ?l)) ; symbolic links
|
||||
(?% . (eshell-pred-file-type ?%)) ; allow user to specify (c def.)
|
||||
(?r . (eshell-pred-file-mode 0400)) ; owner-readable
|
||||
(?w . (eshell-pred-file-mode 0200)) ; owner-writable
|
||||
(?x . (eshell-pred-file-mode 0100)) ; owner-executable
|
||||
(?A . (eshell-pred-file-mode 0040)) ; group-readable
|
||||
(?I . (eshell-pred-file-mode 0020)) ; group-writable
|
||||
(?E . (eshell-pred-file-mode 0010)) ; group-executable
|
||||
(?R . (eshell-pred-file-mode 0004)) ; world-readable
|
||||
(?W . (eshell-pred-file-mode 0002)) ; world-writable
|
||||
(?X . (eshell-pred-file-mode 0001)) ; world-executable
|
||||
(?s . (eshell-pred-file-mode 4000)) ; setuid
|
||||
(?S . (eshell-pred-file-mode 2000)) ; setgid
|
||||
(?t . (eshell-pred-file-mode 1000)) ; sticky bit
|
||||
(?U . '(lambda (file) ; owned by effective uid
|
||||
(if (file-exists-p file)
|
||||
(= (nth 2 (file-attributes file)) (user-uid)))))
|
||||
;;; (?G . '(lambda (file) ; owned by effective gid
|
||||
;;; (if (file-exists-p file)
|
||||
;;; (= (nth 2 (file-attributes file)) (user-uid)))))
|
||||
(?* . '(lambda (file)
|
||||
(and (file-regular-p file)
|
||||
(not (file-symlink-p file))
|
||||
(file-executable-p file))))
|
||||
(?l . (eshell-pred-file-links))
|
||||
(?u . (eshell-pred-user-or-group ?u "user" 2 'eshell-user-id))
|
||||
(?g . (eshell-pred-user-or-group ?g "group" 3 'eshell-group-id))
|
||||
(?a . (eshell-pred-file-time ?a "access" 4))
|
||||
(?m . (eshell-pred-file-time ?m "modification" 5))
|
||||
(?c . (eshell-pred-file-time ?c "change" 6))
|
||||
(?L . (eshell-pred-file-size)))
|
||||
"*A list of predicates than can be applied to a globbing pattern.
|
||||
The format of each entry is
|
||||
|
||||
(CHAR . PREDICATE-FUNC-SEXP)"
|
||||
:type '(repeat (cons character sexp))
|
||||
:group 'eshell-pred)
|
||||
|
||||
(put 'eshell-predicate-alist 'risky-local-variable t)
|
||||
|
||||
(defcustom eshell-modifier-alist
|
||||
'((?e . '(lambda (lst)
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (str)
|
||||
(eshell-stringify
|
||||
(car (eshell-parse-argument str))))) lst)))
|
||||
(?L . '(lambda (lst)
|
||||
(mapcar 'downcase lst)))
|
||||
(?U . '(lambda (lst)
|
||||
(mapcar 'upcase lst)))
|
||||
(?C . '(lambda (lst)
|
||||
(mapcar 'capitalize lst)))
|
||||
(?h . '(lambda (lst)
|
||||
(mapcar 'file-name-directory lst)))
|
||||
(?i . (eshell-include-members))
|
||||
(?x . (eshell-include-members t))
|
||||
(?r . '(lambda (lst)
|
||||
(mapcar 'file-name-sans-extension lst)))
|
||||
(?e . '(lambda (lst)
|
||||
(mapcar 'file-name-extension lst)))
|
||||
(?t . '(lambda (lst)
|
||||
(mapcar 'file-name-nondirectory lst)))
|
||||
(?q . '(lambda (lst)
|
||||
(mapcar 'eshell-escape-arg lst)))
|
||||
(?u . '(lambda (lst)
|
||||
(eshell-uniqify-list lst)))
|
||||
(?o . '(lambda (lst)
|
||||
(sort lst 'string-lessp)))
|
||||
(?O . '(lambda (lst)
|
||||
(nreverse (sort lst 'string-lessp))))
|
||||
(?j . (eshell-join-members))
|
||||
(?S . (eshell-split-members))
|
||||
(?R . 'reverse)
|
||||
(?g . (progn
|
||||
(forward-char)
|
||||
(if (eq (char-before) ?s)
|
||||
(eshell-pred-substitute t)
|
||||
(error "`g' modifier cannot be used alone"))))
|
||||
(?s . (eshell-pred-substitute)))
|
||||
"*A list of modifiers than can be applied to an argument expansion.
|
||||
The format of each entry is
|
||||
|
||||
(CHAR ENTRYWISE-P MODIFIER-FUNC-SEXP)"
|
||||
:type '(repeat (cons character sexp))
|
||||
:group 'eshell-pred)
|
||||
|
||||
(put 'eshell-modifier-alist 'risky-local-variable t)
|
||||
|
||||
(defvar eshell-predicate-help-string
|
||||
"Eshell predicate quick reference:
|
||||
|
||||
- follow symbolic references for predicates after the `-'
|
||||
^ invert sense of predicates after the `^'
|
||||
|
||||
FILE TYPE:
|
||||
/ directories s sockets
|
||||
. regular files p named pipes
|
||||
* executable (files only) @ symbolic links
|
||||
|
||||
%x file type == `x' (as by ls -l; so `c' = char device, etc.)
|
||||
|
||||
PERMISSION BITS (for owner/group/world):
|
||||
r/A/R readable s setuid
|
||||
w/I/W writable S setgid
|
||||
x/E/X executable t sticky bit
|
||||
|
||||
OWNERSHIP:
|
||||
U owned by effective uid
|
||||
u(UID|'user') owned by UID/user
|
||||
g(GID|'group') owned by GID/group
|
||||
|
||||
FILE ATTRIBUTES:
|
||||
l[+-]N +/-/= N links
|
||||
a[Mwhm][+-](N|'FILE') access time +/-/= N mnths/weeks/days/mins
|
||||
if FILE specified, use as comparison basis;
|
||||
so a+'file.c' shows files accessed before
|
||||
file.c was last accessed
|
||||
m[Mwhm][+-](N|'FILE') modification time...
|
||||
c[Mwhm][+-](N|'FILE') change time...
|
||||
L[kmp][+-]N file size +/-/= N Kb/Mb/blocks
|
||||
|
||||
EXAMPLES:
|
||||
*(^@) all non-dot files which are not symlinks
|
||||
.#*(^@) all files which are not symbolic links
|
||||
**/.#*(*) all executable files, searched recursively
|
||||
***/*~f*(-/) recursively (though not traversing symlinks),
|
||||
find all directories (or symlinks referring to
|
||||
directories) whose names do not begin with f.
|
||||
e*(*Lk+50) executables 50k or larger beginning with 'e'")
|
||||
|
||||
(defvar eshell-modifier-help-string
|
||||
"Eshell modifier quick reference:
|
||||
|
||||
FOR SINGLE ARGUMENTS, or each argument of a list of strings:
|
||||
e evaluate again
|
||||
L lowercase
|
||||
U uppercase
|
||||
C capitalize
|
||||
h dirname
|
||||
t basename
|
||||
e file extension
|
||||
r strip file extension
|
||||
q escape special characters
|
||||
|
||||
S split string at any whitespace character
|
||||
S/PAT/ split string at each occurance of PAT
|
||||
|
||||
FOR LISTS OF ARGUMENTS:
|
||||
o sort alphabetically
|
||||
O reverse sort alphabetically
|
||||
u uniq list (typically used after :o or :O)
|
||||
R reverse list
|
||||
|
||||
j join list members, separated by a space
|
||||
j/PAT/ join list members, separated by PAT
|
||||
i/PAT/ exclude all members not matching PAT
|
||||
x/PAT/ exclude all members matching PAT
|
||||
|
||||
s/pat/match/ substitute PAT with MATCH
|
||||
g/pat/match/ substitute PAT with MATCH for all occurances
|
||||
|
||||
EXAMPLES:
|
||||
*.c(:o) sorted list of .c files")
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-display-predicate-help ()
|
||||
(interactive)
|
||||
(with-electric-help
|
||||
(function
|
||||
(lambda ()
|
||||
(insert eshell-predicate-help-string)))))
|
||||
|
||||
(defun eshell-display-modifier-help ()
|
||||
(interactive)
|
||||
(with-electric-help
|
||||
(function
|
||||
(lambda ()
|
||||
(insert eshell-modifier-help-string)))))
|
||||
|
||||
(defun eshell-pred-initialize ()
|
||||
"Initialize the predicate/modifier code."
|
||||
(make-local-hook 'eshell-parse-argument-hook)
|
||||
(add-hook 'eshell-parse-argument-hook
|
||||
'eshell-parse-arg-modifier t t)
|
||||
(define-key eshell-command-map [(meta ?q)] 'eshell-display-predicate-help)
|
||||
(define-key eshell-command-map [(meta ?m)] 'eshell-display-modifier-help))
|
||||
|
||||
(defun eshell-apply-modifiers (lst predicates modifiers)
|
||||
"Apply to LIST a series of PREDICATES and MODIFIERS."
|
||||
(let (stringified)
|
||||
(if (stringp lst)
|
||||
(setq lst (list lst)
|
||||
stringified t))
|
||||
(when (listp lst)
|
||||
(setq lst (eshell-winnow-list lst nil predicates))
|
||||
(while modifiers
|
||||
(setq lst (funcall (car modifiers) lst)
|
||||
modifiers (cdr modifiers)))
|
||||
(if (and stringified
|
||||
(= (length lst) 1))
|
||||
(car lst)
|
||||
lst))))
|
||||
|
||||
(defun eshell-parse-arg-modifier ()
|
||||
"Parse a modifier that has been specified after an argument.
|
||||
This function is specially for adding onto `eshell-parse-argument-hook'."
|
||||
(when (eq (char-after) ?\()
|
||||
(forward-char)
|
||||
(let ((end (eshell-find-delimiter ?\( ?\))))
|
||||
(if (not end)
|
||||
(throw 'eshell-incomplete ?\()
|
||||
(when (eshell-arg-delimiter (1+ end))
|
||||
(save-restriction
|
||||
(narrow-to-region (point) end)
|
||||
(let* ((modifiers (eshell-parse-modifiers))
|
||||
(preds (car modifiers))
|
||||
(mods (cdr modifiers)))
|
||||
(if (or preds mods)
|
||||
;; has to go at the end, which is only natural since
|
||||
;; syntactically it can only occur at the end
|
||||
(setq eshell-current-modifiers
|
||||
(append
|
||||
eshell-current-modifiers
|
||||
(list
|
||||
`(lambda (lst)
|
||||
(eshell-apply-modifiers
|
||||
lst (quote ,preds) (quote ,mods)))))))))
|
||||
(goto-char (1+ end))
|
||||
(eshell-finish-arg))))))
|
||||
|
||||
(defun eshell-parse-modifiers ()
|
||||
"Parse value modifiers and predicates at point.
|
||||
If ALLOW-PREDS is non-nil, predicates will be parsed as well.
|
||||
Return a cons cell of the form
|
||||
|
||||
(PRED-FUNC-LIST . MOD-FUNC-LIST)
|
||||
|
||||
NEW-STRING is STRING minus any modifiers. PRED-FUNC-LIST is a list of
|
||||
predicate functions. MOD-FUNC-LIST is a list of result modifier
|
||||
functions. PRED-FUNCS take a filename and return t if the test
|
||||
succeeds; MOD-FUNCS take any string and preform a modification,
|
||||
returning the resultant string."
|
||||
(let (result negate follow preds mods)
|
||||
(condition-case err
|
||||
(while (not (eobp))
|
||||
(let ((char (char-after)))
|
||||
(cond
|
||||
((eq char ?')
|
||||
(forward-char)
|
||||
(if (looking-at "[^|':]")
|
||||
(let ((func (read (current-buffer))))
|
||||
(if (and func (functionp func))
|
||||
(setq preds (eshell-add-pred-func func preds
|
||||
negate follow))
|
||||
(error "Invalid function predicate '%s'"
|
||||
(eshell-stringify func))))
|
||||
(error "Invalid function predicate")))
|
||||
((eq char ?^)
|
||||
(forward-char)
|
||||
(setq negate (not negate)))
|
||||
((eq char ?-)
|
||||
(forward-char)
|
||||
(setq follow (not follow)))
|
||||
((eq char ?|)
|
||||
(forward-char)
|
||||
(if (looking-at "[^|':]")
|
||||
(let ((func (read (current-buffer))))
|
||||
(if (and func (functionp func))
|
||||
(setq mods
|
||||
(cons `(lambda (lst)
|
||||
(mapcar (function ,func) lst))
|
||||
mods))
|
||||
(error "Invalid function modifier '%s'"
|
||||
(eshell-stringify func))))
|
||||
(error "Invalid function modifier")))
|
||||
((eq char ?:)
|
||||
(forward-char)
|
||||
(let ((mod (assq (char-after) eshell-modifier-alist)))
|
||||
(if (not mod)
|
||||
(error "Unknown modifier character '%c'" (char-after))
|
||||
(forward-char)
|
||||
(setq mods (cons (eval (cdr mod)) mods)))))
|
||||
(t
|
||||
(let ((pred (assq char eshell-predicate-alist)))
|
||||
(if (not pred)
|
||||
(error "Unknown predicate character '%c'" char)
|
||||
(forward-char)
|
||||
(setq preds
|
||||
(eshell-add-pred-func (eval (cdr pred)) preds
|
||||
negate follow))))))))
|
||||
(end-of-buffer
|
||||
(error "Predicate or modifier ended prematurely")))
|
||||
(cons (nreverse preds) (nreverse mods))))
|
||||
|
||||
(defun eshell-add-pred-func (pred funcs negate follow)
|
||||
"Add the predicate function PRED to FUNCS."
|
||||
(if negate
|
||||
(setq pred `(lambda (file)
|
||||
(not (funcall ,pred file)))))
|
||||
(if follow
|
||||
(setq pred `(lambda (file)
|
||||
(funcall ,pred (file-truename file)))))
|
||||
(cons pred funcs))
|
||||
|
||||
(defun eshell-pred-user-or-group (mod-char mod-type attr-index get-id-func)
|
||||
"Return a predicate to test whether a file match a given user/group id."
|
||||
(let (ugid open close end)
|
||||
(if (looking-at "[0-9]+")
|
||||
(progn
|
||||
(setq ugid (string-to-number (match-string 0)))
|
||||
(goto-char (match-end 0)))
|
||||
(setq open (char-after))
|
||||
(if (setq close (memq open '(?\( ?\[ ?\< ?\{)))
|
||||
(setq close (car (last '(?\) ?\] ?\> ?\})
|
||||
(length close))))
|
||||
(setq close open))
|
||||
(forward-char)
|
||||
(setq end (eshell-find-delimiter open close))
|
||||
(unless end
|
||||
(error "Malformed %s name string for modifier `%c'"
|
||||
mod-type mod-char))
|
||||
(setq ugid
|
||||
(funcall get-id-func (buffer-substring (point) end)))
|
||||
(goto-char (1+ end)))
|
||||
(unless ugid
|
||||
(error "Unknown %s name specified for modifier `%c'"
|
||||
mod-type mod-char))
|
||||
`(lambda (file)
|
||||
(let ((attrs (file-attributes file)))
|
||||
(if attrs
|
||||
(= (nth ,attr-index attrs) ,ugid))))))
|
||||
|
||||
(defun eshell-pred-file-time (mod-char mod-type attr-index)
|
||||
"Return a predicate to test whether a file matches a certain time."
|
||||
(let* ((quantum 86400)
|
||||
qual amount when open close end)
|
||||
(when (memq (char-after) '(?M ?w ?h ?m))
|
||||
(setq quantum (char-after))
|
||||
(cond
|
||||
((eq quantum ?M)
|
||||
(setq quantum (* 60 60 24 30)))
|
||||
((eq quantum ?w)
|
||||
(setq quantum (* 60 60 24 7)))
|
||||
((eq quantum ?h)
|
||||
(setq quantum (* 60 60)))
|
||||
((eq quantum ?m)
|
||||
(setq quantum 60))
|
||||
((eq quantum ?s)
|
||||
(setq quantum 1)))
|
||||
(forward-char))
|
||||
(when (memq (char-after) '(?+ ?-))
|
||||
(setq qual (char-after))
|
||||
(forward-char))
|
||||
(if (looking-at "[0-9]+")
|
||||
(progn
|
||||
(setq when (- (eshell-time-to-seconds (current-time))
|
||||
(* (string-to-number (match-string 0))
|
||||
quantum)))
|
||||
(goto-char (match-end 0)))
|
||||
(setq open (char-after))
|
||||
(if (setq close (memq open '(?\( ?\[ ?\< ?\{)))
|
||||
(setq close (car (last '(?\) ?\] ?\> ?\})
|
||||
(length close))))
|
||||
(setq close open))
|
||||
(forward-char)
|
||||
(setq end (eshell-find-delimiter open close))
|
||||
(unless end
|
||||
(error "Malformed %s time modifier `%c'" mod-type mod-char))
|
||||
(let* ((file (buffer-substring (point) end))
|
||||
(attrs (file-attributes file)))
|
||||
(unless attrs
|
||||
(error "Cannot stat file `%s'" file))
|
||||
(setq when (eshell-time-to-seconds (nth attr-index attrs))))
|
||||
(goto-char (1+ end)))
|
||||
`(lambda (file)
|
||||
(let ((attrs (file-attributes file)))
|
||||
(if attrs
|
||||
(,(if (eq qual ?-)
|
||||
'<
|
||||
(if (eq qual ?+)
|
||||
'>
|
||||
'=)) ,when (eshell-time-to-seconds
|
||||
(nth ,attr-index attrs))))))))
|
||||
|
||||
(defun eshell-pred-file-type (type)
|
||||
"Return a test which tests that the file is of a certain TYPE.
|
||||
TYPE must be a character, and should be one of the possible options
|
||||
that 'ls -l' will show in the first column of its display. "
|
||||
(when (eq type ?%)
|
||||
(setq type (char-after))
|
||||
(if (memq type '(?b ?c))
|
||||
(forward-char)
|
||||
(setq type ?%)))
|
||||
`(lambda (file)
|
||||
(let ((attrs (file-attributes (directory-file-name file))))
|
||||
(if attrs
|
||||
(memq (aref (nth 8 attrs) 0)
|
||||
,(if (eq type ?%)
|
||||
'(?b ?c)
|
||||
(list 'quote (list type))))))))
|
||||
|
||||
(defsubst eshell-pred-file-mode (mode)
|
||||
"Return a test which tests that MODE pertains to the file."
|
||||
`(lambda (file)
|
||||
(let ((modes (file-modes file)))
|
||||
(if modes
|
||||
(logand ,mode modes)))))
|
||||
|
||||
(defun eshell-pred-file-links ()
|
||||
"Return a predicate to test whether a file has a given number of links."
|
||||
(let (qual amount)
|
||||
(when (memq (char-after) '(?- ?+))
|
||||
(setq qual (char-after))
|
||||
(forward-char))
|
||||
(unless (looking-at "[0-9]+")
|
||||
(error "Invalid file link count modifier `l'"))
|
||||
(setq amount (string-to-number (match-string 0)))
|
||||
(goto-char (match-end 0))
|
||||
`(lambda (file)
|
||||
(let ((attrs (file-attributes file)))
|
||||
(if attrs
|
||||
(,(if (eq qual ?-)
|
||||
'<
|
||||
(if (eq qual ?+)
|
||||
'>
|
||||
'=)) (nth 1 attrs) ,amount))))))
|
||||
|
||||
(defun eshell-pred-file-size ()
|
||||
"Return a predicate to test whether a file is of a given size."
|
||||
(let ((quantum 1) qual amount)
|
||||
(when (memq (downcase (char-after)) '(?k ?m ?p))
|
||||
(setq qual (downcase (char-after)))
|
||||
(cond
|
||||
((eq qual ?k)
|
||||
(setq quantum 1024))
|
||||
((eq qual ?m)
|
||||
(setq quantum (* 1024 1024)))
|
||||
((eq qual ?p)
|
||||
(setq quantum 512)))
|
||||
(forward-char))
|
||||
(when (memq (char-after) '(?- ?+))
|
||||
(setq qual (char-after))
|
||||
(forward-char))
|
||||
(unless (looking-at "[0-9]+")
|
||||
(error "Invalid file size modifier `L'"))
|
||||
(setq amount (* (string-to-number (match-string 0)) quantum))
|
||||
(goto-char (match-end 0))
|
||||
`(lambda (file)
|
||||
(let ((attrs (file-attributes file)))
|
||||
(if attrs
|
||||
(,(if (eq qual ?-)
|
||||
'<
|
||||
(if (eq qual ?+)
|
||||
'>
|
||||
'=)) (nth 7 attrs) ,amount))))))
|
||||
|
||||
(defun eshell-pred-substitute (&optional repeat)
|
||||
"Return a modifier function that will substitute matches."
|
||||
(let ((delim (char-after))
|
||||
match replace end)
|
||||
(forward-char)
|
||||
(setq end (eshell-find-delimiter delim delim nil nil t)
|
||||
match (buffer-substring-no-properties (point) end))
|
||||
(goto-char (1+ end))
|
||||
(setq end (eshell-find-delimiter delim delim nil nil t)
|
||||
replace (buffer-substring-no-properties (point) end))
|
||||
(goto-char (1+ end))
|
||||
(if repeat
|
||||
`(lambda (lst)
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (str)
|
||||
(let ((i 0))
|
||||
(while (setq i (string-match ,match str i))
|
||||
(setq str (replace-match ,replace t nil str))))
|
||||
str)) lst))
|
||||
`(lambda (lst)
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (str)
|
||||
(if (string-match ,match str)
|
||||
(setq str (replace-match ,replace t nil str)))
|
||||
str)) lst)))))
|
||||
|
||||
(defun eshell-include-members (&optional invert-p)
|
||||
"Include only lisp members matching a regexp."
|
||||
(let ((delim (char-after))
|
||||
regexp end)
|
||||
(forward-char)
|
||||
(setq end (eshell-find-delimiter delim delim nil nil t)
|
||||
regexp (buffer-substring-no-properties (point) end))
|
||||
(goto-char (1+ end))
|
||||
`(lambda (lst)
|
||||
(eshell-winnow-list
|
||||
lst nil '((lambda (elem)
|
||||
,(if invert-p
|
||||
`(not (string-match ,regexp elem))
|
||||
`(string-match ,regexp elem))))))))
|
||||
|
||||
(defun eshell-join-members ()
|
||||
"Return a modifier function that join matches."
|
||||
(let ((delim (char-after))
|
||||
str end)
|
||||
(if (not (memq delim '(?' ?/)))
|
||||
(setq delim " ")
|
||||
(forward-char)
|
||||
(setq end (eshell-find-delimiter delim delim nil nil t)
|
||||
str (buffer-substring-no-properties (point) end))
|
||||
(goto-char (1+ end)))
|
||||
`(lambda (lst)
|
||||
(mapconcat 'identity lst ,str))))
|
||||
|
||||
(defun eshell-split-members ()
|
||||
"Return a modifier function that splits members."
|
||||
(let ((delim (char-after))
|
||||
sep end)
|
||||
(when (memq delim '(?' ?/))
|
||||
(forward-char)
|
||||
(setq end (eshell-find-delimiter delim delim nil nil t)
|
||||
sep (buffer-substring-no-properties (point) end))
|
||||
(goto-char (1+ end)))
|
||||
`(lambda (lst)
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (str)
|
||||
(split-string str ,sep))) lst))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-pred.el ends here
|
174
lisp/eshell/em-prompt.el
Normal file
174
lisp/eshell/em-prompt.el
Normal file
@ -0,0 +1,174 @@
|
||||
;;; em-prompt --- command prompts
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-prompt)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-prompt nil
|
||||
"This module provides command prompts, and navigation between them,
|
||||
as is common with most shells."
|
||||
:tag "Command prompts"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Most of the prompt navigation commands of `comint-mode' are
|
||||
;; supported, such as C-c C-n, C-c C-p, etc.
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-prompt-load-hook '(eshell-prompt-initialize)
|
||||
"*A list of functions to call when loading `eshell-prompt'."
|
||||
:type 'hook
|
||||
:group 'eshell-prompt)
|
||||
|
||||
(defcustom eshell-prompt-function
|
||||
(function
|
||||
(lambda ()
|
||||
(concat (eshell/pwd)
|
||||
(if (= (user-uid) 0) " # " " $ "))))
|
||||
"*A function that returns the Eshell prompt string.
|
||||
Make sure to update `eshell-prompt-regexp' so that it will match your
|
||||
prompt."
|
||||
:type 'function
|
||||
:group 'eshell-prompt)
|
||||
|
||||
(defcustom eshell-prompt-regexp "^[^#$\n]* [#$] "
|
||||
"*A regexp which fully matches your eshell prompt.
|
||||
This setting is important, since it affects how eshell will interpret
|
||||
the lines that are passed to it.
|
||||
If this variable is changed, all Eshell buffers must be exited and
|
||||
re-entered for it to take effect."
|
||||
:type 'regexp
|
||||
:group 'eshell-prompt)
|
||||
|
||||
(defcustom eshell-highlight-prompt t
|
||||
"*If non-nil, Eshell should highlight the prompt."
|
||||
:type 'boolean
|
||||
:group 'eshell-prompt)
|
||||
|
||||
(defface eshell-prompt-face
|
||||
'((((class color) (background light)) (:foreground "Red" :bold t))
|
||||
(((class color) (background dark)) (:foreground "Pink" :bold t))
|
||||
(t (:bold t)))
|
||||
"*The face used to highlight prompt strings.
|
||||
For highlighting other kinds of strings -- similar to shell mode's
|
||||
behavior -- simply use an output filer which changes text properties."
|
||||
:group 'eshell-prompt)
|
||||
|
||||
(defcustom eshell-before-prompt-hook nil
|
||||
"*A list of functions to call before outputting the prompt."
|
||||
:type 'hook
|
||||
:options '(eshell-begin-on-new-line)
|
||||
:group 'eshell-prompt)
|
||||
|
||||
(defcustom eshell-after-prompt-hook nil
|
||||
"*A list of functions to call after outputting the prompt.
|
||||
Note that if `eshell-scroll-show-maximum-output' is non-nil, then
|
||||
setting `eshell-show-maximum-output' here won't do much. It depends
|
||||
on whether the user wants the resizing to happen while output is
|
||||
arriving, or after."
|
||||
:type 'hook
|
||||
:options '(eshell-show-maximum-output)
|
||||
:group 'eshell-prompt)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-prompt-initialize ()
|
||||
"Initialize the prompting code."
|
||||
(unless eshell-non-interactive-p
|
||||
(make-local-hook 'eshell-post-command-hook)
|
||||
(add-hook 'eshell-post-command-hook 'eshell-emit-prompt nil t)
|
||||
|
||||
(make-local-variable 'eshell-prompt-regexp)
|
||||
(if eshell-prompt-regexp
|
||||
(set (make-local-variable 'paragraph-start) eshell-prompt-regexp))
|
||||
|
||||
(set (make-local-variable 'eshell-skip-prompt-function)
|
||||
'eshell-skip-prompt)
|
||||
|
||||
(define-key eshell-command-map [(control ?n)] 'eshell-next-prompt)
|
||||
(define-key eshell-command-map [(control ?p)] 'eshell-previous-prompt)))
|
||||
|
||||
(defun eshell-emit-prompt ()
|
||||
"Emit a prompt if eshell is being used interactively."
|
||||
(run-hooks 'eshell-before-prompt-hook)
|
||||
(if (not eshell-prompt-function)
|
||||
(set-marker eshell-last-output-end (point))
|
||||
(let ((prompt (funcall eshell-prompt-function)))
|
||||
(and eshell-highlight-prompt
|
||||
(add-text-properties 0 (length prompt)
|
||||
'(read-only t
|
||||
face eshell-prompt-face
|
||||
rear-nonsticky (face read-only))
|
||||
prompt))
|
||||
(eshell-interactive-print prompt)))
|
||||
(run-hooks 'eshell-after-prompt-hook))
|
||||
|
||||
(defun eshell-backward-matching-input (regexp arg)
|
||||
"Search backward through buffer for match for REGEXP.
|
||||
Matches are searched for on lines that match `eshell-prompt-regexp'.
|
||||
With prefix argument N, search for Nth previous match.
|
||||
If N is negative, find the next or Nth next match."
|
||||
(interactive (eshell-regexp-arg "Backward input matching (regexp): "))
|
||||
(let* ((re (concat eshell-prompt-regexp ".*" regexp))
|
||||
(pos (save-excursion (end-of-line (if (> arg 0) 0 1))
|
||||
(if (re-search-backward re nil t arg)
|
||||
(point)))))
|
||||
(if (null pos)
|
||||
(progn (message "Not found")
|
||||
(ding))
|
||||
(goto-char pos)
|
||||
(eshell-bol))))
|
||||
|
||||
(defun eshell-forward-matching-input (regexp arg)
|
||||
"Search forward through buffer for match for REGEXP.
|
||||
Matches are searched for on lines that match `eshell-prompt-regexp'.
|
||||
With prefix argument N, search for Nth following match.
|
||||
If N is negative, find the previous or Nth previous match."
|
||||
(interactive (eshell-regexp-arg "Forward input matching (regexp): "))
|
||||
(eshell-backward-matching-input regexp (- arg)))
|
||||
|
||||
(defun eshell-next-prompt (n)
|
||||
"Move to end of Nth next prompt in the buffer.
|
||||
See `eshell-prompt-regexp'."
|
||||
(interactive "p")
|
||||
(forward-paragraph n)
|
||||
(eshell-skip-prompt))
|
||||
|
||||
(defun eshell-previous-prompt (n)
|
||||
"Move to end of Nth previous prompt in the buffer.
|
||||
See `eshell-prompt-regexp'."
|
||||
(interactive "p")
|
||||
(eshell-next-prompt (- (1+ n))))
|
||||
|
||||
(defun eshell-skip-prompt ()
|
||||
"Skip past the text matching regexp `eshell-prompt-regexp'.
|
||||
If this takes us past the end of the current line, don't skip at all."
|
||||
(let ((eol (line-end-position)))
|
||||
(if (and (looking-at eshell-prompt-regexp)
|
||||
(<= (match-end 0) eol))
|
||||
(goto-char (match-end 0)))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-prompt.el ends here
|
248
lisp/eshell/em-rebind.el
Normal file
248
lisp/eshell/em-rebind.el
Normal file
@ -0,0 +1,248 @@
|
||||
;;; em-rebind --- rebind keys when point is at current input
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-rebind)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-rebind nil
|
||||
"This module allows for special keybindings that only take effect
|
||||
while the point is in a region of input text. By default, it binds
|
||||
C-a to move to the beginning of the input text (rather than just the
|
||||
beginning of the line), and C-p and C-n to move through the input
|
||||
history, C-u kills the current input text, etc. It also, if
|
||||
`eshell-confine-point-to-input' is non-nil, does not allow certain
|
||||
commands to cause the point to leave the input area, such as
|
||||
`backward-word', `previous-line', etc. This module intends to mimic
|
||||
the behavior of normal shells while the user editing new input text."
|
||||
:tag "Rebind keys at input"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-rebind-load-hook '(eshell-rebind-initialize)
|
||||
"*A list of functions to call when loading `eshell-rebind'."
|
||||
:type 'hook
|
||||
:group 'eshell-rebind)
|
||||
|
||||
(defcustom eshell-rebind-keys-alist
|
||||
'(([(control ?a)] . eshell-bol)
|
||||
([home] . eshell-bol)
|
||||
([(control ?d)] . eshell-delchar-or-maybe-eof)
|
||||
([backspace] . eshell-delete-backward-char)
|
||||
([delete] . eshell-delete-backward-char)
|
||||
([(control ?w)] . backward-kill-word)
|
||||
([(control ?u)] . eshell-kill-input))
|
||||
"*Bind some keys differently if point is in input text."
|
||||
:type '(repeat (cons (vector :tag "Keys to bind"
|
||||
(repeat :inline t sexp))
|
||||
(function :tag "Command")))
|
||||
:group 'eshell-rebind)
|
||||
|
||||
(defcustom eshell-confine-point-to-input t
|
||||
"*If non-nil, do not allow the point to leave the current input.
|
||||
This is more difficult to do nicely in Emacs than one might think.
|
||||
Basically, the `point-left' attribute is added to the input text, and
|
||||
a function is placed on that hook to take the point back to
|
||||
`eshell-last-output-end' every time the user tries to move away. But
|
||||
since there are many cases in which the point _ought_ to move away
|
||||
\(for programmatic reasons), the variable
|
||||
`eshell-cannot-leave-input-list' defines commands which are affected
|
||||
from this rule. However, this list is by no means as complete as it
|
||||
probably should be, so basically all one can hope for is that other
|
||||
people will left the point alone in the Eshell buffer. Sigh."
|
||||
:type 'boolean
|
||||
:group 'eshell-rebind)
|
||||
|
||||
(defcustom eshell-error-if-move-away t
|
||||
"*If non-nil, consider it an error to try to move outside current input.
|
||||
This is default behavior of shells like bash."
|
||||
:type 'boolean
|
||||
:group 'eshell-rebind)
|
||||
|
||||
(defcustom eshell-remap-previous-input t
|
||||
"*If non-nil, remap input keybindings on previous prompts as well."
|
||||
:type 'boolean
|
||||
:group 'eshell-rebind)
|
||||
|
||||
(defcustom eshell-cannot-leave-input-list
|
||||
'(beginning-of-line-text
|
||||
beginning-of-line
|
||||
move-to-column
|
||||
move-to-column-force
|
||||
move-to-left-margin
|
||||
move-to-tab-stop
|
||||
forward-char
|
||||
backward-char
|
||||
delete-char
|
||||
delete-backward-char
|
||||
backward-delete-char
|
||||
backward-delete-char-untabify
|
||||
kill-paragraph
|
||||
backward-kill-paragraph
|
||||
kill-sentence
|
||||
backward-kill-sentence
|
||||
kill-sexp
|
||||
backward-kill-sexp
|
||||
kill-word
|
||||
backward-kill-word
|
||||
kill-region
|
||||
forward-list
|
||||
backward-list
|
||||
forward-page
|
||||
backward-page
|
||||
forward-point
|
||||
forward-paragraph
|
||||
backward-paragraph
|
||||
backward-prefix-chars
|
||||
forward-sentence
|
||||
backward-sentence
|
||||
forward-sexp
|
||||
backward-sexp
|
||||
forward-to-indentation
|
||||
backward-to-indentation
|
||||
backward-up-list
|
||||
forward-word
|
||||
backward-word
|
||||
forward-line
|
||||
backward-line
|
||||
previous-line
|
||||
next-line
|
||||
forward-visible-line
|
||||
forward-comment
|
||||
forward-thing)
|
||||
"*A list of commands that cannot leave the input area."
|
||||
:type '(repeat function)
|
||||
:group 'eshell-rebind)
|
||||
|
||||
;; Internal Variables:
|
||||
|
||||
(defvar eshell-input-keymap)
|
||||
(defvar eshell-previous-point)
|
||||
(defvar eshell-lock-keymap)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-rebind-initialize ()
|
||||
"Initialize the inputing code."
|
||||
(unless eshell-non-interactive-p
|
||||
(make-local-hook 'eshell-mode-hook)
|
||||
(add-hook 'eshell-mode-hook 'eshell-setup-input-keymap nil t)
|
||||
(make-local-hook 'pre-command-hook)
|
||||
(make-local-variable 'eshell-previous-point)
|
||||
(add-hook 'pre-command-hook 'eshell-save-previous-point nil t)
|
||||
(make-local-hook 'post-command-hook)
|
||||
(make-local-variable 'overriding-local-map)
|
||||
(add-hook 'post-command-hook 'eshell-rebind-input-map nil t)
|
||||
(set (make-local-variable 'eshell-lock-keymap) nil)
|
||||
(define-key eshell-command-map [(meta ?l)] 'eshell-lock-local-map)))
|
||||
|
||||
(defun eshell-lock-local-map (&optional arg)
|
||||
"Lock or unlock the current local keymap.
|
||||
Within a prefix arg, set the local keymap to its normal value, and
|
||||
lock it at that."
|
||||
(interactive "P")
|
||||
(if (or arg (not eshell-lock-keymap))
|
||||
(progn
|
||||
(use-local-map eshell-mode-map)
|
||||
(setq eshell-lock-keymap t)
|
||||
(message "Local keymap locked in normal mode"))
|
||||
(use-local-map eshell-input-keymap)
|
||||
(setq eshell-lock-keymap nil)
|
||||
(message "Local keymap unlocked: obey context")))
|
||||
|
||||
(defun eshell-save-previous-point ()
|
||||
"Save the location of point before the next command is run."
|
||||
(setq eshell-previous-point (point)))
|
||||
|
||||
(defsubst eshell-point-within-input-p (pos)
|
||||
"Test whether POS is within an input range."
|
||||
(let (begin)
|
||||
(or (and (>= pos eshell-last-output-end)
|
||||
eshell-last-output-end)
|
||||
(and eshell-remap-previous-input
|
||||
(setq begin
|
||||
(save-excursion
|
||||
(eshell-bol)
|
||||
(and (not (bolp)) (point))))
|
||||
(>= pos begin)
|
||||
(<= pos (line-end-position))
|
||||
begin))))
|
||||
|
||||
(defun eshell-rebind-input-map ()
|
||||
"Rebind the input keymap based on the location of the cursor."
|
||||
(ignore-errors
|
||||
(unless eshell-lock-keymap
|
||||
(if (eshell-point-within-input-p (point))
|
||||
(use-local-map eshell-input-keymap)
|
||||
(let (begin)
|
||||
(if (and eshell-confine-point-to-input
|
||||
(setq begin
|
||||
(eshell-point-within-input-p eshell-previous-point))
|
||||
(memq this-command eshell-cannot-leave-input-list))
|
||||
(progn
|
||||
(use-local-map eshell-input-keymap)
|
||||
(goto-char begin)
|
||||
(if (and eshell-error-if-move-away
|
||||
(not (eq this-command 'kill-region)))
|
||||
(beep)))
|
||||
(use-local-map eshell-mode-map)))))))
|
||||
|
||||
(defun eshell-setup-input-keymap ()
|
||||
"Setup the input keymap to be used during input editing."
|
||||
(make-local-variable 'eshell-input-keymap)
|
||||
(setq eshell-input-keymap (make-sparse-keymap))
|
||||
(set-keymap-parent eshell-input-keymap eshell-mode-map)
|
||||
(let ((bindings eshell-rebind-keys-alist))
|
||||
(while bindings
|
||||
(define-key eshell-input-keymap (caar bindings)
|
||||
(cdar bindings))
|
||||
(setq bindings (cdr bindings)))))
|
||||
|
||||
(defun eshell-delete-backward-char (n &optional killflag)
|
||||
"Delete the last character, unless it's part of the output."
|
||||
(interactive "P")
|
||||
(let ((count (prefix-numeric-value n)))
|
||||
(if (eshell-point-within-input-p (- (point) count))
|
||||
(delete-backward-char count n)
|
||||
(beep))))
|
||||
|
||||
(defun eshell-delchar-or-maybe-eof (arg)
|
||||
"Delete ARG characters forward or send an EOF to subprocess.
|
||||
Sends an EOF only if point is at the end of the buffer and there is no
|
||||
input."
|
||||
(interactive "p")
|
||||
(let ((proc (get-buffer-process (current-buffer))))
|
||||
(if (eobp)
|
||||
(cond
|
||||
((not (= (point) eshell-last-output-end))
|
||||
(beep))
|
||||
(proc
|
||||
(process-send-eof))
|
||||
(t
|
||||
(eshell-life-is-too-much)))
|
||||
(eshell-delete-backward-char (- arg)))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-rebind.el ends here
|
130
lisp/eshell/em-script.el
Normal file
130
lisp/eshell/em-script.el
Normal file
@ -0,0 +1,130 @@
|
||||
;;; em-script --- Eshell script files
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-script)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-script nil
|
||||
"This module allows for the execution of files containing Eshell
|
||||
commands, as a script file."
|
||||
:tag "Running script files."
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-script-load-hook '(eshell-script-initialize)
|
||||
"*A list of functions to call when loading `eshell-script'."
|
||||
:type 'hook
|
||||
:group 'eshell-script)
|
||||
|
||||
(defcustom eshell-login-script (concat eshell-directory-name "login")
|
||||
"*If non-nil, a file to invoke when starting up Eshell interactively.
|
||||
This file should be a file containing Eshell commands, where comment
|
||||
lines begin with '#'."
|
||||
:type 'file
|
||||
:group 'eshell-script)
|
||||
|
||||
(defcustom eshell-rc-script (concat eshell-directory-name "profile")
|
||||
"*If non-nil, a file to invoke whenever Eshell is started.
|
||||
This includes when running `eshell-command'."
|
||||
:type 'file
|
||||
:group 'eshell-script)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-script-initialize ()
|
||||
"Initialize the script parsing code."
|
||||
(make-local-variable 'eshell-interpreter-alist)
|
||||
(setq eshell-interpreter-alist
|
||||
(cons '((lambda (file)
|
||||
(string= (file-name-nondirectory file)
|
||||
"eshell")) . eshell/source)
|
||||
eshell-interpreter-alist))
|
||||
;; these two variables are changed through usage, but we don't want
|
||||
;; to ruin it for other modules
|
||||
(let (eshell-inside-quote-regexp
|
||||
eshell-outside-quote-regexp)
|
||||
(and (not eshell-non-interactive-p)
|
||||
eshell-login-script
|
||||
(file-readable-p eshell-login-script)
|
||||
(eshell-do-eval
|
||||
(list 'eshell-commands
|
||||
(catch 'eshell-replace-command
|
||||
(eshell-source-file eshell-login-script))) t))
|
||||
(and eshell-rc-script
|
||||
(file-readable-p eshell-rc-script)
|
||||
(eshell-do-eval
|
||||
(list 'eshell-commands
|
||||
(catch 'eshell-replace-command
|
||||
(eshell-source-file eshell-rc-script))) t))))
|
||||
|
||||
(defun eshell-source-file (file &optional args subcommand-p)
|
||||
"Execute a series of Eshell commands in FILE, passing ARGS.
|
||||
Comments begin with '#'."
|
||||
(interactive "f")
|
||||
(let ((orig (point))
|
||||
(here (point-max))
|
||||
(inhibit-point-motion-hooks t)
|
||||
after-change-functions)
|
||||
(goto-char (point-max))
|
||||
(insert-file-contents file)
|
||||
(goto-char (point-max))
|
||||
(throw 'eshell-replace-command
|
||||
(prog1
|
||||
(list 'let
|
||||
(list (list 'eshell-command-name (list 'quote file))
|
||||
(list 'eshell-command-arguments
|
||||
(list 'quote args)))
|
||||
(let ((cmd (eshell-parse-command (cons here (point)))))
|
||||
(if subcommand-p
|
||||
(setq cmd (list 'eshell-as-subcommand cmd)))
|
||||
cmd))
|
||||
(delete-region here (point))
|
||||
(goto-char orig)))))
|
||||
|
||||
(defun eshell/source (&rest args)
|
||||
"Source a file in a subshell environment."
|
||||
(eshell-eval-using-options
|
||||
"source" args
|
||||
'((?h "help" nil nil "show this usage screen")
|
||||
:show-usage
|
||||
:usage "FILE [ARGS]
|
||||
Invoke the Eshell commands in FILE in a subshell, binding ARGS to $1,
|
||||
$2, etc.")
|
||||
(eshell-source-file (car args) (cdr args) t)))
|
||||
|
||||
(defun eshell/. (&rest args)
|
||||
"Source a file in the current environment."
|
||||
(eshell-eval-using-options
|
||||
"." args
|
||||
'((?h "help" nil nil "show this usage screen")
|
||||
:show-usage
|
||||
:usage "FILE [ARGS]
|
||||
Invoke the Eshell commands in FILE within the current shell
|
||||
environment, binding ARGS to $1, $2, etc.")
|
||||
(eshell-source-file (car args) (cdr args))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-script.el ends here
|
305
lisp/eshell/em-smart.el
Normal file
305
lisp/eshell/em-smart.el
Normal file
@ -0,0 +1,305 @@
|
||||
;;; em-smart --- smart display of output
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-smart)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-smart nil
|
||||
"This module combines the facility of normal, modern shells with
|
||||
some of the edit/review concepts inherent in the design of Plan 9's
|
||||
9term. See the docs for more details.
|
||||
|
||||
Most likely you will have to turn this option on and play around with
|
||||
it to get a real sense of how it works."
|
||||
:tag "Smart display of output"
|
||||
:link '(info-link "(eshell.info)Smart display of output")
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; The best way to get a sense of what this code is trying to do is by
|
||||
;; using it. Basically, the philosophy represents a blend between the
|
||||
;; ease of use of modern day shells, and the review-before-you-proceed
|
||||
;; mentality of Plan 9's 9term.
|
||||
;;
|
||||
;; @ When you invoke a command, it is assumed that you want to read
|
||||
;; the output of that command.
|
||||
;;
|
||||
;; @ If the output is not what you wanted, it is assumed that you will
|
||||
;; want to edit, and then resubmit a refined version of that
|
||||
;; command.
|
||||
;;
|
||||
;; @ If the output is valid, pressing any self-inserting character key
|
||||
;; will jump to end of the buffer and insert that character, in
|
||||
;; order to begin entry of a new command.
|
||||
;;
|
||||
;; @ If you show an intention to edit the previous command -- by
|
||||
;; moving around within it -- then the next self-inserting
|
||||
;; characters will insert *there*, instead of at the bottom of the
|
||||
;; buffer.
|
||||
;;
|
||||
;; @ If you show an intention to review old commands, such as M-p or
|
||||
;; M-r, point will jump to the bottom of the buffer before invoking
|
||||
;; that command.
|
||||
;;
|
||||
;; @ If none of the above has happened yet (i.e., your point is just
|
||||
;; sitting on the previous command), you can use SPACE and BACKSPACE
|
||||
;; (or DELETE) to page forward and backward *through the output of
|
||||
;; the last command only*. It will constrain the movement of the
|
||||
;; point and window so that the maximum amount of output is always
|
||||
;; displayed at all times.
|
||||
;;
|
||||
;; @ While output is being generated from a command, the window will
|
||||
;; be constantly reconfigured (until it would otherwise make no
|
||||
;; difference) in order to always show you the most output from the
|
||||
;; command possible. This happens if you change window sizes,
|
||||
;; scroll, etc.
|
||||
;;
|
||||
;; @ Like I said, it's not really comprehensible until you try it! ;)
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-smart-load-hook '(eshell-smart-initialize)
|
||||
"*A list of functions to call when loading `eshell-smart'."
|
||||
:type 'hook
|
||||
:group 'eshell-smart)
|
||||
|
||||
(defcustom eshell-smart-unload-hook
|
||||
(list
|
||||
(function
|
||||
(lambda ()
|
||||
(remove-hook 'window-configuration-change-hook
|
||||
'eshell-refresh-windows))))
|
||||
"*A hook that gets run when `eshell-smart' is unloaded."
|
||||
:type 'hook
|
||||
:group 'eshell-smart)
|
||||
|
||||
(defcustom eshell-review-quick-commands nil
|
||||
"*If nil, point does not stay on quick commands.
|
||||
A quick command is one that produces no output, and exits
|
||||
successfully."
|
||||
:type 'boolean
|
||||
:group 'eshell-smart)
|
||||
|
||||
(defcustom eshell-smart-display-navigate-list
|
||||
'(insert-parentheses
|
||||
mouse-yank-at-click
|
||||
mouse-yank-secondary
|
||||
yank-pop
|
||||
yank-rectangle
|
||||
yank)
|
||||
"*A list of commands which cause Eshell to jump to the end of buffer."
|
||||
:type '(repeat function)
|
||||
:group 'eshell-smart)
|
||||
|
||||
(defcustom eshell-smart-space-goes-to-end t
|
||||
"*If non-nil, space will go to end of buffer when point-max is visible.
|
||||
That is, if a command is running and the user presses SPACE at a time
|
||||
when the end of the buffer is visible, point will go to the end of the
|
||||
buffer and smart-display will be turned off (that is, subsequently
|
||||
pressing backspace will not cause the buffer to scroll down).
|
||||
|
||||
This feature is provided to make it very easy to watch the output of a
|
||||
long-running command, such as make, where it's more desirable to see
|
||||
the output go by than to review it afterward.
|
||||
|
||||
Setting this variable to nil means that space and backspace will
|
||||
always have a consistent behavior, which is to move back and forth
|
||||
through displayed output. But it also means that enabling output
|
||||
tracking requires the user to manually move point to the end of the
|
||||
buffer using \\[end-of-buffer]."
|
||||
:type 'boolean
|
||||
:group 'eshell-smart)
|
||||
|
||||
(defcustom eshell-where-to-jump 'begin
|
||||
"*This variable indicates where point should jump to after a command.
|
||||
The options are `begin', `after' or `end'."
|
||||
:type '(radio (const :tag "Beginning of command" begin)
|
||||
(const :tag "After command word" after)
|
||||
(const :tag "End of command" end))
|
||||
:group 'eshell-smart)
|
||||
|
||||
;;; Internal Variables:
|
||||
|
||||
(defvar eshell-smart-displayed nil)
|
||||
(defvar eshell-smart-command-done nil)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-smart-initialize ()
|
||||
"Setup Eshell smart display."
|
||||
(unless eshell-non-interactive-p
|
||||
;; override a few variables, since they would interfere with the
|
||||
;; smart display functionality.
|
||||
(set (make-local-variable 'eshell-scroll-to-bottom-on-output) nil)
|
||||
(set (make-local-variable 'eshell-scroll-to-bottom-on-input) nil)
|
||||
(set (make-local-variable 'eshell-scroll-show-maximum-output) t)
|
||||
|
||||
(make-local-hook 'window-scroll-functions)
|
||||
(add-hook 'window-scroll-functions 'eshell-smart-scroll-window nil t)
|
||||
(add-hook 'window-configuration-change-hook 'eshell-refresh-windows)
|
||||
|
||||
(make-local-hook 'eshell-output-filter-functions)
|
||||
(add-hook 'eshell-output-filter-functions 'eshell-refresh-windows t t)
|
||||
|
||||
(make-local-hook 'pre-command-hook)
|
||||
(make-local-hook 'after-change-functions)
|
||||
(add-hook 'after-change-functions
|
||||
'eshell-disable-after-change nil t)
|
||||
|
||||
(make-local-hook 'eshell-input-filter-functions)
|
||||
(add-hook 'eshell-input-filter-functions
|
||||
'eshell-smart-display-setup nil t)
|
||||
|
||||
(make-local-variable 'eshell-smart-command-done)
|
||||
(make-local-hook 'eshell-post-command-hook)
|
||||
(add-hook 'eshell-post-command-hook
|
||||
(function
|
||||
(lambda ()
|
||||
(setq eshell-smart-command-done t))) t t)
|
||||
|
||||
(unless eshell-review-quick-commands
|
||||
(add-hook 'eshell-post-command-hook
|
||||
'eshell-smart-maybe-jump-to-end nil t))))
|
||||
|
||||
(defun eshell-smart-scroll-window (wind start)
|
||||
"Scroll the given Eshell window accordingly."
|
||||
(unless eshell-currently-handling-window
|
||||
(let ((inhibit-point-motion-hooks t)
|
||||
(eshell-currently-handling-window t))
|
||||
(save-current-buffer
|
||||
(save-selected-window
|
||||
(select-window wind)
|
||||
(eshell-smart-redisplay))))))
|
||||
|
||||
(defun eshell-refresh-windows (&optional frame)
|
||||
"Refresh all visible Eshell buffers."
|
||||
(let (affected)
|
||||
(walk-windows
|
||||
(function
|
||||
(lambda (wind)
|
||||
(with-current-buffer (window-buffer wind)
|
||||
(when eshell-mode
|
||||
(let (window-scroll-functions)
|
||||
(eshell-smart-scroll-window wind (window-start))
|
||||
(setq affected t))))))
|
||||
0 frame)
|
||||
(if affected
|
||||
(let (window-scroll-functions)
|
||||
(eshell-redisplay)))))
|
||||
|
||||
(defun eshell-smart-display-setup ()
|
||||
"Set the point to somewhere in the beginning of the last command."
|
||||
(cond
|
||||
((eq eshell-where-to-jump 'begin)
|
||||
(goto-char eshell-last-input-start))
|
||||
((eq eshell-where-to-jump 'after)
|
||||
(goto-char (next-single-property-change
|
||||
eshell-last-input-start 'arg-end))
|
||||
(if (= (point) (- eshell-last-input-end 2))
|
||||
(forward-char)))
|
||||
((eq eshell-where-to-jump 'end)
|
||||
(goto-char (1- eshell-last-input-end)))
|
||||
(t
|
||||
(error "Invalid value for `eshell-where-to-jump'")))
|
||||
(setq eshell-smart-command-done nil)
|
||||
(add-hook 'pre-command-hook 'eshell-smart-display-move nil t)
|
||||
(eshell-refresh-windows))
|
||||
|
||||
(defun eshell-disable-after-change (b e l)
|
||||
"Disable smart display mode if the buffer changes in any way."
|
||||
(when eshell-smart-command-done
|
||||
(remove-hook 'pre-command-hook 'eshell-smart-display-move t)
|
||||
(setq eshell-smart-command-done nil)))
|
||||
|
||||
(defun eshell-smart-maybe-jump-to-end ()
|
||||
"Jump to the end of the input buffer.
|
||||
This is done whenever a command exits sucessfully that displayed no
|
||||
output."
|
||||
(when (and (= eshell-last-command-status 0)
|
||||
(= (count-lines eshell-last-input-end
|
||||
eshell-last-output-end) 0))
|
||||
(goto-char (point-max))
|
||||
(remove-hook 'pre-command-hook 'eshell-smart-display-move t)))
|
||||
|
||||
(defun eshell-smart-redisplay ()
|
||||
"Display as much output as possible, smartly."
|
||||
(if (eobp)
|
||||
(recenter -1)
|
||||
(and (memq 'eshell-smart-display-move pre-command-hook)
|
||||
(>= (point) eshell-last-input-start)
|
||||
(< (point) eshell-last-input-end)
|
||||
(set-window-start (selected-window)
|
||||
(line-beginning-position) t))
|
||||
(if (pos-visible-in-window-p (point-max))
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(recenter -1)))))
|
||||
|
||||
(defun eshell-smart-goto-end ()
|
||||
"Like `end-of-buffer', but do not push a mark."
|
||||
(interactive)
|
||||
(goto-char (point-max)))
|
||||
|
||||
(defun eshell-smart-display-move ()
|
||||
"Handle self-inserting or movement commands intelligently."
|
||||
(let (clear)
|
||||
(if (or current-prefix-arg
|
||||
(and (> (point) eshell-last-input-start)
|
||||
(< (point) eshell-last-input-end))
|
||||
(>= (point) eshell-last-output-end))
|
||||
(setq clear t)
|
||||
(cond
|
||||
((eq this-command 'self-insert-command)
|
||||
(if (eq last-command-char ? )
|
||||
(if (and eshell-smart-space-goes-to-end
|
||||
eshell-current-command)
|
||||
(if (not (pos-visible-in-window-p (point-max)))
|
||||
(setq this-command 'scroll-up)
|
||||
(setq this-command 'eshell-smart-goto-end))
|
||||
(setq this-command 'scroll-up))
|
||||
(setq clear t)
|
||||
(goto-char (point-max))))
|
||||
((eq this-command 'delete-backward-char)
|
||||
(setq this-command 'ignore)
|
||||
(if (< (point) eshell-last-input-start)
|
||||
(eshell-show-output)
|
||||
(if (pos-visible-in-window-p eshell-last-input-start)
|
||||
(progn
|
||||
(ignore-errors
|
||||
(scroll-down))
|
||||
(eshell-show-output))
|
||||
(scroll-down)
|
||||
(if (pos-visible-in-window-p eshell-last-input-end)
|
||||
(eshell-show-output)))))
|
||||
((or (memq this-command eshell-smart-display-navigate-list)
|
||||
(and (eq this-command 'eshell-send-input)
|
||||
(not (and (>= (point) eshell-last-input-start)
|
||||
(< (point) eshell-last-input-end)))))
|
||||
(setq clear t)
|
||||
(goto-char (point-max)))))
|
||||
(if clear
|
||||
(remove-hook 'pre-command-hook 'eshell-smart-display-move t))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-smart.el ends here
|
266
lisp/eshell/em-term.el
Normal file
266
lisp/eshell/em-term.el
Normal file
@ -0,0 +1,266 @@
|
||||
;;; em-term --- running visual commands
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-term)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-term nil
|
||||
"This module causes visual commands (e.g., 'vi') to be executed by
|
||||
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"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; 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
|
||||
;; as "visual" programs. In order to run these progrem under Emacs,
|
||||
;; 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.
|
||||
|
||||
(require 'term)
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-term-load-hook '(eshell-term-initialize)
|
||||
"*A list of functions to call when loading `eshell-term'."
|
||||
: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!!
|
||||
"*A list of commands that present their output in a visual fashion."
|
||||
:type '(repeat string)
|
||||
:group 'eshell-term)
|
||||
|
||||
(defcustom eshell-term-name "eterm"
|
||||
"*Name to use for the TERM variable when running visual commands.
|
||||
See `term-term-name' in term.el for more information on how this is
|
||||
used."
|
||||
:type 'string
|
||||
:group 'eshell-term)
|
||||
|
||||
(defcustom eshell-escape-control-x t
|
||||
"*If non-nil, allow <C-x> to be handled by Emacs key in visual buffers.
|
||||
See the variable `eshell-visual-commands'. If this variable is set to
|
||||
nil, <C-x> will send that control character to the invoked process."
|
||||
:type 'boolean
|
||||
:group 'eshell-term)
|
||||
|
||||
;;; 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
|
||||
(cons (cons (function
|
||||
(lambda (command)
|
||||
(member (file-name-nondirectory command)
|
||||
eshell-visual-commands)))
|
||||
'eshell-exec-visual)
|
||||
eshell-interpreter-alist)))
|
||||
|
||||
(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
|
||||
(interp (eshell-find-interpreter (car args)))
|
||||
(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)
|
||||
|
||||
(defun eshell-term-sentinel (proc string)
|
||||
"Destroy the buffer visiting PROC."
|
||||
(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))))
|
||||
|
||||
;; 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.
|
||||
; (if (and (symbolp last-input-char)
|
||||
; (get last-input-char 'ascii-character))
|
||||
; (setq last-input-char (get last-input-char 'ascii-character)))
|
||||
; (eshell-term-send-raw-string (make-string 1 last-input-char)))
|
||||
|
||||
; (defun eshell-term-send-raw-meta ()
|
||||
; (interactive)
|
||||
; (if (symbolp last-input-char)
|
||||
; ;; Convert `return' to C-m, etc.
|
||||
; (let ((tmp (get last-input-char 'event-symbol-elements)))
|
||||
; (if tmp
|
||||
; (setq last-input-char (car tmp)))
|
||||
; (if (symbolp last-input-char)
|
||||
; (progn
|
||||
; (setq tmp (get last-input-char 'ascii-character))
|
||||
; (if tmp (setq last-input-char tmp))))))
|
||||
; (eshell-term-send-raw-string (if (and (numberp last-input-char)
|
||||
; (> last-input-char 127)
|
||||
; (< last-input-char 256))
|
||||
; (make-string 1 last-input-char)
|
||||
; (format "\e%c" last-input-char))))
|
||||
|
||||
; (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 ()))
|
||||
; (x-get-cutbuffer)
|
||||
; (error "No selection or cut buffer available")))
|
||||
; ;; 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))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-term.el ends here
|
927
lisp/eshell/em-unix.el
Normal file
927
lisp/eshell/em-unix.el
Normal file
@ -0,0 +1,927 @@
|
||||
;;; em-unix --- UNIX command aliases
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-unix)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-unix nil
|
||||
"This module defines many of the more common UNIX utilities as
|
||||
aliases implemented in Lisp. These include mv, ln, cp, rm, etc. If
|
||||
the user passes arguments which are too complex, or are unrecognized
|
||||
by the Lisp variant, the external version will be called (if
|
||||
available). The only reason not to use them would be because they are
|
||||
usually much slower. But in several cases their tight integration
|
||||
with Eshell makes them more versatile than their traditional cousins
|
||||
\(such as being able to use `kill' to kill Eshell background processes
|
||||
by name)."
|
||||
:tag "UNIX commands in Lisp"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This file contains implementations of several UNIX command in Emacs
|
||||
;; Lisp, for several reasons:
|
||||
;;
|
||||
;; 1) it makes them available on all platforms where the Lisp
|
||||
;; functions used are available
|
||||
;;
|
||||
;; 2) it makes their functionality accessible and modified by the
|
||||
;; Lisp programmer.
|
||||
;;
|
||||
;; 3) it allows Eshell to refrain from having to invoke external
|
||||
;; processes for common operations.
|
||||
|
||||
(defcustom eshell-unix-load-hook '(eshell-unix-initialize)
|
||||
"*A list of functions to run when `eshell-unix' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-plain-grep-behavior nil
|
||||
"*If non-nil, standalone \"grep\" commands will behave normally.
|
||||
Standalone in this context means not redirected, and not on the
|
||||
receiving side of a command pipeline."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-no-grep-available (not (eshell-search-path "grep"))
|
||||
"*If non-nil, no grep is available on the current machine."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-plain-diff-behavior nil
|
||||
"*If non-nil, standalone \"diff\" commands will behave normally.
|
||||
Standalone in this context means not redirected, and not on the
|
||||
receiving side of a command pipeline."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-plain-locate-behavior nil
|
||||
"*If non-nil, standalone \"locate\" commands will behave normally.
|
||||
Standalone in this context means not redirected, and not on the
|
||||
receiving side of a command pipeline."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-rm-removes-directories nil
|
||||
"*If non-nil, `rm' will remove directory entries.
|
||||
Otherwise, `rmdir' is required."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-rm-interactive-query (= (user-uid) 0)
|
||||
"*If non-nil, `rm' will query before removing anything."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-mv-interactive-query (= (user-uid) 0)
|
||||
"*If non-nil, `mv' will query before overwriting anything."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-mv-overwrite-files t
|
||||
"*If non-nil, `mv' will overwrite files without warning."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-cp-interactive-query (= (user-uid) 0)
|
||||
"*If non-nil, `cp' will query before overwriting anything."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-cp-overwrite-files t
|
||||
"*If non-nil, `cp' will overwrite files without warning."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-ln-interactive-query (= (user-uid) 0)
|
||||
"*If non-nil, `ln' will query before overwriting anything."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(defcustom eshell-ln-overwrite-files t
|
||||
"*If non-nil, `ln' will overwrite files without warning."
|
||||
:type 'boolean
|
||||
:group 'eshell-unix)
|
||||
|
||||
(require 'esh-opt)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-unix-initialize ()
|
||||
"Initialize the UNIX support/emulation code."
|
||||
(make-local-hook 'eshell-post-command-hook)
|
||||
(when (eshell-using-module 'eshell-cmpl)
|
||||
(make-local-hook 'pcomplete-try-first-hook)
|
||||
(add-hook 'pcomplete-try-first-hook
|
||||
'eshell-complete-host-reference nil t)))
|
||||
|
||||
(defalias 'eshell/date 'current-time-string)
|
||||
(defalias 'eshell/basename 'file-name-nondirectory)
|
||||
(defalias 'eshell/dirname 'file-name-directory)
|
||||
|
||||
(eval-when-compile
|
||||
(defvar interactive)
|
||||
(defvar preview)
|
||||
(defvar recursive)
|
||||
(defvar verbose))
|
||||
|
||||
(defun eshell/man (&rest args)
|
||||
"Invoke man, flattening the arguments appropriately."
|
||||
(funcall 'man (apply 'eshell-flatten-and-stringify args)))
|
||||
|
||||
(defun eshell-remove-entries (path files &optional top-level)
|
||||
(while files
|
||||
(if (string-match "\\`\\.\\.?\\'"
|
||||
(file-name-nondirectory (car files)))
|
||||
(if top-level
|
||||
(eshell-error "rm: cannot remove `.' or `..'\n"))
|
||||
(if (and (file-directory-p (car files))
|
||||
(not (file-symlink-p (car files))))
|
||||
(let ((dir (file-name-as-directory (car files))))
|
||||
(eshell-remove-entries dir
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (file)
|
||||
(concat dir file)))
|
||||
(directory-files dir)))
|
||||
(if verbose
|
||||
(eshell-printn (format "rm: removing directory `%s'"
|
||||
(car files))))
|
||||
(unless
|
||||
(or preview
|
||||
(and interactive
|
||||
(not (y-or-n-p
|
||||
(format "rm: remove directory `%s'? "
|
||||
(car files))))))
|
||||
(eshell-funcalln 'delete-directory (car files))))
|
||||
(if verbose
|
||||
(eshell-printn (format "rm: removing file `%s'"
|
||||
(car files))))
|
||||
(unless (or preview
|
||||
(and interactive
|
||||
(not (y-or-n-p
|
||||
(format "rm: remove `%s'? "
|
||||
(car files))))))
|
||||
(eshell-funcalln 'delete-file (car files)))))
|
||||
(setq files (cdr files))))
|
||||
|
||||
(defun eshell/rm (&rest args)
|
||||
"Implementation of rm in Lisp.
|
||||
This is implemented to call either `delete-file', `kill-buffer',
|
||||
`kill-process', or `unintern', depending on the nature of the
|
||||
argument."
|
||||
(setq args (eshell-flatten-list args))
|
||||
(eshell-eval-using-options
|
||||
"rm" args
|
||||
'((?h "help" nil nil "show this usage screen")
|
||||
(?f "force" nil force-removal "force removal")
|
||||
(?i "interactive" nil interactive "prompt before any removal")
|
||||
(?n "preview" nil preview "don't change anything on disk")
|
||||
(?r "recursive" nil recursive
|
||||
"remove the contents of directories recursively")
|
||||
(?R nil nil recursive "(same)")
|
||||
(?v "verbose" nil verbose "explain what is being done")
|
||||
:preserve-args
|
||||
:external "rm"
|
||||
:show-usage
|
||||
:usage "[OPTION]... FILE...
|
||||
Remove (unlink) the FILE(s).")
|
||||
(unless interactive
|
||||
(setq interactive eshell-rm-interactive-query))
|
||||
(if (and force-removal interactive)
|
||||
(setq interactive nil))
|
||||
(while args
|
||||
(let ((entry (if (stringp (car args))
|
||||
(directory-file-name (car args))
|
||||
(if (numberp (car args))
|
||||
(number-to-string (car args))
|
||||
(car args)))))
|
||||
(cond
|
||||
((bufferp entry)
|
||||
(if verbose
|
||||
(eshell-printn (format "rm: removing buffer `%s'" entry)))
|
||||
(unless (or preview
|
||||
(and interactive
|
||||
(not (y-or-n-p (format "rm: delete buffer `%s'? "
|
||||
entry)))))
|
||||
(eshell-funcalln 'kill-buffer entry)))
|
||||
((processp entry)
|
||||
(if verbose
|
||||
(eshell-printn (format "rm: killing process `%s'" entry)))
|
||||
(unless (or preview
|
||||
(and interactive
|
||||
(not (y-or-n-p (format "rm: kill process `%s'? "
|
||||
entry)))))
|
||||
(eshell-funcalln 'kill-process entry)))
|
||||
((symbolp entry)
|
||||
(if verbose
|
||||
(eshell-printn (format "rm: uninterning symbol `%s'" entry)))
|
||||
(unless
|
||||
(or preview
|
||||
(and interactive
|
||||
(not (y-or-n-p (format "rm: unintern symbol `%s'? "
|
||||
entry)))))
|
||||
(eshell-funcalln 'unintern entry)))
|
||||
((stringp entry)
|
||||
(if (and (file-directory-p entry)
|
||||
(not (file-symlink-p entry)))
|
||||
(if (or recursive
|
||||
eshell-rm-removes-directories)
|
||||
(if (or preview
|
||||
(not interactive)
|
||||
(y-or-n-p
|
||||
(format "rm: descend into directory `%s'? "
|
||||
entry)))
|
||||
(eshell-remove-entries nil (list entry) t))
|
||||
(eshell-error (format "rm: %s: is a directory\n" entry)))
|
||||
(eshell-remove-entries nil (list entry) t)))))
|
||||
(setq args (cdr args)))
|
||||
nil))
|
||||
|
||||
(defun eshell/mkdir (&rest args)
|
||||
"Implementation of mkdir in Lisp."
|
||||
(eshell-eval-using-options
|
||||
"mkdir" args
|
||||
'((?h "help" nil nil "show this usage screen")
|
||||
:external "mkdir"
|
||||
:show-usage
|
||||
:usage "[OPTION] DIRECTORY...
|
||||
Create the DIRECTORY(ies), if they do not already exist.")
|
||||
(while args
|
||||
(eshell-funcalln 'make-directory (car args))
|
||||
(setq args (cdr args)))
|
||||
nil))
|
||||
|
||||
(defun eshell/rmdir (&rest args)
|
||||
"Implementation of rmdir in Lisp."
|
||||
(eshell-eval-using-options
|
||||
"rmdir" args
|
||||
'((?h "help" nil nil "show this usage screen")
|
||||
:external "rmdir"
|
||||
:show-usage
|
||||
:usage "[OPTION] DIRECTORY...
|
||||
Remove the DIRECTORY(ies), if they are empty.")
|
||||
(while args
|
||||
(eshell-funcalln 'delete-directory (car args))
|
||||
(setq args (cdr args)))
|
||||
nil))
|
||||
|
||||
(eval-when-compile
|
||||
(defvar no-dereference)
|
||||
(defvar preview)
|
||||
(defvar verbose))
|
||||
|
||||
(defvar eshell-warn-dot-directories t)
|
||||
|
||||
(defun eshell-shuffle-files (command action files target func deep &rest args)
|
||||
"Shuffle around some filesystem entries, using FUNC to do the work."
|
||||
(if (null target)
|
||||
(error "%s: missing destination file" command))
|
||||
(let ((attr-target (file-attributes target))
|
||||
(is-dir (or (file-directory-p target)
|
||||
(and preview (not eshell-warn-dot-directories))))
|
||||
attr)
|
||||
(if (and (not preview) (not is-dir)
|
||||
(> (length files) 1))
|
||||
(error "%s: when %s multiple files, last argument must be a directory"
|
||||
command action))
|
||||
(while files
|
||||
(setcar files (directory-file-name (car files)))
|
||||
(cond
|
||||
((string-match "\\`\\.\\.?\\'"
|
||||
(file-name-nondirectory (car files)))
|
||||
(if eshell-warn-dot-directories
|
||||
(eshell-error (format "%s: %s: omitting directory\n"
|
||||
command (car files)))))
|
||||
((and attr-target
|
||||
(not (eshell-under-windows-p))
|
||||
(setq attr (file-attributes (car files)))
|
||||
(= (nth 10 attr-target) (nth 10 attr))
|
||||
(= (nth 11 attr-target) (nth 11 attr)))
|
||||
(eshell-error (format "%s: `%s' and `%s' are the same file\n"
|
||||
command (car files) target)))
|
||||
(t
|
||||
(let ((source (car files))
|
||||
(target (if is-dir
|
||||
(expand-file-name
|
||||
(file-name-nondirectory (car files)) target)
|
||||
target))
|
||||
link)
|
||||
(if (and (file-directory-p source)
|
||||
(or (not no-dereference)
|
||||
(not (file-symlink-p source)))
|
||||
(not (memq func '(make-symbolic-link
|
||||
add-name-to-file))))
|
||||
(if (and (eq func 'copy-file)
|
||||
(not recursive))
|
||||
(eshell-error (format "%s: %s: omitting directory\n"
|
||||
command (car files)))
|
||||
(let (eshell-warn-dot-directories)
|
||||
(if (and (not deep)
|
||||
(eq func 'rename-file)
|
||||
(= (nth 11 (file-attributes
|
||||
(file-name-directory
|
||||
(expand-file-name source))))
|
||||
(nth 11 (file-attributes
|
||||
(file-name-directory
|
||||
(expand-file-name target))))))
|
||||
(apply 'eshell-funcalln func source target args)
|
||||
(unless (file-directory-p target)
|
||||
(if verbose
|
||||
(eshell-printn
|
||||
(format "%s: making directory %s"
|
||||
command target)))
|
||||
(unless preview
|
||||
(eshell-funcalln 'make-directory target)))
|
||||
(eshell-shuffle-files command action
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (file)
|
||||
(concat source "/" file)))
|
||||
(directory-files source))
|
||||
target func t args)
|
||||
(when (eq func 'rename-file)
|
||||
(if verbose
|
||||
(eshell-printn
|
||||
(format "%s: deleting directory %s"
|
||||
command source)))
|
||||
(unless preview
|
||||
(eshell-funcalln 'delete-directory source))))))
|
||||
(if verbose
|
||||
(eshell-printn (format "%s: %s -> %s" command
|
||||
source target)))
|
||||
(unless preview
|
||||
(if (and no-dereference
|
||||
(setq link (file-symlink-p source)))
|
||||
(progn
|
||||
(apply 'eshell-funcalln 'make-symbolic-link
|
||||
link target args)
|
||||
(if (eq func 'rename-file)
|
||||
(if (and (file-directory-p source)
|
||||
(not (file-symlink-p source)))
|
||||
(eshell-funcalln 'delete-directory source)
|
||||
(eshell-funcalln 'delete-file source))))
|
||||
(apply 'eshell-funcalln func source target args)))))))
|
||||
(setq files (cdr files)))))
|
||||
|
||||
(defun eshell-shorthand-tar-command (command args)
|
||||
"Rewrite `cp -v dir a.tar.gz' to `tar cvzf a.tar.gz dir'."
|
||||
(let* ((archive (car (last args)))
|
||||
(tar-args
|
||||
(cond ((string-match "z2" archive) "If")
|
||||
((string-match "gz" archive) "zf")
|
||||
((string-match "\\(az\\|Z\\)" archive) "Zf")
|
||||
(t "f"))))
|
||||
(if (file-exists-p archive)
|
||||
(setq tar-args (concat "u" tar-args))
|
||||
(setq tar-args (concat "c" tar-args)))
|
||||
(if verbose
|
||||
(setq tar-args (concat "v" tar-args)))
|
||||
(if (equal command "mv")
|
||||
(setq tar-args (concat "--remove-files -" tar-args)))
|
||||
;; truncate the archive name from the arguments
|
||||
(setcdr (last args 2) nil)
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command
|
||||
(format "tar %s %s" tar-args archive) args))))
|
||||
|
||||
;; this is to avoid duplicating code...
|
||||
(defmacro eshell-mvcp-template
|
||||
(command action func query-var force-var &optional preserve)
|
||||
`(if (and (string-match eshell-tar-regexp (car (last args)))
|
||||
(or (> (length args) 2)
|
||||
(and (file-directory-p (car args))
|
||||
(or (not no-dereference)
|
||||
(not (file-symlink-p (car args)))))))
|
||||
(eshell-shorthand-tar-command ,command args)
|
||||
(let (target)
|
||||
(if (> (length args) 1)
|
||||
(progn
|
||||
(setq target (car (last args)))
|
||||
(setcdr (last args 2) nil))
|
||||
(setq args nil))
|
||||
(eshell-shuffle-files
|
||||
,command ,action args target ,func nil
|
||||
,@(append
|
||||
`((if (and (or interactive
|
||||
,query-var)
|
||||
(not force))
|
||||
1 (or force ,force-var)))
|
||||
(if preserve
|
||||
(list preserve)))))
|
||||
nil))
|
||||
|
||||
(defun eshell/mv (&rest args)
|
||||
"Implementation of mv in Lisp."
|
||||
(eshell-eval-using-options
|
||||
"mv" args
|
||||
'((?f "force" nil force
|
||||
"remove existing destinations, never prompt")
|
||||
(?i "interactive" nil interactive
|
||||
"request confirmation if target already exists")
|
||||
(?n "preview" nil preview
|
||||
"don't change anything on disk")
|
||||
(?v "verbose" nil verbose
|
||||
"explain what is being done")
|
||||
(nil "help" nil nil "show this usage screen")
|
||||
:external "mv"
|
||||
:show-usage
|
||||
:usage "[OPTION]... SOURCE DEST
|
||||
or: mv [OPTION]... SOURCE... DIRECTORY
|
||||
Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
|
||||
\[OPTION] DIRECTORY...")
|
||||
(let ((no-dereference t))
|
||||
(eshell-mvcp-template "mv" "moving" 'rename-file
|
||||
eshell-mv-interactive-query
|
||||
eshell-mv-overwrite-files))))
|
||||
|
||||
(defun eshell/cp (&rest args)
|
||||
"Implementation of cp in Lisp."
|
||||
(eshell-eval-using-options
|
||||
"cp" args
|
||||
'((?a "archive" nil archive
|
||||
"same as -dpR")
|
||||
(?d "no-dereference" nil no-dereference
|
||||
"preserve links")
|
||||
(?f "force" nil force
|
||||
"remove existing destinations, never prompt")
|
||||
(?i "interactive" nil interactive
|
||||
"request confirmation if target already exists")
|
||||
(?n "preview" nil preview
|
||||
"don't change anything on disk")
|
||||
(?p "preserve" nil preserve
|
||||
"preserve file attributes if possible")
|
||||
(?R "recursive" nil recursive
|
||||
"copy directories recursively")
|
||||
(?v "verbose" nil verbose
|
||||
"explain what is being done")
|
||||
(nil "help" nil nil "show this usage screen")
|
||||
:external "cp"
|
||||
:show-usage
|
||||
:usage "[OPTION]... SOURCE DEST
|
||||
or: cp [OPTION]... SOURCE... DIRECTORY
|
||||
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.")
|
||||
(if archive
|
||||
(setq preserve t no-dereference t recursive t))
|
||||
(eshell-mvcp-template "cp" "copying" 'copy-file
|
||||
eshell-cp-interactive-query
|
||||
eshell-cp-overwrite-files preserve)))
|
||||
|
||||
(defun eshell/ln (&rest args)
|
||||
"Implementation of ln in Lisp."
|
||||
(eshell-eval-using-options
|
||||
"ln" args
|
||||
'((?h "help" nil nil "show this usage screen")
|
||||
(?s "symbolic" nil symbolic
|
||||
"make symbolic links instead of hard links")
|
||||
(?i "interactive" nil interactive "request confirmation if target already exists")
|
||||
(?f "force" nil force "remove existing destinations, never prompt")
|
||||
(?n "preview" nil preview
|
||||
"don't change anything on disk")
|
||||
(?v "verbose" nil verbose "explain what is being done")
|
||||
:external "ln"
|
||||
:show-usage
|
||||
:usage "[OPTION]... TARGET [LINK_NAME]
|
||||
or: ln [OPTION]... TARGET... DIRECTORY
|
||||
Create a link to the specified TARGET with optional LINK_NAME. If there is
|
||||
more than one TARGET, the last argument must be a directory; create links
|
||||
in DIRECTORY to each TARGET. Create hard links by default, symbolic links
|
||||
with '--symbolic'. When creating hard links, each TARGET must exist.")
|
||||
(let (target no-dereference)
|
||||
(if (> (length args) 1)
|
||||
(progn
|
||||
(setq target (car (last args)))
|
||||
(setcdr (last args 2) nil))
|
||||
(setq args nil))
|
||||
(eshell-shuffle-files "ln" "linking" args target
|
||||
(if symbolic
|
||||
'make-symbolic-link
|
||||
'add-name-to-file) nil
|
||||
(if (and (or interactive
|
||||
eshell-ln-interactive-query)
|
||||
(not force))
|
||||
1 (or force eshell-ln-overwrite-files))))
|
||||
nil))
|
||||
|
||||
(defun eshell/cat (&rest args)
|
||||
"Implementation of cat in Lisp."
|
||||
(if eshell-in-pipeline-p
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command "*cat" args))
|
||||
(eshell-init-print-buffer)
|
||||
(eshell-eval-using-options
|
||||
"cat" args
|
||||
'((?h "help" nil nil "show this usage screen")
|
||||
:external "cat"
|
||||
:show-usage
|
||||
:usage "[OPTION] FILE...
|
||||
Concatenate FILE(s), or standard input, to standard output.")
|
||||
(eshell-for file args
|
||||
(if (string= file "-")
|
||||
(throw 'eshell-external
|
||||
(eshell-external-command "cat" args))))
|
||||
(let ((curbuf (current-buffer)))
|
||||
(eshell-for file args
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(goto-char (point-min))
|
||||
(while (not (eobp))
|
||||
(let ((str (buffer-substring
|
||||
(point) (min (1+ (line-end-position))
|
||||
(point-max)))))
|
||||
(with-current-buffer curbuf
|
||||
(eshell-buffered-print str)))
|
||||
(forward-line)))))
|
||||
(eshell-flush)
|
||||
;; if the file does not end in a newline, do not emit one
|
||||
(setq eshell-ensure-newline-p nil))))
|
||||
|
||||
;; special front-end functions for compilation-mode buffers
|
||||
|
||||
(defun eshell/make (&rest args)
|
||||
"Use `compile' to do background makes."
|
||||
(if (and eshell-current-subjob-p
|
||||
(eshell-interactive-output-p))
|
||||
(let ((compilation-process-setup-function
|
||||
(list 'lambda nil
|
||||
(list 'setq 'process-environment
|
||||
(list 'quote (eshell-copy-environment))))))
|
||||
(compile (concat "make " (eshell-flatten-and-stringify args))))
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command "*make" args))))
|
||||
|
||||
(defun eshell-occur-mode-goto-occurrence ()
|
||||
"Go to the occurrence the current line describes."
|
||||
(interactive)
|
||||
(let ((pos (occur-mode-find-occurrence)))
|
||||
(pop-to-buffer (marker-buffer pos))
|
||||
(goto-char (marker-position pos))))
|
||||
|
||||
(defun eshell-occur-mode-mouse-goto (event)
|
||||
"In Occur mode, go to the occurrence whose line you click on."
|
||||
(interactive "e")
|
||||
(let (buffer pos)
|
||||
(save-excursion
|
||||
(set-buffer (window-buffer (posn-window (event-end event))))
|
||||
(save-excursion
|
||||
(goto-char (posn-point (event-end event)))
|
||||
(setq pos (occur-mode-find-occurrence))
|
||||
(setq buffer occur-buffer)))
|
||||
(pop-to-buffer (marker-buffer pos))
|
||||
(goto-char (marker-position pos))))
|
||||
|
||||
(defun eshell-poor-mans-grep (args)
|
||||
"A poor version of grep that opens every file and uses `occur'.
|
||||
This eats up memory, since it leaves the buffers open (to speed future
|
||||
searches), and it's very slow. But, if your system has no grep
|
||||
available..."
|
||||
(save-selected-window
|
||||
(let ((default-dir default-directory))
|
||||
(with-current-buffer (get-buffer-create "*grep*")
|
||||
(let ((inhibit-read-only t)
|
||||
(default-directory default-dir))
|
||||
(erase-buffer)
|
||||
(occur-mode)
|
||||
(let ((files (eshell-flatten-list (cdr args)))
|
||||
(inhibit-redisplay t)
|
||||
string)
|
||||
(when (car args)
|
||||
(if (get-buffer "*Occur*")
|
||||
(kill-buffer (get-buffer "*Occur*")))
|
||||
(setq string nil)
|
||||
(while files
|
||||
(with-current-buffer (find-file-noselect (car files))
|
||||
(save-excursion
|
||||
(ignore-errors
|
||||
(occur (car args))))
|
||||
(if (get-buffer "*Occur*")
|
||||
(with-current-buffer (get-buffer "*Occur*")
|
||||
(setq string (buffer-string))
|
||||
(kill-buffer (current-buffer)))))
|
||||
(if string (insert string))
|
||||
(setq string nil
|
||||
files (cdr files)))))
|
||||
(setq occur-buffer (current-buffer))
|
||||
(local-set-key [mouse-2] 'eshell-occur-mode-mouse-goto)
|
||||
(local-set-key [(control ?c) (control ?c)]
|
||||
'eshell-occur-mode-goto-occurrence)
|
||||
(local-set-key [(control ?m)]
|
||||
'eshell-occur-mode-goto-occurrence)
|
||||
(local-set-key [return] 'eshell-occur-mode-goto-occurrence)
|
||||
(pop-to-buffer (current-buffer) t)
|
||||
(goto-char (point-min))
|
||||
(resize-temp-buffer-window))))))
|
||||
|
||||
(defun eshell-grep (command args &optional maybe-use-occur)
|
||||
"Generic service function for the various grep aliases.
|
||||
It calls Emacs' grep utility if the command is not redirecting output,
|
||||
and if it's not part of a command pipeline. Otherwise, it calls the
|
||||
external command."
|
||||
(if (and maybe-use-occur eshell-no-grep-available)
|
||||
(eshell-poor-mans-grep args)
|
||||
(if (or eshell-plain-grep-behavior
|
||||
(not (and (eshell-interactive-output-p)
|
||||
(not eshell-in-pipeline-p)
|
||||
(not eshell-in-subcommand-p))))
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command (concat "*" command) args))
|
||||
(let* ((compilation-process-setup-function
|
||||
(list 'lambda nil
|
||||
(list 'setq 'process-environment
|
||||
(list 'quote (eshell-copy-environment)))))
|
||||
(args (mapconcat 'identity
|
||||
(mapcar 'shell-quote-argument
|
||||
(eshell-flatten-list args))
|
||||
" "))
|
||||
(cmd (progn
|
||||
(set-text-properties 0 (length args)
|
||||
'(invisible t) args)
|
||||
(format "%s -n %s" command args)))
|
||||
compilation-scroll-output)
|
||||
(grep cmd)))))
|
||||
|
||||
(defun eshell/grep (&rest args)
|
||||
"Use Emacs grep facility instead of calling external grep."
|
||||
(eshell-grep "grep" args t))
|
||||
|
||||
(defun eshell/egrep (&rest args)
|
||||
"Use Emacs grep facility instead of calling external egrep."
|
||||
(eshell-grep "egrep" args t))
|
||||
|
||||
(defun eshell/fgrep (&rest args)
|
||||
"Use Emacs grep facility instead of calling external fgrep."
|
||||
(eshell-grep "fgrep" args t))
|
||||
|
||||
(defun eshell/agrep (&rest args)
|
||||
"Use Emacs grep facility instead of calling external agrep."
|
||||
(eshell-grep "agrep" args))
|
||||
|
||||
(defun eshell/glimpse (&rest args)
|
||||
"Use Emacs grep facility instead of calling external glimpse."
|
||||
(let (null-device)
|
||||
(eshell-grep "glimpse" (append '("-z" "-y") args))))
|
||||
|
||||
;; completions rules for some common UNIX commands
|
||||
|
||||
(defsubst eshell-complete-hostname ()
|
||||
"Complete a command that wants a hostname for an argument."
|
||||
(pcomplete-here (eshell-read-host-names)))
|
||||
|
||||
(defun eshell-complete-host-reference ()
|
||||
"If there is a host reference, complete it."
|
||||
(let ((arg (pcomplete-actual-arg))
|
||||
index)
|
||||
(when (setq index (string-match "@[a-z.]*\\'" arg))
|
||||
(setq pcomplete-stub (substring arg (1+ index))
|
||||
pcomplete-last-completion-raw t)
|
||||
(throw 'pcomplete-completions (eshell-read-host-names)))))
|
||||
|
||||
(defalias 'pcomplete/ftp 'eshell-complete-hostname)
|
||||
(defalias 'pcomplete/ncftp 'eshell-complete-hostname)
|
||||
(defalias 'pcomplete/ping 'eshell-complete-hostname)
|
||||
(defalias 'pcomplete/rlogin 'eshell-complete-hostname)
|
||||
|
||||
(defun pcomplete/telnet ()
|
||||
(require 'pcmpl-unix)
|
||||
(pcomplete-opt "xl(pcmpl-unix-user-names)")
|
||||
(eshell-complete-hostname))
|
||||
|
||||
(defun pcomplete/rsh ()
|
||||
"Complete `rsh', which, after the user and hostname, is like xargs."
|
||||
(require 'pcmpl-unix)
|
||||
(pcomplete-opt "l(pcmpl-unix-user-names)")
|
||||
(eshell-complete-hostname)
|
||||
(pcomplete-here (funcall pcomplete-command-completion-function))
|
||||
(funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
|
||||
pcomplete-default-completion-function)))
|
||||
|
||||
(defalias 'pcomplete/ssh 'pcomplete/rsh)
|
||||
|
||||
(eval-when-compile
|
||||
(defvar block-size)
|
||||
(defvar by-bytes)
|
||||
(defvar dereference-links)
|
||||
(defvar grand-total)
|
||||
(defvar human-readable)
|
||||
(defvar max-depth)
|
||||
(defvar only-one-filesystem)
|
||||
(defvar show-all))
|
||||
|
||||
(defsubst eshell-du-size-string (size)
|
||||
(let* ((str (eshell-printable-size size human-readable block-size t))
|
||||
(len (length str)))
|
||||
(concat str (if (< len 8)
|
||||
(make-string (- 8 len) ? )))))
|
||||
|
||||
(defun eshell-du-sum-directory (path depth)
|
||||
"Summarize PATH, and its member directories."
|
||||
(let ((entries (eshell-directory-files-and-attributes path))
|
||||
(size 0.0))
|
||||
(while entries
|
||||
(unless (string-match "\\`\\.\\.?\\'" (caar entries))
|
||||
(let* ((entry (concat path (char-to-string directory-sep-char)
|
||||
(caar entries)))
|
||||
(symlink (and (stringp (cadr (car entries)))
|
||||
(cadr (car entries)))))
|
||||
(unless (or (and symlink (not dereference-links))
|
||||
(and only-one-filesystem
|
||||
(not (= only-one-filesystem
|
||||
(nth 12 (car entries))))))
|
||||
(if symlink
|
||||
(setq entry symlink))
|
||||
(setq size
|
||||
(+ size
|
||||
(if (eq t (cadr (car entries)))
|
||||
(eshell-du-sum-directory entry (1+ depth))
|
||||
(let ((file-size (nth 8 (car entries))))
|
||||
(prog1
|
||||
file-size
|
||||
(if show-all
|
||||
(eshell-print
|
||||
(concat (eshell-du-size-string file-size)
|
||||
entry "\n")))))))))))
|
||||
(setq entries (cdr entries)))
|
||||
(if (or (not max-depth)
|
||||
(= depth max-depth)
|
||||
(= depth 0))
|
||||
(eshell-print (concat (eshell-du-size-string size)
|
||||
(directory-file-name path) "\n")))
|
||||
size))
|
||||
|
||||
(defun eshell/du (&rest args)
|
||||
"Implementation of \"du\" in Lisp, passing RAGS."
|
||||
(if (eshell-search-path "du")
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command "*du" args))
|
||||
(eshell-eval-using-options
|
||||
"du" args
|
||||
'((?a "all" nil show-all
|
||||
"write counts for all files, not just directories")
|
||||
(nil "block-size" t block-size
|
||||
"use SIZE-byte blocks (i.e., --block-size SIZE)")
|
||||
(?b "bytes" nil by-bytes
|
||||
"print size in bytes")
|
||||
(?c "total" nil grand-total
|
||||
"produce a grand total")
|
||||
(?d "max-depth" t max-depth
|
||||
"display data only this many levels of data")
|
||||
(?h "human-readable" 1024 human-readable
|
||||
"print sizes in human readable format")
|
||||
(?H "is" 1000 human-readable
|
||||
"likewise, but use powers of 1000 not 1024")
|
||||
(?k "kilobytes" 1024 block-size
|
||||
"like --block-size 1024")
|
||||
(?L "dereference" nil dereference-links
|
||||
"dereference all symbolic links")
|
||||
(?m "megabytes" 1048576 block-size
|
||||
"like --block-size 1048576")
|
||||
(?s "summarize" 0 max-depth
|
||||
"display only a total for each argument")
|
||||
(?x "one-file-system" nil only-one-filesystem
|
||||
"skip directories on different filesystems")
|
||||
(nil "help" nil nil
|
||||
"show this usage screen")
|
||||
:external "du"
|
||||
:usage "[OPTION]... FILE...
|
||||
Summarize disk usage of each FILE, recursively for directories.")
|
||||
(unless by-bytes
|
||||
(setq block-size (or block-size 1024)))
|
||||
(if (and max-depth (stringp max-depth))
|
||||
(setq max-depth (string-to-int max-depth)))
|
||||
;; filesystem support means nothing under Windows
|
||||
(if (eshell-under-windows-p)
|
||||
(setq only-one-filesystem nil))
|
||||
(unless args
|
||||
(setq args '(".")))
|
||||
(let ((size 0.0))
|
||||
(while args
|
||||
(if only-one-filesystem
|
||||
(setq only-one-filesystem
|
||||
(nth 11 (file-attributes
|
||||
(file-name-as-directory (car args))))))
|
||||
(setq size (+ size (eshell-du-sum-directory
|
||||
(directory-file-name (car args)) 0)))
|
||||
(setq args (cdr args)))
|
||||
(if grand-total
|
||||
(eshell-print (concat (eshell-du-size-string size)
|
||||
"total\n")))))))
|
||||
|
||||
(defvar eshell-time-start nil)
|
||||
|
||||
(defun eshell-show-elapsed-time ()
|
||||
(let ((elapsed (format "%.3f secs\n"
|
||||
(- (eshell-time-to-seconds (current-time))
|
||||
eshell-time-start))))
|
||||
(set-text-properties 0 (length elapsed) '(face bold) elapsed)
|
||||
(eshell-interactive-print elapsed))
|
||||
(remove-hook 'eshell-post-command-hook 'eshell-show-elapsed-time t))
|
||||
|
||||
(defun eshell/time (&rest args)
|
||||
"Implementation of \"time\" in Lisp."
|
||||
(let ((time-args (copy-alist args))
|
||||
(continue t)
|
||||
last-arg)
|
||||
(while (and continue args)
|
||||
(if (not (string-match "^-" (car args)))
|
||||
(progn
|
||||
(if last-arg
|
||||
(setcdr last-arg nil)
|
||||
(setq args '("")))
|
||||
(setq continue nil))
|
||||
(setq last-arg args
|
||||
args (cdr args))))
|
||||
(eshell-eval-using-options
|
||||
"time" args
|
||||
'((?h "help" nil nil "show this usage screen")
|
||||
:external "time"
|
||||
:show-usage
|
||||
:usage "COMMAND...
|
||||
Show wall-clock time elapsed during execution of COMMAND.")
|
||||
(setq eshell-time-start (eshell-time-to-seconds (current-time)))
|
||||
(add-hook 'eshell-post-command-hook 'eshell-show-elapsed-time nil t)
|
||||
;; after setting
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command (car time-args) (cdr time-args))))))
|
||||
|
||||
(defalias 'eshell/whoami 'user-login-name)
|
||||
|
||||
(defvar eshell-diff-window-config nil)
|
||||
|
||||
(defun eshell-diff-quit ()
|
||||
"Restore the window configuration previous to diff'ing."
|
||||
(interactive)
|
||||
(if eshell-diff-window-config
|
||||
(set-window-configuration eshell-diff-window-config)))
|
||||
|
||||
(defun eshell/diff (&rest args)
|
||||
"Alias \"diff\" to call Emacs `diff' function."
|
||||
(if (or eshell-plain-diff-behavior
|
||||
(not (and (eshell-interactive-output-p)
|
||||
(not eshell-in-pipeline-p)
|
||||
(not eshell-in-subcommand-p))))
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command "*diff" args))
|
||||
(setq args (eshell-flatten-list args))
|
||||
(if (< (length args) 2)
|
||||
(error "diff: missing operand"))
|
||||
(let ((old (car (last args 2)))
|
||||
(new (car (last args)))
|
||||
(config (current-window-configuration)))
|
||||
(if (= (length args) 2)
|
||||
(setq args nil)
|
||||
(setcdr (last args 3) nil))
|
||||
(with-current-buffer
|
||||
(diff old new (eshell-flatten-and-stringify args))
|
||||
(when (fboundp 'diff-mode)
|
||||
(diff-mode)
|
||||
(set (make-local-variable 'eshell-diff-window-config) config)
|
||||
(local-set-key [?q] 'eshell-diff-quit)
|
||||
(if (fboundp 'turn-on-font-lock-if-enabled)
|
||||
(turn-on-font-lock-if-enabled))))
|
||||
(other-window 1)
|
||||
(goto-char (point-min))
|
||||
nil)))
|
||||
|
||||
(defun eshell/locate (&rest args)
|
||||
"Alias \"locate\" to call Emacs `locate' function."
|
||||
(if (or eshell-plain-locate-behavior
|
||||
(not (and (eshell-interactive-output-p)
|
||||
(not eshell-in-pipeline-p)
|
||||
(not eshell-in-subcommand-p)))
|
||||
(and (stringp (car args))
|
||||
(string-match "^-" (car args))))
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command "*locate" args))
|
||||
(save-selected-window
|
||||
(let ((locate-history-list (list (car args))))
|
||||
(locate-with-filter (car args) (cadr args))))))
|
||||
|
||||
(defun eshell/occur (&rest args)
|
||||
"Alias \"occur\" to call Emacs `occur' function."
|
||||
(let ((inhibit-read-only t))
|
||||
(if args
|
||||
(error "usage: occur: (REGEXP)")
|
||||
(occur (car args)))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-unix.el ends here
|
119
lisp/eshell/em-xtra.el
Normal file
119
lisp/eshell/em-xtra.el
Normal file
@ -0,0 +1,119 @@
|
||||
;;; em-xtra --- extra alias functions
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'em-xtra)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-xtra nil
|
||||
"This module defines some extra alias functions which are entirely
|
||||
optional. They can be viewed as samples for how to write Eshell alias
|
||||
functions, or as aliases which make some of Emacs' behavior more
|
||||
naturally accessible within Emacs."
|
||||
:tag "Extra alias functions"
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
(require 'compile)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell/expr (&rest args)
|
||||
"Implementation of expr, using the calc package."
|
||||
(if (not (fboundp 'calc-eval))
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command "*expr" args))
|
||||
;; to fool the byte-compiler...
|
||||
(let ((func 'calc-eval))
|
||||
(funcall func (eshell-flatten-and-stringify args)))))
|
||||
|
||||
(defun eshell/substitute (&rest args)
|
||||
"Easy front-end to `intersection', for comparing lists of strings."
|
||||
(apply 'substitute (car args) (cadr args) :test 'equal
|
||||
(cddr args)))
|
||||
|
||||
(defun eshell/count (&rest args)
|
||||
"Easy front-end to `intersection', for comparing lists of strings."
|
||||
(apply 'count (car args) (cadr args) :test 'equal
|
||||
(cddr args)))
|
||||
|
||||
(defun eshell/mismatch (&rest args)
|
||||
"Easy front-end to `intersection', for comparing lists of strings."
|
||||
(apply 'mismatch (car args) (cadr args) :test 'equal
|
||||
(cddr args)))
|
||||
|
||||
(defun eshell/union (&rest args)
|
||||
"Easy front-end to `intersection', for comparing lists of strings."
|
||||
(apply 'union (car args) (cadr args) :test 'equal
|
||||
(cddr args)))
|
||||
|
||||
(defun eshell/intersection (&rest args)
|
||||
"Easy front-end to `intersection', for comparing lists of strings."
|
||||
(apply 'intersection (car args) (cadr args) :test 'equal
|
||||
(cddr args)))
|
||||
|
||||
(defun eshell/set-difference (&rest args)
|
||||
"Easy front-end to `intersection', for comparing lists of strings."
|
||||
(apply 'set-difference (car args) (cadr args) :test 'equal
|
||||
(cddr args)))
|
||||
|
||||
(defun eshell/set-exclusive-or (&rest args)
|
||||
"Easy front-end to `intersection', for comparing lists of strings."
|
||||
(apply 'set-exclusive-or (car args) (cadr args) :test 'equal
|
||||
(cddr args)))
|
||||
|
||||
(defalias 'eshell/ff 'find-name-dired)
|
||||
(defalias 'eshell/gf 'find-grep-dired)
|
||||
|
||||
(defun pcomplete/bcc32 ()
|
||||
"Completion function for Borland's C++ compiler."
|
||||
(let ((cur (pcomplete-arg 0)))
|
||||
(cond
|
||||
((string-match "\\`-w\\([^;]+;\\)*\\([^;]*\\)\\'" cur)
|
||||
(pcomplete-here
|
||||
'("ali" "amb" "amp" "asc" "asm" "aus" "bbf" "bei" "big" "ccc"
|
||||
"cln" "cod" "com" "cpt" "csu" "def" "dig" "dpu" "dsz" "dup"
|
||||
"eas" "eff" "ext" "hch" "hid" "ias" "ibc" "ifr" "ill" "nil"
|
||||
"lin" "lvc" "mcs" "mes" "mpc" "mpd" "msg" "nak" "ncf" "nci"
|
||||
"ncl" "nfd" "ngu" "nin" "nma" "nmu" "nod" "nop" "npp" "nsf"
|
||||
"nst" "ntd" "nto" "nvf" "obi" "obs" "ofp" "osh" "ovf" "par"
|
||||
"pch" "pck" "pia" "pin" "pow" "prc" "pre" "pro" "rch" "ret"
|
||||
"rng" "rpt" "rvl" "sig" "spa" "stl" "stu" "stv" "sus" "tai"
|
||||
"tes" "thr" "ucp" "use" "voi" "zdi") (match-string 2 cur)))
|
||||
((string-match "\\`-[LIn]\\([^;]+;\\)*\\([^;]*\\)\\'" cur)
|
||||
(pcomplete-here (pcomplete-dirs) (match-string 2 cur)))
|
||||
((string-match "\\`-[Ee]\\(.*\\)\\'" cur)
|
||||
(pcomplete-here (pcomplete-dirs-or-entries "\\.[Ee][Xx][Ee]\\'")
|
||||
(match-string 1 cur)))
|
||||
((string-match "\\`-o\\(.*\\)\\'" cur)
|
||||
(pcomplete-here (pcomplete-dirs-or-entries "\\.[Oo][Bb][Jj]\\'")
|
||||
(match-string 1 cur)))
|
||||
(t
|
||||
(pcomplete-opt "3456ABCDEHIKLMNOPRSTUVXabcdefgijklnoptuvwxyz"))))
|
||||
(while (pcomplete-here
|
||||
(pcomplete-dirs-or-entries "\\.[iCc]\\([Pp][Pp]\\)?\\'"))))
|
||||
|
||||
(defalias 'pcomplete/bcc 'pcomplete/bcc32)
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; em-xtra.el ends here
|
383
lisp/eshell/esh-arg.el
Normal file
383
lisp/eshell/esh-arg.el
Normal file
@ -0,0 +1,383 @@
|
||||
;;; esh-arg --- argument processing
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'esh-arg)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-arg nil
|
||||
"Argument parsing involves transforming the arguments passed on the
|
||||
command line into equivalent Lisp forms that, when evaluated, will
|
||||
yield the values intended."
|
||||
:tag "Argument parsing"
|
||||
:group 'eshell)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Parsing of arguments can be extended by adding functions to the
|
||||
;; hook `eshell-parse-argument-hook'. For a good example of this, see
|
||||
;; `eshell-parse-drive-letter', defined in eshell-dirs.el.
|
||||
|
||||
(defcustom eshell-parse-argument-hook
|
||||
(list
|
||||
;; a term such as #<buffer NAME>, or #<process NAME> is a buffer
|
||||
;; or process reference
|
||||
'eshell-parse-special-reference
|
||||
|
||||
;; numbers convert to numbers if they stand alone
|
||||
(function
|
||||
(lambda ()
|
||||
(when (and (not eshell-current-argument)
|
||||
(not eshell-current-quoted)
|
||||
(looking-at eshell-number-regexp)
|
||||
(eshell-arg-delimiter (match-end 0)))
|
||||
(goto-char (match-end 0))
|
||||
(string-to-number (match-string 0)))))
|
||||
|
||||
;; parse any non-special characters, based on the current context
|
||||
(function
|
||||
(lambda ()
|
||||
(unless eshell-inside-quote-regexp
|
||||
(setq eshell-inside-quote-regexp
|
||||
(format "[^%s]+"
|
||||
(apply 'string eshell-special-chars-inside-quoting))))
|
||||
(unless eshell-outside-quote-regexp
|
||||
(setq eshell-outside-quote-regexp
|
||||
(format "[^%s]+"
|
||||
(apply 'string eshell-special-chars-outside-quoting))))
|
||||
(when (looking-at (if eshell-current-quoted
|
||||
eshell-inside-quote-regexp
|
||||
eshell-outside-quote-regexp))
|
||||
(goto-char (match-end 0))
|
||||
(let ((str (match-string 0)))
|
||||
(if str
|
||||
(set-text-properties 0 (length str) nil str))
|
||||
str))))
|
||||
|
||||
;; whitespace or a comment is an argument delimiter
|
||||
(function
|
||||
(lambda ()
|
||||
(let (comment-p)
|
||||
(when (or (looking-at "[ \t]+")
|
||||
(and (not eshell-current-argument)
|
||||
(looking-at "#\\([^<'].*\\|$\\)")
|
||||
(setq comment-p t)))
|
||||
(if comment-p
|
||||
(add-text-properties (match-beginning 0) (match-end 0)
|
||||
'(comment t)))
|
||||
(goto-char (match-end 0))
|
||||
(eshell-finish-arg)))))
|
||||
|
||||
;; backslash before a special character means escape it
|
||||
'eshell-parse-backslash
|
||||
|
||||
;; text beginning with ' is a literally quoted
|
||||
'eshell-parse-literal-quote
|
||||
|
||||
;; text beginning with " is interpolably quoted
|
||||
'eshell-parse-double-quote
|
||||
|
||||
;; argument delimiter
|
||||
'eshell-parse-delimiter)
|
||||
"*Define how to process Eshell command line arguments.
|
||||
When each function on this hook is called, point will be at the
|
||||
current position within the argument list. The function should either
|
||||
return nil, meaning that it did no argument parsing, or it should
|
||||
return the result of the parse as a sexp. It is also responsible for
|
||||
moving the point forward to reflect the amount of input text that was
|
||||
parsed.
|
||||
|
||||
If no function handles the current character at point, it will be
|
||||
treated as a literal character."
|
||||
:type 'hook
|
||||
:group 'eshell-arg)
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-arg-load-hook '(eshell-arg-initialize)
|
||||
"*A hook that gets run when `eshell-arg' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-arg)
|
||||
|
||||
(defcustom eshell-delimiter-argument-list '(?\; ?& ?\| ?\> ? ?\t ?\n)
|
||||
"List of characters to recognize as argument separators."
|
||||
:type '(repeat character)
|
||||
:group 'eshell-arg)
|
||||
|
||||
(defcustom eshell-special-chars-inside-quoting '(?\\ ?\")
|
||||
"*Characters which are still special inside double quotes."
|
||||
:type '(repeat character)
|
||||
:group 'eshell-arg)
|
||||
|
||||
(defcustom eshell-special-chars-outside-quoting
|
||||
(append eshell-delimiter-argument-list '(?# ?! ?\\ ?\" ?\'))
|
||||
"*Characters that require escaping outside of double quotes.
|
||||
Without escaping them, they will introduce a change in the argument."
|
||||
:type '(repeat character)
|
||||
:group 'eshell-arg)
|
||||
|
||||
;;; Internal Variables:
|
||||
|
||||
(defvar eshell-current-argument nil)
|
||||
(defvar eshell-current-modifiers nil)
|
||||
(defvar eshell-arg-listified nil)
|
||||
(defvar eshell-nested-argument nil)
|
||||
(defvar eshell-current-quoted nil)
|
||||
(defvar eshell-inside-quote-regexp nil)
|
||||
(defvar eshell-outside-quote-regexp nil)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-arg-initialize ()
|
||||
"Initialize the argument parsing code."
|
||||
(define-key eshell-command-map [(meta ?b)] 'eshell-insert-buffer-name)
|
||||
(set (make-local-variable 'eshell-inside-quote-regexp) nil)
|
||||
(set (make-local-variable 'eshell-outside-quote-regexp) nil))
|
||||
|
||||
(defun eshell-insert-buffer-name (buffer-name)
|
||||
"Insert BUFFER-NAME into the current buffer at point."
|
||||
(interactive "BName of buffer: ")
|
||||
(insert-and-inherit "#<buffer " buffer-name ">"))
|
||||
|
||||
(defsubst eshell-escape-arg (string)
|
||||
"Return STRING with the `escaped' property on it."
|
||||
(if (stringp string)
|
||||
(add-text-properties 0 (length string) '(escaped t) string))
|
||||
string)
|
||||
|
||||
(defun eshell-resolve-current-argument ()
|
||||
"If there are pending modifications to be made, make them now."
|
||||
(when eshell-current-argument
|
||||
(when eshell-arg-listified
|
||||
(let ((parts eshell-current-argument))
|
||||
(while parts
|
||||
(unless (stringp (car parts))
|
||||
(setcar parts
|
||||
(list 'eshell-to-flat-string (car parts))))
|
||||
(setq parts (cdr parts)))
|
||||
(setq eshell-current-argument
|
||||
(list 'eshell-convert
|
||||
(append (list 'concat) eshell-current-argument))))
|
||||
(setq eshell-arg-listified nil))
|
||||
(while eshell-current-modifiers
|
||||
(setq eshell-current-argument
|
||||
(list (car eshell-current-modifiers) eshell-current-argument)
|
||||
eshell-current-modifiers (cdr eshell-current-modifiers))))
|
||||
(setq eshell-current-modifiers nil))
|
||||
|
||||
(defun eshell-finish-arg (&optional argument)
|
||||
"Finish the current argument being processed."
|
||||
(if argument
|
||||
(setq eshell-current-argument argument))
|
||||
(throw 'eshell-arg-done t))
|
||||
|
||||
(defsubst eshell-arg-delimiter (&optional pos)
|
||||
"Return non-nil if POS is an argument delimiter.
|
||||
If POS is nil, the location of point is checked."
|
||||
(let ((pos (or pos (point))))
|
||||
(or (= pos (point-max))
|
||||
(memq (char-after pos) eshell-delimiter-argument-list))))
|
||||
|
||||
;; Argument parsing
|
||||
|
||||
(defun eshell-parse-arguments (beg end)
|
||||
"Parse all of the arguments at point from BEG to END.
|
||||
Returns the list of arguments in their raw form.
|
||||
Point is left at the end of the arguments."
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(goto-char beg)
|
||||
(narrow-to-region beg end)
|
||||
(let ((inhibit-point-motion-hooks t)
|
||||
(args (list t))
|
||||
after-change-functions
|
||||
delim)
|
||||
(remove-text-properties (point-min) (point-max)
|
||||
'(arg-begin nil arg-end nil))
|
||||
(if (setq
|
||||
delim
|
||||
(catch 'eshell-incomplete
|
||||
(while (not (eobp))
|
||||
(let* ((here (point))
|
||||
(arg (eshell-parse-argument)))
|
||||
(if (= (point) here)
|
||||
(error "Failed to parse argument '%s'"
|
||||
(buffer-substring here (point-max))))
|
||||
(and arg (nconc args (list arg)))))))
|
||||
(if (listp delim)
|
||||
(throw 'eshell-incomplete delim)
|
||||
(throw 'eshell-incomplete
|
||||
(list delim (point) (cdr args)))))
|
||||
(cdr args)))))
|
||||
|
||||
(defun eshell-parse-argument ()
|
||||
"Get the next argument. Leave point after it."
|
||||
(let* ((outer (null eshell-nested-argument))
|
||||
(arg-begin (and outer (point)))
|
||||
(eshell-nested-argument t)
|
||||
eshell-current-argument
|
||||
eshell-current-modifiers
|
||||
eshell-arg-listified)
|
||||
(catch 'eshell-arg-done
|
||||
(while (not (eobp))
|
||||
(let ((result
|
||||
(or (run-hook-with-args-until-success
|
||||
'eshell-parse-argument-hook)
|
||||
(prog1
|
||||
(char-to-string (char-after))
|
||||
(forward-char)))))
|
||||
(if (not eshell-current-argument)
|
||||
(setq eshell-current-argument result)
|
||||
(unless eshell-arg-listified
|
||||
(setq eshell-current-argument
|
||||
(list eshell-current-argument)
|
||||
eshell-arg-listified t))
|
||||
(nconc eshell-current-argument (list result))))))
|
||||
(when (and outer eshell-current-argument)
|
||||
(add-text-properties arg-begin (1+ arg-begin)
|
||||
'(arg-begin t rear-nonsticky
|
||||
(arg-begin arg-end)))
|
||||
(add-text-properties (1- (point)) (point)
|
||||
'(arg-end t rear-nonsticky
|
||||
(arg-end arg-begin))))
|
||||
(eshell-resolve-current-argument)
|
||||
eshell-current-argument))
|
||||
|
||||
(defsubst eshell-operator (&rest args)
|
||||
"A stub function that generates an error if a floating operator is found."
|
||||
(error "Unhandled operator in input text"))
|
||||
|
||||
(defsubst eshell-looking-at-backslash-return (pos)
|
||||
"Test whether a backslash-return sequence occurs at POS."
|
||||
(and (eq (char-after pos) ?\\)
|
||||
(or (= (1+ pos) (point-max))
|
||||
(and (eq (char-after (1+ pos)) ?\n)
|
||||
(= (+ pos 2) (point-max))))))
|
||||
|
||||
(defun eshell-quote-backslash (string &optional index)
|
||||
"Intelligently backslash the character occuring in STRING at INDEX.
|
||||
If the character is itself a backslash, it needs no escaping."
|
||||
(let ((char (aref string index)))
|
||||
(if (eq char ?\\)
|
||||
(char-to-string char)
|
||||
(if (memq char eshell-special-chars-outside-quoting)
|
||||
(string ?\\ char)))))
|
||||
|
||||
(defun eshell-parse-backslash ()
|
||||
"Parse a single backslash (\) character, which might mean escape.
|
||||
It only means escape if the character immediately following is a
|
||||
special character that is not itself a backslash."
|
||||
(when (eq (char-after) ?\\)
|
||||
(if (eshell-looking-at-backslash-return (point))
|
||||
(throw 'eshell-incomplete ?\\)
|
||||
(if (and (not (eq (char-after (1+ (point))) ?\\))
|
||||
(if eshell-current-quoted
|
||||
(memq (char-after (1+ (point)))
|
||||
eshell-special-chars-inside-quoting)
|
||||
(memq (char-after (1+ (point)))
|
||||
eshell-special-chars-outside-quoting)))
|
||||
(progn
|
||||
(forward-char 2)
|
||||
(list 'eshell-escape-arg
|
||||
(char-to-string (char-before))))
|
||||
;; allow \\<RET> to mean a literal "\" character followed by a
|
||||
;; normal return, rather than a backslash followed by a line
|
||||
;; continuator (i.e., "\\ + \n" rather than "\ + \\n"). This
|
||||
;; is necessary because backslashes in Eshell are not special
|
||||
;; unless they either precede something special, or precede a
|
||||
;; backslash that precedes something special. (Mainly this is
|
||||
;; done to make using backslash on Windows systems more
|
||||
;; natural-feeling).
|
||||
(if (eshell-looking-at-backslash-return (1+ (point)))
|
||||
(forward-char))
|
||||
(forward-char)
|
||||
"\\"))))
|
||||
|
||||
(defun eshell-parse-literal-quote ()
|
||||
"Parse a literally quoted string. Nothing has special meaning!"
|
||||
(if (eq (char-after) ?\')
|
||||
(let ((end (eshell-find-delimiter ?\' ?\')))
|
||||
(if (not end)
|
||||
(throw 'eshell-incomplete ?\')
|
||||
(let ((string (buffer-substring-no-properties (1+ (point)) end)))
|
||||
(goto-char (1+ end))
|
||||
(while (string-match "''" string)
|
||||
(setq string (replace-match "'" t t string)))
|
||||
(list 'eshell-escape-arg string))))))
|
||||
|
||||
(defun eshell-parse-double-quote ()
|
||||
"Parse a double quoted string, which allows for variable interpolation."
|
||||
(when (eq (char-after) ?\")
|
||||
(forward-char)
|
||||
(let* ((end (eshell-find-delimiter ?\" ?\" nil nil t))
|
||||
(eshell-current-quoted t))
|
||||
(if (not end)
|
||||
(throw 'eshell-incomplete ?\")
|
||||
(prog1
|
||||
(save-restriction
|
||||
(narrow-to-region (point) end)
|
||||
(list 'eshell-escape-arg
|
||||
(eshell-parse-argument)))
|
||||
(goto-char (1+ end)))))))
|
||||
|
||||
(defun eshell-parse-special-reference ()
|
||||
"Parse a special syntax reference, of the form '#<type arg>'."
|
||||
(if (and (not eshell-current-argument)
|
||||
(not eshell-current-quoted)
|
||||
(looking-at "#<\\(buffer\\|process\\)\\s-"))
|
||||
(let ((here (point)))
|
||||
(goto-char (match-end 0))
|
||||
(let* ((buffer-p (string= (match-string 1) "buffer"))
|
||||
(end (eshell-find-delimiter ?\< ?\>)))
|
||||
(if (not end)
|
||||
(throw 'eshell-incomplete ?\<)
|
||||
(if (eshell-arg-delimiter (1+ end))
|
||||
(prog1
|
||||
(list (if buffer-p 'get-buffer-create 'get-process)
|
||||
(buffer-substring-no-properties (point) end))
|
||||
(goto-char (1+ end)))
|
||||
(ignore (goto-char here))))))))
|
||||
|
||||
(defun eshell-parse-delimiter ()
|
||||
"Parse an argument delimiter, which is essentially a command operator."
|
||||
;; this `eshell-operator' keyword gets parsed out by
|
||||
;; `eshell-separate-commands'. Right now the only possibility for
|
||||
;; error is an incorrect output redirection specifier.
|
||||
(when (looking-at "[&|;\n]\\s-*")
|
||||
(let ((end (match-end 0)))
|
||||
(if eshell-current-argument
|
||||
(eshell-finish-arg)
|
||||
(eshell-finish-arg
|
||||
(prog1
|
||||
(list 'eshell-operator
|
||||
(cond
|
||||
((eq (char-after end) ?\&)
|
||||
(setq end (1+ end)) "&&")
|
||||
((eq (char-after end) ?\|)
|
||||
(setq end (1+ end)) "||")
|
||||
((eq (char-after) ?\n) ";")
|
||||
(t
|
||||
(char-to-string (char-after)))))
|
||||
(goto-char end)))))))
|
||||
|
||||
;;; esh-arg.el ends here
|
311
lisp/eshell/esh-ext.el
Normal file
311
lisp/eshell/esh-ext.el
Normal file
@ -0,0 +1,311 @@
|
||||
;;; esh-ext --- commands external to Eshell
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'esh-ext)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-ext nil
|
||||
"External commands are invoked when operating system executables are
|
||||
loaded into memory, thus beginning a new process."
|
||||
:tag "External commands"
|
||||
:group 'eshell)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; To force a command to invoked external, either provide an explicit
|
||||
;; pathname for the command argument, or prefix the command name with
|
||||
;; an asterix character. Example:
|
||||
;;
|
||||
;; grep ; make invoke `grep' Lisp function, or `eshell/grep'
|
||||
;; /bin/grep ; will definitely invoke /bin/grep
|
||||
;; *grep ; will also invoke /bin/grep
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-ext-load-hook '(eshell-ext-initialize)
|
||||
"*A hook that gets run when `eshell-ext' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-ext)
|
||||
|
||||
(defcustom eshell-binary-suffixes
|
||||
(if (eshell-under-windows-p)
|
||||
'(".exe" ".com" ".bat" ".cmd" "")
|
||||
'(""))
|
||||
"*A list of suffixes used when searching for executable files."
|
||||
:type '(repeat string)
|
||||
:group 'eshell-ext)
|
||||
|
||||
(defcustom eshell-force-execution nil
|
||||
"*If non-nil, try to execute binary files regardless of permissions.
|
||||
This can be useful on systems like Windows, where the operating system
|
||||
doesn't happen to honor the permission bits in certain cases; or in
|
||||
cases where you want to associate an interpreter with a particular
|
||||
kind of script file, but the language won't let you but a '#!'
|
||||
interpreter line in the file, and you don't want to make it executable
|
||||
since nothing else but Eshell will be able to understand
|
||||
`eshell-interpreter-alist'."
|
||||
:type 'boolean
|
||||
:group 'eshell-ext)
|
||||
|
||||
(defun eshell-search-path (name)
|
||||
"Search the environment path for NAME."
|
||||
(if (file-name-absolute-p name)
|
||||
name
|
||||
(let ((list (parse-colon-path (getenv "PATH")))
|
||||
suffixes n1 n2 file)
|
||||
(while list
|
||||
(setq n1 (concat (car list) name))
|
||||
(setq suffixes eshell-binary-suffixes)
|
||||
(while suffixes
|
||||
(setq n2 (concat n1 (car suffixes)))
|
||||
(if (and (or (file-executable-p n2)
|
||||
(and eshell-force-execution
|
||||
(file-readable-p n2)))
|
||||
(not (file-directory-p n2)))
|
||||
(setq file n2 suffixes nil list nil))
|
||||
(setq suffixes (cdr suffixes)))
|
||||
(setq list (cdr list)))
|
||||
file)))
|
||||
|
||||
(defcustom eshell-windows-shell-file
|
||||
(if (eshell-under-windows-p)
|
||||
(if (string-match "\\(\\`cmdproxy\\|sh\\)\\.\\(com\\|exe\\)"
|
||||
shell-file-name)
|
||||
(or (eshell-search-path "cmd.exe")
|
||||
(eshell-search-path "command.exe"))
|
||||
shell-file-name))
|
||||
"*The name of the shell command to use for DOS/Windows batch files.
|
||||
This defaults to nil on non-Windows systems, where this variable is
|
||||
wholly ignored."
|
||||
:type 'file
|
||||
:group 'eshell-ext)
|
||||
|
||||
(defsubst eshell-invoke-batch-file (&rest args)
|
||||
"Invoke a .BAT or .CMD file on DOS/Windows systems."
|
||||
;; since CMD.EXE can't handle forward slashes in the initial
|
||||
;; argument...
|
||||
(setcar args (subst-char-in-string directory-sep-char
|
||||
?\\ (car args)))
|
||||
(throw 'eshell-replace-command
|
||||
(eshell-parse-command eshell-windows-shell-file
|
||||
(cons "/c" args))))
|
||||
|
||||
(defcustom eshell-interpreter-alist
|
||||
(if (eshell-under-windows-p)
|
||||
'(("\\.\\(bat\\|cmd\\)\\'" . eshell-invoke-batch-file)))
|
||||
"*An alist defining interpreter substitutions.
|
||||
Each member is a cons cell of the form:
|
||||
|
||||
(MATCH . INTERPRETER)
|
||||
|
||||
MATCH should be a regexp, which is matched against the command name,
|
||||
or a function. If either returns a non-nil value, then INTERPRETER
|
||||
will be used for that command.
|
||||
|
||||
If INTERPRETER is a string, it will be called as the command name,
|
||||
with the original command name passed as the first argument, with all
|
||||
subsequent arguments following. If INTERPRETER is a function, it will
|
||||
be called with all of those arguments. Note that interpreter
|
||||
functions should throw `eshell-replace-command' with the alternate
|
||||
command form, or they should return a value compatible with the
|
||||
possible return values of `eshell-external-command', which see."
|
||||
:type '(repeat (cons (choice regexp (function :tag "Predicate"))
|
||||
(choice string (function :tag "Interpreter"))))
|
||||
:group 'eshell-ext)
|
||||
|
||||
(defcustom eshell-alternate-command-hook nil
|
||||
"*A hook run whenever external command lookup fails.
|
||||
If a functions wishes to provide an alternate command, they must throw
|
||||
it using the tag `eshell-replace-command'. This is done because the
|
||||
substituted command need not be external at all, and therefore must be
|
||||
passed up to a higher level for re-evaluation.
|
||||
|
||||
Or, if the function returns a filename, that filename will be invoked
|
||||
with the current command arguments rather than the command specified
|
||||
by the user on the command line."
|
||||
:type 'hook
|
||||
:group 'eshell-ext)
|
||||
|
||||
(defcustom eshell-command-interpreter-max-length 256
|
||||
"*The maximum length of any command interpreter string, plus args."
|
||||
:type 'integer
|
||||
:group 'eshell-ext)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-ext-initialize ()
|
||||
"Initialize the external command handling code."
|
||||
(make-local-hook 'eshell-named-command-hook)
|
||||
(add-hook 'eshell-named-command-hook 'eshell-explicit-command nil t))
|
||||
|
||||
(defun eshell-explicit-command (command args)
|
||||
"If a command name begins with `*', call it externally always.
|
||||
This bypasses all Lisp functions and aliases."
|
||||
(when (and (> (length command) 1)
|
||||
(eq (aref command 0) ?*))
|
||||
(let ((cmd (eshell-search-path (substring command 1))))
|
||||
(if cmd
|
||||
(or (eshell-external-command cmd args)
|
||||
(error "%s: external command failed" cmd))
|
||||
(error "%s: external command not found"
|
||||
(substring command 1))))))
|
||||
|
||||
(defun eshell-remote-command (handler command args)
|
||||
"Insert output from a remote COMMAND, using ARGS.
|
||||
A remote command is something that executes on a different machine.
|
||||
An external command simply means external to Emacs.
|
||||
|
||||
Note that this function is very crude at the moment. It gathers up
|
||||
all the output from the remote command, and sends it all at once,
|
||||
causing the user to wonder if anything's really going on..."
|
||||
(let ((outbuf (generate-new-buffer " *eshell remote output*"))
|
||||
(errbuf (generate-new-buffer " *eshell remote error*"))
|
||||
(exitcode 1))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(setq exitcode
|
||||
(funcall handler 'shell-command
|
||||
(mapconcat 'shell-quote-argument
|
||||
(append (list command) args) " ")
|
||||
outbuf errbuf))
|
||||
(eshell-print (save-excursion (set-buffer outbuf)
|
||||
(buffer-string)))
|
||||
(eshell-error (save-excursion (set-buffer errbuf)
|
||||
(buffer-string))))
|
||||
(eshell-close-handles exitcode 'nil)
|
||||
(kill-buffer outbuf)
|
||||
(kill-buffer errbuf))))
|
||||
|
||||
(defun eshell-external-command (command args)
|
||||
"Insert output from an external COMMAND, using ARGS."
|
||||
(setq args (eshell-stringify-list (eshell-flatten-list args)))
|
||||
(let ((handler
|
||||
(unless (or (equal default-directory "/")
|
||||
(and (eshell-under-windows-p)
|
||||
(string-match "\\`[A-Za-z]:[/\\\\]\\'"
|
||||
default-directory)))
|
||||
(find-file-name-handler default-directory
|
||||
'shell-command))))
|
||||
(if handler
|
||||
(eshell-remote-command handler command args))
|
||||
(let ((interp (eshell-find-interpreter command)))
|
||||
(assert interp)
|
||||
(if (functionp (car interp))
|
||||
(apply (car interp) (append (cdr interp) args))
|
||||
(eshell-gather-process-output
|
||||
(car interp) (append (cdr interp) args))))))
|
||||
|
||||
(defun eshell/addpath (&rest args)
|
||||
"Add a set of paths to PATH."
|
||||
(eshell-eval-using-options
|
||||
"addpath" args
|
||||
'((?b "begin" nil prepend "add path element at beginning")
|
||||
(?h "help" nil nil "display this usage message")
|
||||
:usage "[-b] PATH
|
||||
Adds the given PATH to $PATH.")
|
||||
(if args
|
||||
(progn
|
||||
(if prepend
|
||||
(setq args (nreverse args)))
|
||||
(while args
|
||||
(setenv "PATH"
|
||||
(if prepend
|
||||
(concat (car args) path-separator
|
||||
(getenv "PATH"))
|
||||
(concat (getenv "PATH") path-separator
|
||||
(car args))))
|
||||
(setq args (cdr args))))
|
||||
(let ((paths (parse-colon-path (getenv "PATH"))))
|
||||
(while paths
|
||||
(eshell-printn (car paths))
|
||||
(setq paths (cdr paths)))))))
|
||||
|
||||
(defun eshell-script-interpreter (file)
|
||||
"Extract the script to run from FILE, if it has #!<interp> in it.
|
||||
Return nil, or a list of the form:
|
||||
|
||||
(INTERPRETER [ARGS] FILE)"
|
||||
(let ((maxlen eshell-command-interpreter-max-length))
|
||||
(if (and (file-readable-p file)
|
||||
(file-regular-p file))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents-literally file nil 0 maxlen)
|
||||
(if (looking-at "#!\\([^ \t\n]+\\)\\([ \t]+\\(.+\\)\\)?")
|
||||
(if (match-string 3)
|
||||
(list (match-string 1)
|
||||
(match-string 3)
|
||||
file)
|
||||
(list (match-string 1)
|
||||
file)))))))
|
||||
|
||||
(defun eshell-find-interpreter (file &optional no-examine-p)
|
||||
"Find the command interpreter with which to execute FILE.
|
||||
If NO-EXAMINE-P is non-nil, FILE will not be inspected for a script
|
||||
line of the form #!<interp>."
|
||||
(let ((finterp
|
||||
(catch 'found
|
||||
(ignore
|
||||
(eshell-for possible eshell-interpreter-alist
|
||||
(cond
|
||||
((functionp (car possible))
|
||||
(and (funcall (car possible) file)
|
||||
(throw 'found (cdr possible))))
|
||||
((stringp (car possible))
|
||||
(and (string-match (car possible) file)
|
||||
(throw 'found (cdr possible))))
|
||||
(t
|
||||
(error "Invalid interpreter-alist test"))))))))
|
||||
(if finterp ; first check
|
||||
(list finterp file)
|
||||
(let ((fullname (if (file-name-directory file) file
|
||||
(eshell-search-path file)))
|
||||
(suffixes eshell-binary-suffixes))
|
||||
(if (and fullname (not (or eshell-force-execution
|
||||
(file-executable-p fullname))))
|
||||
(while suffixes
|
||||
(let ((try (concat fullname (car suffixes))))
|
||||
(if (or (file-executable-p try)
|
||||
(and eshell-force-execution
|
||||
(file-readable-p try)))
|
||||
(setq fullname try suffixes nil)
|
||||
(setq suffixes (cdr suffixes))))))
|
||||
(cond ((not (and fullname (file-exists-p fullname)))
|
||||
(let ((name (or fullname file)))
|
||||
(unless (setq fullname
|
||||
(run-hook-with-args-until-success
|
||||
'eshell-alternate-command-hook file))
|
||||
(error "%s: command not found" name))))
|
||||
((not (or eshell-force-execution
|
||||
(file-executable-p fullname)))
|
||||
(error "%s: Permission denied" fullname)))
|
||||
(let (interp)
|
||||
(unless no-examine-p
|
||||
(setq interp (eshell-script-interpreter fullname))
|
||||
(if interp
|
||||
(setq interp
|
||||
(cons (car (eshell-find-interpreter (car interp) t))
|
||||
(cdr interp)))))
|
||||
(or interp (list fullname)))))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; esh-ext.el ends here
|
135
lisp/eshell/esh-groups.el
Normal file
135
lisp/eshell/esh-groups.el
Normal file
@ -0,0 +1,135 @@
|
||||
;;; do not modify this file; it is auto-generated
|
||||
|
||||
(defgroup eshell-alias nil
|
||||
"Command aliases allow for easy definition of alternate commands."
|
||||
:tag "Command aliases"
|
||||
:link '(info-link "(eshell.info)Command aliases")
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-banner nil
|
||||
"This sample module displays a welcome banner at login.
|
||||
It exists so that others wishing to create their own Eshell extension
|
||||
modules may have a simple template to begin with."
|
||||
:tag "Login banner"
|
||||
:link '(info-link "(eshell.info)Login banner")
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-basic nil
|
||||
"The \"basic\" code provides a set of convenience functions which
|
||||
are traditionally considered shell builtins. Since all of the
|
||||
functionality provided by them is accessible through Lisp, they are
|
||||
not really builtins at all, but offer a command-oriented way to do the
|
||||
same thing."
|
||||
:tag "Basic shell commands"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-cmpl nil
|
||||
"This module provides a programmable completion function bound to
|
||||
the TAB key, which allows for completing command names, file names,
|
||||
variable names, arguments, etc."
|
||||
:tag "Argument completion"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-dirs nil
|
||||
"Directory navigation involves changing directories, examining the
|
||||
current directory, maintaining a directory stack, and also keeping
|
||||
track of a history of the last directory locations the user was in.
|
||||
Emacs does provide standard Lisp definitions of `pwd' and `cd', but
|
||||
they lack somewhat in feel from the typical shell equivalents."
|
||||
:tag "Directory navigation"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-glob nil
|
||||
"This module provides extended globbing syntax, similar what is used
|
||||
by zsh for filename generation."
|
||||
:tag "Extended filename globbing"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-hist nil
|
||||
"This module provides command history management."
|
||||
:tag "History list management"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-ls nil
|
||||
"This module implements the \"ls\" utility fully in Lisp. If it is
|
||||
passed any unrecognized command switches, it will revert to the
|
||||
operating system's version. This version of \"ls\" uses text
|
||||
properties to colorize its output based on the setting of
|
||||
`eshell-ls-use-colors'."
|
||||
:tag "Implementation of `ls' in Lisp"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-pred nil
|
||||
"This module allows for predicates to be applied to globbing
|
||||
patterns (similar to zsh), in addition to string modifiers which can
|
||||
be applied either to globbing results, variable references, or just
|
||||
ordinary strings."
|
||||
:tag "Value modifiers and predicates"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-prompt nil
|
||||
"This module provides command prompts, and navigation between them,
|
||||
as is common with most shells."
|
||||
:tag "Command prompts"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-rebind nil
|
||||
"This module allows for special keybindings that only take effect
|
||||
while the point is in a region of input text. By default, it binds
|
||||
C-a to move to the beginning of the input text (rather than just the
|
||||
beginning of the line), and C-p and C-n to move through the input
|
||||
history, C-u kills the current input text, etc. It also, if
|
||||
`eshell-confine-point-to-input' is non-nil, does not allow certain
|
||||
commands to cause the point to leave the input area, such as
|
||||
`backward-word', `previous-line', etc. This module intends to mimic
|
||||
the behavior of normal shells while the user editing new input text."
|
||||
:tag "Rebind keys at input"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-script nil
|
||||
"This module allows for the execution of files containing Eshell
|
||||
commands, as a script file."
|
||||
:tag "Running script files."
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-smart nil
|
||||
"This module combines the facility of normal, modern shells with
|
||||
some of the edit/review concepts inherent in the design of Plan 9's
|
||||
9term. See the docs for more details.
|
||||
|
||||
Most likely you will have to turn this option on and play around with
|
||||
it to get a real sense of how it works."
|
||||
:tag "Smart display of output"
|
||||
:link '(info-link "(eshell.info)Smart display of output")
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-term nil
|
||||
"This module causes visual commands (e.g., 'vi') to be executed by
|
||||
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"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-unix nil
|
||||
"This module defines many of the more common UNIX utilities as
|
||||
aliases implemented in Lisp. These include mv, ln, cp, rm, etc. If
|
||||
the user passes arguments which are too complex, or are unrecognized
|
||||
by the Lisp variant, the external version will be called (if
|
||||
available). The only reason not to use them would be because they are
|
||||
usually much slower. But in several cases their tight integration
|
||||
with Eshell makes them more versatile than their traditional cousins
|
||||
\(such as being able to use `kill' to kill Eshell background processes
|
||||
by name)."
|
||||
:tag "UNIX commands in Lisp"
|
||||
:group 'eshell-module)
|
||||
|
||||
(defgroup eshell-xtra nil
|
||||
"This module defines some extra alias functions which are entirely
|
||||
optional. They can be viewed as samples for how to write Eshell alias
|
||||
functions, or as aliases which make some of Emacs' behavior more
|
||||
naturally accessible within Emacs."
|
||||
:tag "Extra alias functions"
|
||||
:group 'eshell-module)
|
||||
|
509
lisp/eshell/esh-io.el
Normal file
509
lisp/eshell/esh-io.el
Normal file
@ -0,0 +1,509 @@
|
||||
;;; esh-io --- I/O management
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'esh-io)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-io nil
|
||||
"Eshell's I/O management code provides a scheme for treating many
|
||||
different kinds of objects -- symbols, files, buffers, etc. -- as
|
||||
though they were files."
|
||||
:tag "I/O management"
|
||||
:group 'eshell)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; At the moment, only output redirection is supported in Eshell. To
|
||||
;; use input redirection, the following syntax will work, assuming
|
||||
;; that the command after the pipe is always an external command:
|
||||
;;
|
||||
;; cat <file> | <command>
|
||||
;;
|
||||
;; Otherwise, output redirection and piping are provided in a manner
|
||||
;; consistent with most shells. Therefore, only unique features are
|
||||
;; mentioned here.
|
||||
;;
|
||||
;;;_* Insertion
|
||||
;;
|
||||
;; To insert at the location of point in a buffer, use '>>>':
|
||||
;;
|
||||
;; echo alpha >>> #<buffer *scratch*>;
|
||||
;;
|
||||
;;;_* Pseudo-devices
|
||||
;;
|
||||
;; A few pseudo-devices are provided, since Emacs cannot write
|
||||
;; directly to a UNIX device file:
|
||||
;;
|
||||
;; echo alpha > /dev/null ; the bit bucket
|
||||
;; echo alpha > /dev/kill ; set the kill ring
|
||||
;; echo alpha >> /dev/clip ; append to the clipboard
|
||||
;;
|
||||
;;;_* Multiple output targets
|
||||
;;
|
||||
;; Eshell can write to multiple output targets, including pipes.
|
||||
;; Example:
|
||||
;;
|
||||
;; (+ 1 2) > a > b > c ; prints number to all three files
|
||||
;; (+ 1 2) > a | wc ; prints to 'a', and pipes to 'wc'
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-io-load-hook '(eshell-io-initialize)
|
||||
"*A hook that gets run when `eshell-io' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-io)
|
||||
|
||||
(defcustom eshell-number-of-handles 3
|
||||
"*The number of file handles that eshell supports.
|
||||
Currently this is standard input, output and error. But even all of
|
||||
these Emacs does not currently support with asynchronous processes
|
||||
\(which is what eshell uses so that you can continue doing work in
|
||||
other buffers) ."
|
||||
:type 'integer
|
||||
:group 'eshell-io)
|
||||
|
||||
(defcustom eshell-output-handle 1
|
||||
"*The index of the standard output handle."
|
||||
:type 'integer
|
||||
:group 'eshell-io)
|
||||
|
||||
(defcustom eshell-error-handle 2
|
||||
"*The index of the standard error handle."
|
||||
:type 'integer
|
||||
:group 'eshell-io)
|
||||
|
||||
(defcustom eshell-buffer-shorthand nil
|
||||
"*If non-nil, a symbol name can be used for a buffer in redirection.
|
||||
If nil, redirecting to a buffer requires buffer name syntax. If this
|
||||
variable is set, redirection directly to Lisp symbols will be
|
||||
impossible.
|
||||
|
||||
Example:
|
||||
|
||||
echo hello > '*scratch* ; works if `eshell-buffer-shorthand' is t
|
||||
echo hello > #<buffer *scratch*> ; always works"
|
||||
:type 'boolean
|
||||
:group 'eshell-io)
|
||||
|
||||
(defcustom eshell-print-queue-size 5
|
||||
"*The size of the print queue, for doing buffered printing.
|
||||
This is basically a speed enhancement, to avoid blocking the Lisp code
|
||||
from executing while Emacs is redisplaying."
|
||||
:type 'integer
|
||||
:group 'eshell-io)
|
||||
|
||||
(defcustom eshell-virtual-targets
|
||||
'(("/dev/eshell" eshell-interactive-print nil)
|
||||
("/dev/kill" (lambda (mode)
|
||||
(if (eq mode 'overwrite)
|
||||
(kill-new ""))
|
||||
'eshell-kill-append) t)
|
||||
("/dev/clip" (lambda (mode)
|
||||
(if (eq mode 'overwrite)
|
||||
(let ((x-select-enable-clipboard t))
|
||||
(kill-new "")))
|
||||
'eshell-clipboard-append) t))
|
||||
"*Map virtual devices name to Emacs Lisp functions.
|
||||
If the user specifies any of the filenames above as a redirection
|
||||
target, the function in the second element will be called.
|
||||
|
||||
If the third element is non-nil, the redirection mode is passed as an
|
||||
argument (which is the symbol `overwrite', `append' or `insert'), and
|
||||
the function is expected to return another function -- which is the
|
||||
output function. Otherwise, the second element itself is the output
|
||||
function.
|
||||
|
||||
The output function is then called repeatedly with a single strings,
|
||||
with represents success pieces of the output of the command, until nil
|
||||
is passed, meaning EOF.
|
||||
|
||||
NOTE: /dev/null is handled specially as a virtual target, and should
|
||||
not be added to this variable."
|
||||
:type '(repeat
|
||||
(list (string :tag "Target")
|
||||
function
|
||||
(choice (const :tag "Func returns output-func" t)
|
||||
(const :tag "Func is output-func" nil))))
|
||||
:group 'eshell-io)
|
||||
|
||||
(put 'eshell-virtual-targets 'risky-local-variable t)
|
||||
|
||||
;;; Internal Variables:
|
||||
|
||||
(defvar eshell-current-handles nil)
|
||||
|
||||
(defvar eshell-last-command-status 0
|
||||
"The exit code from the last command. 0 if successful.")
|
||||
|
||||
(defvar eshell-last-command-result nil
|
||||
"The result of the last command. Not related to success.")
|
||||
|
||||
(defvar eshell-output-file-buffer nil
|
||||
"If non-nil, the current buffer is a file output buffer.")
|
||||
|
||||
(defvar eshell-print-count)
|
||||
(defvar eshell-current-redirections)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-io-initialize ()
|
||||
"Initialize the I/O subsystem code."
|
||||
(make-local-hook 'eshell-parse-argument-hook)
|
||||
(add-hook 'eshell-parse-argument-hook
|
||||
'eshell-parse-redirection nil t)
|
||||
(make-local-variable 'eshell-current-redirections)
|
||||
(make-local-hook 'eshell-pre-rewrite-command-hook)
|
||||
(add-hook 'eshell-pre-rewrite-command-hook
|
||||
'eshell-strip-redirections nil t)
|
||||
(make-local-hook 'eshell-post-rewrite-command-hook)
|
||||
(add-hook 'eshell-post-rewrite-command-hook
|
||||
'eshell-apply-redirections nil t))
|
||||
|
||||
(defun eshell-parse-redirection ()
|
||||
"Parse an output redirection, such as '2>'."
|
||||
(if (and (not eshell-current-quoted)
|
||||
(looking-at "\\([0-9]\\)?\\(<\\|>+\\)&?\\([0-9]\\)?\\s-*"))
|
||||
(if eshell-current-argument
|
||||
(eshell-finish-arg)
|
||||
(let ((sh (match-string 1))
|
||||
(oper (match-string 2))
|
||||
; (th (match-string 3))
|
||||
)
|
||||
(if (string= oper "<")
|
||||
(error "Eshell does not support input redirection"))
|
||||
(eshell-finish-arg
|
||||
(prog1
|
||||
(list 'eshell-set-output-handle
|
||||
(or (and sh (string-to-int sh)) 1)
|
||||
(list 'quote
|
||||
(aref [overwrite append insert]
|
||||
(1- (length oper)))))
|
||||
(goto-char (match-end 0))))))))
|
||||
|
||||
(defun eshell-strip-redirections (terms)
|
||||
"Rewrite any output redirections in TERMS."
|
||||
(setq eshell-current-redirections (list t))
|
||||
(let ((tl terms)
|
||||
(tt (cdr terms)))
|
||||
(while tt
|
||||
(if (not (and (consp (car tt))
|
||||
(eq (caar tt) 'eshell-set-output-handle)))
|
||||
(setq tt (cdr tt)
|
||||
tl (cdr tl))
|
||||
(unless (cdr tt)
|
||||
(error "Missing redirection target"))
|
||||
(nconc eshell-current-redirections
|
||||
(list (list 'ignore
|
||||
(append (car tt) (list (cadr tt))))))
|
||||
(setcdr tl (cddr tt))
|
||||
(setq tt (cddr tt))))
|
||||
(setq eshell-current-redirections
|
||||
(cdr eshell-current-redirections))))
|
||||
|
||||
(defun eshell-apply-redirections (cmdsym)
|
||||
"Apply any redirection which were specified for COMMAND."
|
||||
(if eshell-current-redirections
|
||||
(set cmdsym
|
||||
(append (list 'progn)
|
||||
eshell-current-redirections
|
||||
(list (symbol-value cmdsym))))))
|
||||
|
||||
(defun eshell-create-handles
|
||||
(standard-output output-mode &optional standard-error error-mode)
|
||||
"Create a new set of file handles for a command.
|
||||
The default location for standard output and standard error will go to
|
||||
STANDARD-OUTPUT and STANDARD-ERROR, respectively."
|
||||
(let ((handles (make-vector eshell-number-of-handles nil))
|
||||
(output-target (eshell-get-target standard-output output-mode))
|
||||
(error-target (eshell-get-target standard-error error-mode)))
|
||||
(aset handles eshell-output-handle (cons output-target 1))
|
||||
(if standard-error
|
||||
(aset handles eshell-error-handle (cons error-target 1))
|
||||
(aset handles eshell-error-handle (cons output-target 1)))
|
||||
handles))
|
||||
|
||||
(defun eshell-protect-handles (handles)
|
||||
"Protect the handles in HANDLES from a being closed."
|
||||
(let ((idx 0))
|
||||
(while (< idx eshell-number-of-handles)
|
||||
(if (aref handles idx)
|
||||
(setcdr (aref handles idx)
|
||||
(1+ (cdr (aref handles idx)))))
|
||||
(setq idx (1+ idx))))
|
||||
handles)
|
||||
|
||||
(defun eshell-close-target (target status)
|
||||
"Close an output TARGET, passing STATUS as the result.
|
||||
STATUS should be non-nil on successful termination of the output."
|
||||
(cond
|
||||
((symbolp target) nil)
|
||||
|
||||
;; If we were redirecting to a file, save the file and close the
|
||||
;; buffer.
|
||||
((markerp target)
|
||||
(let ((buf (marker-buffer target)))
|
||||
(when buf ; somebody's already killed it!
|
||||
(save-current-buffer
|
||||
(set-buffer buf)
|
||||
(when eshell-output-file-buffer
|
||||
(save-buffer)
|
||||
(when (eq eshell-output-file-buffer t)
|
||||
(or status (set-buffer-modified-p nil))
|
||||
(kill-buffer buf)))))))
|
||||
|
||||
;; If we're redirecting to a process (via a pipe, or process
|
||||
;; redirection), send it EOF so that it knows we're finished.
|
||||
((processp target)
|
||||
(if (eq (process-status target) 'run)
|
||||
(process-send-eof target)))
|
||||
|
||||
;; A plain function redirection needs no additional arguments
|
||||
;; passed.
|
||||
((functionp target)
|
||||
(funcall target status))
|
||||
|
||||
;; But a more complicated function redirection (which can only
|
||||
;; happen with aliases at the moment) has arguments that need to be
|
||||
;; passed along with it.
|
||||
((consp target)
|
||||
(apply (car target) status (cdr target)))))
|
||||
|
||||
(defun eshell-close-handles (exit-code &optional result handles)
|
||||
"Close all of the current handles, taking refcounts into account.
|
||||
EXIT-CODE is the process exit code; mainly, it is zero, if the command
|
||||
completed successfully. RESULT is the quoted value of the last
|
||||
command. If nil, then the meta variables for keeping track of the
|
||||
last execution result should not be changed."
|
||||
(let ((idx 0))
|
||||
(assert (or (not result) (eq (car result) 'quote)))
|
||||
(setq eshell-last-command-status exit-code
|
||||
eshell-last-command-result (cadr result))
|
||||
(while (< idx eshell-number-of-handles)
|
||||
(let ((handles (or handles eshell-current-handles)))
|
||||
(when (aref handles idx)
|
||||
(setcdr (aref handles idx)
|
||||
(1- (cdr (aref handles idx))))
|
||||
(when (= (cdr (aref handles idx)) 0)
|
||||
(let ((target (car (aref handles idx))))
|
||||
(if (not (listp target))
|
||||
(eshell-close-target target (= exit-code 0))
|
||||
(while target
|
||||
(eshell-close-target (car target) (= exit-code 0))
|
||||
(setq target (cdr target)))))
|
||||
(setcar (aref handles idx) nil))))
|
||||
(setq idx (1+ idx)))
|
||||
nil))
|
||||
|
||||
(defun eshell-kill-append (string)
|
||||
"Call `kill-append' with STRING, if it is indeed a string."
|
||||
(if (stringp string)
|
||||
(kill-append string nil)))
|
||||
|
||||
(defun eshell-clipboard-append (string)
|
||||
"Call `kill-append' with STRING, if it is indeed a string."
|
||||
(if (stringp string)
|
||||
(let ((x-select-enable-clipboard t))
|
||||
(kill-append string nil))))
|
||||
|
||||
(defun eshell-get-target (target &optional mode)
|
||||
"Convert TARGET, which is a raw argument, into a valid output target.
|
||||
MODE is either `overwrite', `append' or `insert'."
|
||||
(setq mode (or mode 'insert))
|
||||
(cond
|
||||
((stringp target)
|
||||
(let ((redir (assoc target eshell-virtual-targets)))
|
||||
(if redir
|
||||
(if (nth 2 redir)
|
||||
(funcall (nth 1 redir) mode)
|
||||
(nth 1 redir))
|
||||
(let* ((exists (get-file-buffer target))
|
||||
(buf (find-file-noselect target t)))
|
||||
(with-current-buffer buf
|
||||
(if buffer-read-only
|
||||
(error "Cannot write to read-only file `%s'" target))
|
||||
(set (make-local-variable 'eshell-output-file-buffer)
|
||||
(if (eq exists buf) 0 t))
|
||||
(cond ((eq mode 'overwrite)
|
||||
(erase-buffer))
|
||||
((eq mode 'append)
|
||||
(goto-char (point-max))))
|
||||
(point-marker))))))
|
||||
((or (bufferp target)
|
||||
(and (boundp 'eshell-buffer-shorthand)
|
||||
(symbol-value 'eshell-buffer-shorthand)
|
||||
(symbolp target)))
|
||||
(let ((buf (if (bufferp target)
|
||||
target
|
||||
(get-buffer-create
|
||||
(symbol-name target)))))
|
||||
(with-current-buffer buf
|
||||
(cond ((eq mode 'overwrite)
|
||||
(erase-buffer))
|
||||
((eq mode 'append)
|
||||
(goto-char (point-max))))
|
||||
(point-marker))))
|
||||
((functionp target)
|
||||
nil)
|
||||
((symbolp target)
|
||||
(if (eq mode 'overwrite)
|
||||
(set target nil))
|
||||
target)
|
||||
((or (processp target)
|
||||
(markerp target))
|
||||
target)
|
||||
(t
|
||||
(error "Illegal redirection target: %s"
|
||||
(eshell-stringify target)))))
|
||||
|
||||
(eval-when-compile
|
||||
(defvar grep-null-device))
|
||||
|
||||
(defun eshell-set-output-handle (index mode &optional target)
|
||||
"Set handle INDEX, using MODE, to point to TARGET."
|
||||
(when target
|
||||
(if (and (stringp target)
|
||||
(or (cond
|
||||
((boundp 'null-device)
|
||||
(string= target null-device))
|
||||
((boundp 'grep-null-device)
|
||||
(string= target grep-null-device))
|
||||
(t nil))
|
||||
(string= target "/dev/null")))
|
||||
(aset eshell-current-handles index nil)
|
||||
(let ((where (eshell-get-target target mode))
|
||||
(current (car (aref eshell-current-handles index))))
|
||||
(if (and (listp current)
|
||||
(not (member where current)))
|
||||
(setq current (append current (list where)))
|
||||
(setq current (list where)))
|
||||
(if (not (aref eshell-current-handles index))
|
||||
(aset eshell-current-handles index (cons nil 1)))
|
||||
(setcar (aref eshell-current-handles index) current)))))
|
||||
|
||||
(defun eshell-interactive-output-p ()
|
||||
"Return non-nil if current handles are bound for interactive display."
|
||||
(and (eq (car (aref eshell-current-handles
|
||||
eshell-output-handle)) t)
|
||||
(eq (car (aref eshell-current-handles
|
||||
eshell-error-handle)) t)))
|
||||
|
||||
(defvar eshell-print-queue nil)
|
||||
(defvar eshell-print-queue-count -1)
|
||||
|
||||
(defun eshell-flush (&optional reset-p)
|
||||
"Flush out any lines that have been queued for printing.
|
||||
Must be called before printing begins with -1 as its argument, and
|
||||
after all printing is over with no argument."
|
||||
(ignore
|
||||
(if reset-p
|
||||
(setq eshell-print-queue nil
|
||||
eshell-print-queue-count reset-p)
|
||||
(if eshell-print-queue
|
||||
(eshell-print eshell-print-queue))
|
||||
(eshell-flush 0))))
|
||||
|
||||
(defun eshell-init-print-buffer ()
|
||||
"Initialize the buffered printing queue."
|
||||
(eshell-flush -1))
|
||||
|
||||
(defun eshell-buffered-print (&rest strings)
|
||||
"A buffered print -- *for strings only*."
|
||||
(if (< eshell-print-queue-count 0)
|
||||
(progn
|
||||
(eshell-print (apply 'concat strings))
|
||||
(setq eshell-print-queue-count 0))
|
||||
(if (= eshell-print-queue-count eshell-print-queue-size)
|
||||
(eshell-flush))
|
||||
(setq eshell-print-queue
|
||||
(concat eshell-print-queue (apply 'concat strings))
|
||||
eshell-print-queue-count (1+ eshell-print-queue-count))))
|
||||
|
||||
(defsubst eshell-print (object)
|
||||
"Output OBJECT to the error handle."
|
||||
(eshell-output-object object eshell-output-handle))
|
||||
|
||||
(defsubst eshell-error (object)
|
||||
"Output OBJECT to the error handle."
|
||||
(eshell-output-object object eshell-error-handle))
|
||||
|
||||
(defsubst eshell-errorn (object)
|
||||
"Output OBJECT to the error handle."
|
||||
(eshell-error object)
|
||||
(eshell-error "\n"))
|
||||
|
||||
(defsubst eshell-printn (object)
|
||||
"Output OBJECT to the error handle."
|
||||
(eshell-print object)
|
||||
(eshell-print "\n"))
|
||||
|
||||
(defun eshell-output-object-to-target (object target)
|
||||
"Insert OBJECT into TARGET.
|
||||
Returns what was actually sent, or nil if nothing was sent."
|
||||
(cond
|
||||
((functionp target)
|
||||
(funcall target object))
|
||||
|
||||
((symbolp target)
|
||||
(if (eq target t) ; means "print to display"
|
||||
(eshell-output-filter nil (eshell-stringify object))
|
||||
(if (not (symbol-value target))
|
||||
(set target object)
|
||||
(setq object (eshell-stringify object))
|
||||
(if (not (stringp (symbol-value target)))
|
||||
(set target (eshell-stringify
|
||||
(symbol-value target))))
|
||||
(set target (concat (symbol-value target) object)))))
|
||||
|
||||
((markerp target)
|
||||
(if (buffer-live-p (marker-buffer target))
|
||||
(with-current-buffer (marker-buffer target)
|
||||
(let ((moving (= (point) target)))
|
||||
(save-excursion
|
||||
(goto-char target)
|
||||
(setq object (eshell-stringify object))
|
||||
(insert-and-inherit object)
|
||||
(set-marker target (point-marker)))
|
||||
(if moving
|
||||
(goto-char target))))))
|
||||
|
||||
((processp target)
|
||||
(when (eq (process-status target) 'run)
|
||||
(setq object (eshell-stringify object))
|
||||
(process-send-string target object)))
|
||||
|
||||
((consp target)
|
||||
(apply (car target) object (cdr target))))
|
||||
object)
|
||||
|
||||
(defun eshell-output-object (object &optional handle-index handles)
|
||||
"Insert OBJECT, using HANDLE-INDEX specifically)."
|
||||
(let ((target (car (aref (or handles eshell-current-handles)
|
||||
(or handle-index eshell-output-handle)))))
|
||||
(if (and target (not (listp target)))
|
||||
(eshell-output-object-to-target object target)
|
||||
(while target
|
||||
(eshell-output-object-to-target object (car target))
|
||||
(setq target (cdr target))))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; esh-io.el ends here
|
142
lisp/eshell/esh-maint.el
Normal file
142
lisp/eshell/esh-maint.el
Normal file
@ -0,0 +1,142 @@
|
||||
;;; esh-maint --- init code for building eshell
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; Code:
|
||||
|
||||
(provide 'esh-maint)
|
||||
|
||||
(and (fboundp 'font-lock-add-keywords)
|
||||
(font-lock-add-keywords
|
||||
'emacs-lisp-mode
|
||||
'(("(eshell-for\\>" . font-lock-keyword-face)
|
||||
("(eshell-deftest\\>" . font-lock-keyword-face)
|
||||
("(eshell-condition-case\\>" . font-lock-keyword-face))))
|
||||
|
||||
(if (file-directory-p "../pcomplete")
|
||||
(add-to-list 'load-path "../pcomplete"))
|
||||
|
||||
(if (locate-library "pcomplete")
|
||||
(require 'pcomplete))
|
||||
|
||||
(eval-when-compile
|
||||
(require 'cl)
|
||||
(setq cl-optimize-speed 9))
|
||||
|
||||
;; (defun eshell-generate-autoloads ()
|
||||
;; (interactive)
|
||||
;; (require 'autoload)
|
||||
;; (setq generated-autoload-file
|
||||
;; (expand-file-name (car command-line-args-left)))
|
||||
;; (setq command-line-args-left (cdr command-line-args-left))
|
||||
;; (batch-update-autoloads))
|
||||
|
||||
(require 'eshell)
|
||||
(require 'esh-mode) ; brings in eshell-util
|
||||
(require 'esh-opt)
|
||||
(require 'esh-test)
|
||||
|
||||
;; (defun eshell-generate-main-menu ()
|
||||
;; "Create the main menu for the eshell documentation."
|
||||
;; (insert "@menu
|
||||
;; * The Emacs shell:: eshell.
|
||||
|
||||
;; Core Functionality\n")
|
||||
;; (eshell-for module
|
||||
;; (sort (eshell-subgroups 'eshell)
|
||||
;; (function
|
||||
;; (lambda (a b)
|
||||
;; (string-lessp (symbol-name a)
|
||||
;; (symbol-name b)))))
|
||||
;; (insert (format "* %-34s"
|
||||
;; (concat (get module 'custom-tag) "::"))
|
||||
;; (symbol-name module) ".\n"))
|
||||
;; (insert "\nOptional Functionality\n")
|
||||
;; (eshell-for module
|
||||
;; (sort (eshell-subgroups 'eshell-module)
|
||||
;; (function
|
||||
;; (lambda (a b)
|
||||
;; (string-lessp (symbol-name a)
|
||||
;; (symbol-name b)))))
|
||||
;; (insert (format "* %-34s"
|
||||
;; (concat (get module 'custom-tag) "::"))
|
||||
;; (symbol-name module) ".\n"))
|
||||
;; (insert "@end menu\n"))
|
||||
|
||||
;; (defun eshell-make-texi ()
|
||||
;; "Make the eshell.texi file."
|
||||
;; (interactive)
|
||||
;; (require 'eshell-auto)
|
||||
;; (require 'texidoc)
|
||||
;; (require 'pcomplete)
|
||||
;; (apply 'texidoc-files 'eshell-generate-main-menu "eshell.doci"
|
||||
;; (append
|
||||
;; (list "eshell.el")
|
||||
;; (sort (mapcar
|
||||
;; (function
|
||||
;; (lambda (sym)
|
||||
;; (let ((name (symbol-name sym)))
|
||||
;; (if (string-match "\\`eshell-\\(.*\\)" name)
|
||||
;; (setq name (concat "esh-" (match-string 1 name))))
|
||||
;; (concat name ".el"))))
|
||||
;; (eshell-subgroups 'eshell))
|
||||
;; 'string-lessp)
|
||||
;; (sort (mapcar
|
||||
;; (function
|
||||
;; (lambda (sym)
|
||||
;; (let ((name (symbol-name sym)))
|
||||
;; (if (string-match "\\`eshell-\\(.*\\)" name)
|
||||
;; (setq name (concat "em-" (match-string 1 name))))
|
||||
;; (concat name ".el"))))
|
||||
;; (eshell-subgroups 'eshell-module))
|
||||
;; 'string-lessp)
|
||||
;; (list "eshell.texi"))))
|
||||
|
||||
;; (defun eshell-make-readme ()
|
||||
;; "Make the README file from eshell.el."
|
||||
;; (interactive)
|
||||
;; (require 'eshell-auto)
|
||||
;; (require 'texidoc)
|
||||
;; (require 'pcomplete)
|
||||
;; (texidoc-files nil "eshell.doci" "eshell.el" "README.texi")
|
||||
;; (set-buffer (get-buffer "README.texi"))
|
||||
;; (goto-char (point-min))
|
||||
;; (search-forward "@chapter")
|
||||
;; (beginning-of-line)
|
||||
;; (forward-line -1)
|
||||
;; (kill-line 2)
|
||||
;; (re-search-forward "^@section User Options")
|
||||
;; (beginning-of-line)
|
||||
;; (delete-region (point) (point-max))
|
||||
;; (insert "@bye\n")
|
||||
;; (save-buffer)
|
||||
;; (with-temp-buffer
|
||||
;; (call-process "makeinfo" nil t nil "--no-headers" "README.texi")
|
||||
;; (goto-char (point-min))
|
||||
;; (search-forward "The Emacs Shell")
|
||||
;; (beginning-of-line)
|
||||
;; (delete-region (point-min) (point))
|
||||
;; (write-file "README"))
|
||||
;; (delete-file "README.texi")
|
||||
;; (kill-buffer "README.texi"))
|
||||
|
||||
;;; esh-maint.el ends here
|
139
lisp/eshell/esh-module.el
Normal file
139
lisp/eshell/esh-module.el
Normal file
@ -0,0 +1,139 @@
|
||||
;;; esh-module --- Eshell modules
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'esh-module)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-module nil
|
||||
"The `eshell-module' group is for Eshell extension modules, which
|
||||
provide optional behavior which the user can enable or disable by
|
||||
customizing the variable `eshell-modules-list'."
|
||||
:tag "Extension modules"
|
||||
:group 'eshell)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
(require 'esh-util)
|
||||
|
||||
(defun eshell-load-defgroups (&optional directory)
|
||||
"Load `defgroup' statements from Eshell's module files."
|
||||
(with-current-buffer
|
||||
(find-file-noselect (expand-file-name "esh-groups.el" directory))
|
||||
(erase-buffer)
|
||||
(insert ";;; do not modify this file; it is auto-generated\n\n")
|
||||
(let ((files (directory-files (or directory
|
||||
(car command-line-args-left))
|
||||
nil "\\`em-.*\\.el\\'")))
|
||||
(while files
|
||||
(message "Loading defgroup from `%s'" (car files))
|
||||
(let (defgroup)
|
||||
(catch 'handled
|
||||
(with-current-buffer (find-file-noselect (car files))
|
||||
(goto-char (point-min))
|
||||
(while t
|
||||
(forward-sexp)
|
||||
(if (eobp) (throw 'handled t))
|
||||
(backward-sexp)
|
||||
(let ((begin (point))
|
||||
(defg (looking-at "(defgroup")))
|
||||
(forward-sexp)
|
||||
(if defg
|
||||
(setq defgroup (buffer-substring begin (point))))))))
|
||||
(if defgroup
|
||||
(insert defgroup "\n\n")))
|
||||
(setq files (cdr files))))
|
||||
(save-buffer)))
|
||||
|
||||
;; load the defgroup's for the standard extension modules, so that
|
||||
;; documentation can be provided when the user customize's
|
||||
;; `eshell-modules-list'.
|
||||
(eval-when-compile
|
||||
(when (equal (file-name-nondirectory byte-compile-current-file)
|
||||
"esh-module.el")
|
||||
(let* ((directory (file-name-directory byte-compile-current-file))
|
||||
(elc-file (expand-file-name "esh-groups.elc" directory)))
|
||||
(eshell-load-defgroups directory)
|
||||
(if (file-exists-p elc-file) (delete-file elc-file)))))
|
||||
|
||||
(load "esh-groups" t t)
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-module-unload-hook
|
||||
'(eshell-unload-extension-modules)
|
||||
"*A hook run when `eshell-module' is unloaded."
|
||||
:type 'hook
|
||||
:group 'eshell-module)
|
||||
|
||||
(defcustom eshell-modules-list
|
||||
'(eshell-alias
|
||||
eshell-banner
|
||||
eshell-basic
|
||||
eshell-cmpl
|
||||
eshell-dirs
|
||||
eshell-glob
|
||||
eshell-hist
|
||||
eshell-ls
|
||||
eshell-pred
|
||||
eshell-prompt
|
||||
eshell-script
|
||||
eshell-term
|
||||
eshell-unix)
|
||||
"*A list of optional add-on modules to be loaded by Eshell.
|
||||
Changes will only take effect in future Eshell buffers."
|
||||
:type (append
|
||||
(list 'set ':tag "Supported modules")
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (modname)
|
||||
(let ((modsym (intern modname)))
|
||||
(list 'const
|
||||
':tag (format "%s -- %s" modname
|
||||
(get modsym 'custom-tag))
|
||||
':link (caar (get modsym 'custom-links))
|
||||
':doc (concat "\n" (get modsym 'group-documentation)
|
||||
"\n ")
|
||||
modsym))))
|
||||
(sort (mapcar 'symbol-name
|
||||
(eshell-subgroups 'eshell-module))
|
||||
'string-lessp))
|
||||
'((repeat :inline t :tag "Other modules" symbol)))
|
||||
:group 'eshell-module)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defsubst eshell-using-module (module)
|
||||
"Return non-nil if a certain Eshell MODULE is in use.
|
||||
The MODULE should be a symbol corresponding to that module's
|
||||
customization group. Example: `eshell-cmpl' for that module."
|
||||
(memq module eshell-modules-list))
|
||||
|
||||
(defun eshell-unload-extension-modules ()
|
||||
"Unload any memory resident extension modules."
|
||||
(eshell-for module (eshell-subgroups 'eshell-module)
|
||||
(if (featurep module)
|
||||
(ignore-errors
|
||||
(message "Unloading %s..." (symbol-name module))
|
||||
(unload-feature module)
|
||||
(message "Unloading %s...done" (symbol-name module))))))
|
||||
|
||||
;;; esh-module.el ends here
|
226
lisp/eshell/esh-opt.el
Normal file
226
lisp/eshell/esh-opt.el
Normal file
@ -0,0 +1,226 @@
|
||||
;;; esh-opt --- command options processing
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'esh-opt)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-opt nil
|
||||
"The options processing code handles command argument parsing for
|
||||
Eshell commands implemented in Lisp."
|
||||
:tag "Command options processing"
|
||||
:group 'eshell)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; User Functions:
|
||||
|
||||
(defmacro eshell-eval-using-options (name macro-args
|
||||
options &rest body-forms)
|
||||
"Process NAME's MACRO-ARGS using a set of command line OPTIONS.
|
||||
After doing so, settings will be stored in local symbols as declared
|
||||
by OPTIONS; FORMS will then be evaluated -- assuming all was OK.
|
||||
|
||||
The syntax of OPTIONS is:
|
||||
|
||||
'((?C nil nil multi-column \"multi-column display\")
|
||||
(nil \"help\" nil nil \"show this usage display\")
|
||||
(?r \"reverse\" nil reverse-list \"reverse order while sorting\")
|
||||
:external \"ls\"
|
||||
:usage \"[OPTION]... [FILE]...
|
||||
List information about the FILEs (the current directory by default).
|
||||
Sort entries alphabetically across.\")
|
||||
|
||||
`eshell-eval-using-options' returns the value of the last form in
|
||||
BODY-FORMS. If instead an external command is run, the tag
|
||||
`eshell-external' will be thrown with the new process for its value.
|
||||
|
||||
Lastly, any remaining arguments will be available in a locally
|
||||
interned variable `args' (created using a `let' form)."
|
||||
`(let ((temp-args
|
||||
,(if (memq ':preserve-args (cadr options))
|
||||
macro-args
|
||||
(list 'eshell-stringify-list
|
||||
(list 'eshell-flatten-list macro-args)))))
|
||||
(let ,(append (mapcar (function
|
||||
(lambda (opt)
|
||||
(or (and (listp opt) (nth 3 opt))
|
||||
'eshell-option-stub)))
|
||||
(cadr options))
|
||||
'(usage-msg last-value ext-command args))
|
||||
(eshell-do-opt ,name ,options (quote ,body-forms)))))
|
||||
|
||||
;;; Internal Functions:
|
||||
|
||||
(eval-when-compile
|
||||
(defvar temp-args)
|
||||
(defvar last-value)
|
||||
(defvar usage-msg)
|
||||
(defvar ext-command)
|
||||
(defvar args))
|
||||
|
||||
(defun eshell-do-opt (name options body-forms)
|
||||
"Helper function for `eshell-eval-using-options'.
|
||||
This code doesn't really need to be macro expanded everywhere."
|
||||
(setq args temp-args)
|
||||
(if (setq
|
||||
ext-command
|
||||
(catch 'eshell-ext-command
|
||||
(when (setq
|
||||
usage-msg
|
||||
(catch 'eshell-usage
|
||||
(setq last-value nil)
|
||||
(if (and (= (length args) 0)
|
||||
(memq ':show-usage options))
|
||||
(throw 'eshell-usage
|
||||
(eshell-show-usage name options)))
|
||||
(setq args (eshell-process-args name args options)
|
||||
last-value (eval (append (list 'progn)
|
||||
body-forms)))
|
||||
nil))
|
||||
(error usage-msg))))
|
||||
(throw 'eshell-external
|
||||
(eshell-external-command ext-command args))
|
||||
last-value))
|
||||
|
||||
(defun eshell-show-usage (name options)
|
||||
"Display the usage message for NAME, using OPTIONS."
|
||||
(let ((usage (format "usage: %s %s\n\n" name
|
||||
(cadr (memq ':usage options))))
|
||||
(extcmd (memq ':external options))
|
||||
(post-usage (memq ':post-usage options))
|
||||
had-option)
|
||||
(while options
|
||||
(when (listp (car options))
|
||||
(let ((opt (car options)))
|
||||
(setq had-option t)
|
||||
(cond ((and (nth 0 opt)
|
||||
(nth 1 opt))
|
||||
(setq usage
|
||||
(concat usage
|
||||
(format " %-20s %s\n"
|
||||
(format "-%c, --%s" (nth 0 opt)
|
||||
(nth 1 opt))
|
||||
(nth 4 opt)))))
|
||||
((nth 0 opt)
|
||||
(setq usage
|
||||
(concat usage
|
||||
(format " %-20s %s\n"
|
||||
(format "-%c" (nth 0 opt))
|
||||
(nth 4 opt)))))
|
||||
((nth 1 opt)
|
||||
(setq usage
|
||||
(concat usage
|
||||
(format " %-20s %s\n"
|
||||
(format " --%s" (nth 1 opt))
|
||||
(nth 4 opt)))))
|
||||
(t (setq had-option nil)))))
|
||||
(setq options (cdr options)))
|
||||
(if post-usage
|
||||
(setq usage (concat usage (and had-option "\n")
|
||||
(cadr post-usage))))
|
||||
(when extcmd
|
||||
(setq extcmd (eshell-search-path (cadr extcmd)))
|
||||
(if extcmd
|
||||
(setq usage
|
||||
(concat usage
|
||||
(format "
|
||||
This command is implemented in Lisp. If an unrecognized option is
|
||||
passed to this command, the external version '%s'
|
||||
will be called instead." extcmd)))))
|
||||
(throw 'eshell-usage usage)))
|
||||
|
||||
(defun eshell-set-option (name ai opt options)
|
||||
"Using NAME's remaining args (index AI), set the OPT within OPTIONS.
|
||||
If the option consumes an argument for its value, the argument list
|
||||
will be modified."
|
||||
(if (not (nth 3 opt))
|
||||
(eshell-show-usage name options)
|
||||
(if (eq (nth 2 opt) t)
|
||||
(if (> ai (length args))
|
||||
(error "%s: missing option argument" name)
|
||||
(set (nth 3 opt) (nth ai args))
|
||||
(if (> ai 0)
|
||||
(setcdr (nthcdr (1- ai) args) (nthcdr (1+ ai) args))
|
||||
(setq args (cdr args))))
|
||||
(set (nth 3 opt) (or (nth 2 opt) t)))))
|
||||
|
||||
(defun eshell-process-option (name switch kind ai options)
|
||||
"For NAME, process SWITCH (of type KIND), from args at index AI.
|
||||
The SWITCH will be looked up in the set of OPTIONS.
|
||||
|
||||
SWITCH should be either a string or character. KIND should be the
|
||||
integer 0 if it's a character, or 1 if it's a string.
|
||||
|
||||
The SWITCH is then be matched against OPTIONS. If no matching handler
|
||||
is found, and an :external command is defined (and available), it will
|
||||
be called; otherwise, an error will be triggered to say that the
|
||||
switch is unrecognized."
|
||||
(let* ((opts options)
|
||||
found)
|
||||
(while opts
|
||||
(if (and (listp (car opts))
|
||||
(nth kind (car opts))
|
||||
(if (= kind 0)
|
||||
(eq switch (nth kind (car opts)))
|
||||
(string= switch (nth kind (car opts)))))
|
||||
(progn
|
||||
(eshell-set-option name ai (car opts) options)
|
||||
(setq found t opts nil))
|
||||
(setq opts (cdr opts))))
|
||||
(unless found
|
||||
(let ((extcmd (memq ':external options)))
|
||||
(when extcmd
|
||||
(setq extcmd (eshell-search-path (cadr extcmd)))
|
||||
(if extcmd
|
||||
(throw 'eshell-ext-command extcmd)
|
||||
(if (char-valid-p switch)
|
||||
(error "%s: unrecognized option -%c" name switch)
|
||||
(error "%s: unrecognized option --%s" name switch))))))))
|
||||
|
||||
(defun eshell-process-args (name args options)
|
||||
"Process the given ARGS using OPTIONS.
|
||||
This assumes that symbols have been intern'd by `eshell-with-options'."
|
||||
(let ((ai 0) arg)
|
||||
(while (< ai (length args))
|
||||
(setq arg (nth ai args))
|
||||
(if (not (and (stringp arg)
|
||||
(string-match "^-\\(-\\)?\\(.*\\)" arg)))
|
||||
(setq ai (1+ ai))
|
||||
(let* ((dash (match-string 1 arg))
|
||||
(switch (match-string 2 arg)))
|
||||
(if (= ai 0)
|
||||
(setq args (cdr args))
|
||||
(setcdr (nthcdr (1- ai) args) (nthcdr (1+ ai) args)))
|
||||
(if dash
|
||||
(if (> (length switch) 0)
|
||||
(eshell-process-option name switch 1 ai options)
|
||||
(setq ai (length args)))
|
||||
(let ((len (length switch))
|
||||
(index 0))
|
||||
(while (< index len)
|
||||
(eshell-process-option name (aref switch index) 0 ai options)
|
||||
(setq index (1+ index)))))))))
|
||||
args)
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; esh-opt.el ends here
|
447
lisp/eshell/esh-proc.el
Normal file
447
lisp/eshell/esh-proc.el
Normal file
@ -0,0 +1,447 @@
|
||||
;;; esh-proc --- process management
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'esh-proc)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-proc nil
|
||||
"When Eshell invokes external commands, it always does so
|
||||
asynchronously, so that Emacs isn't tied up waiting for the process to
|
||||
finish."
|
||||
:tag "Process management"
|
||||
:group 'eshell)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-proc-load-hook '(eshell-proc-initialize)
|
||||
"*A hook that gets run when `eshell-proc' is loaded."
|
||||
:type 'hook
|
||||
:group 'eshell-proc)
|
||||
|
||||
(defcustom eshell-process-wait-seconds 0
|
||||
"*The number of seconds to delay waiting for a synchronous process."
|
||||
:type 'integer
|
||||
:group 'eshell-proc)
|
||||
|
||||
(defcustom eshell-process-wait-milliseconds 50
|
||||
"*The number of milliseconds to delay waiting for a synchronous process."
|
||||
:type 'integer
|
||||
:group 'eshell-proc)
|
||||
|
||||
(defcustom eshell-done-messages-in-minibuffer t
|
||||
"*If non-nil, subjob \"Done\" messages will display in minibuffer."
|
||||
:type 'boolean
|
||||
:group 'eshell-proc)
|
||||
|
||||
(defcustom eshell-delete-exited-processes t
|
||||
"*If nil, process entries will stick around until `jobs' is run.
|
||||
This variable sets the buffer-local value of `delete-exited-processes'
|
||||
in Eshell buffers.
|
||||
|
||||
This variable causes Eshell to mimic the behavior of bash when set to
|
||||
nil. It allows the user to view the exit status of a completed subjob
|
||||
\(process) at their leisure, because the process entry remains in
|
||||
memory until the user examines it using \\[list-processes].
|
||||
|
||||
Otherwise, if `eshell-done-messages-in-minibuffer' is nil, and this
|
||||
variable is set to t, the only indication the user will have that a
|
||||
subjob is done is that it will no longer appear in the
|
||||
\\[list-processes\\] display.
|
||||
|
||||
Note that Eshell will have to be restarted for a change in this
|
||||
variable's value to take effect."
|
||||
:type 'boolean
|
||||
:group 'eshell-proc)
|
||||
|
||||
(defcustom eshell-reset-signals
|
||||
"^\\(interrupt\\|killed\\|quit\\|stopped\\)"
|
||||
"*If a termination signal matches this regexp, the terminal will be reset."
|
||||
:type 'regexp
|
||||
:group 'eshell-proc)
|
||||
|
||||
(defcustom eshell-exec-hook nil
|
||||
"*Called each time a process is exec'd by `eshell-gather-process-output'.
|
||||
It is passed one argument, which is the process that was just started.
|
||||
It is useful for things that must be done each time a process is
|
||||
executed in a eshell mode buffer (e.g., `process-kill-without-query').
|
||||
In contrast, `eshell-mode-hook' is only executed once when the buffer
|
||||
is created."
|
||||
:type 'hook
|
||||
:group 'eshell-proc)
|
||||
|
||||
(defcustom eshell-kill-hook '(eshell-reset-after-proc)
|
||||
"*Called when a process run by `eshell-gather-process-output' has ended.
|
||||
It is passed two arguments: the process that was just ended, and the
|
||||
termination status (as a string). Note that the first argument may be
|
||||
nil, in which case the user attempted to send a signal, but there was
|
||||
no relevant process. This can be used for displaying help
|
||||
information, for example."
|
||||
:type 'hook
|
||||
:group 'eshell-proc)
|
||||
|
||||
;;; Internal Variables:
|
||||
|
||||
(defvar eshell-current-subjob-p nil)
|
||||
|
||||
(defvar eshell-process-list nil
|
||||
"A list of the current status of subprocesses.")
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-proc-initialize ()
|
||||
"Initialize the process handling code."
|
||||
(make-local-variable 'eshell-process-list)
|
||||
(define-key eshell-command-map [(meta ?i)] 'eshell-insert-process)
|
||||
(define-key eshell-command-map [(control ?c)] 'eshell-interrupt-process)
|
||||
(define-key eshell-command-map [(control ?k)] 'eshell-kill-process)
|
||||
(define-key eshell-command-map [(control ?d)] 'eshell-send-eof-to-process)
|
||||
(define-key eshell-command-map [(control ?q)] 'eshell-continue-process)
|
||||
(define-key eshell-command-map [(control ?s)] 'list-processes)
|
||||
(define-key eshell-command-map [(control ?z)] 'eshell-stop-process)
|
||||
(define-key eshell-command-map [(control ?\\)] 'eshell-quit-process))
|
||||
|
||||
(defun eshell-reset-after-proc (proc status)
|
||||
"Reset the command input location after a process terminates.
|
||||
The signals which will cause this to happen are matched by
|
||||
`eshell-reset-signals'."
|
||||
(if (string-match eshell-reset-signals status)
|
||||
(eshell-reset)))
|
||||
|
||||
(defun eshell-wait-for-process (&rest procs)
|
||||
"Wait until PROC has successfully completed."
|
||||
(while procs
|
||||
(let ((proc (car procs)))
|
||||
(when (processp proc)
|
||||
;; NYI: If the process gets stopped here, that's bad.
|
||||
(while (assq proc eshell-process-list)
|
||||
(if (input-pending-p)
|
||||
(discard-input))
|
||||
(sit-for eshell-process-wait-seconds
|
||||
eshell-process-wait-milliseconds))))
|
||||
(setq procs (cdr procs))))
|
||||
|
||||
(defalias 'eshell/wait 'eshell-wait-for-process)
|
||||
|
||||
(defun eshell/jobs (&rest args)
|
||||
"List processes, if there are any."
|
||||
(and (process-list)
|
||||
(list-processes)))
|
||||
|
||||
(defun eshell/kill (&rest args)
|
||||
"Kill processes, buffers, symbol or files."
|
||||
(let ((ptr args)
|
||||
(signum 'SIGINT))
|
||||
(while ptr
|
||||
(if (or (processp (car ptr))
|
||||
(and (stringp (car ptr))
|
||||
(string-match "^[A-Za-z/][A-Za-z0-9<>/]+$"
|
||||
(car ptr))))
|
||||
;; What about when $lisp-variable is possible here?
|
||||
;; It could very well name a process.
|
||||
(setcar ptr (get-process (car ptr))))
|
||||
(setq ptr (cdr ptr)))
|
||||
(while args
|
||||
(let ((id (if (processp (car args))
|
||||
(process-id (car args))
|
||||
(car args))))
|
||||
(when id
|
||||
(cond
|
||||
((null id)
|
||||
(error "kill: bad signal spec"))
|
||||
((and (numberp id) (= id 0))
|
||||
(error "kill: bad signal spec `%d'" id))
|
||||
((and (stringp id)
|
||||
(string-match "^-?[0-9]+$" id))
|
||||
(setq signum (abs (string-to-number id))))
|
||||
((stringp id)
|
||||
(let (case-fold-search)
|
||||
(if (string-match "^-\\([A-Z]+\\)$" id)
|
||||
(setq signum
|
||||
(intern (concat "SIG" (match-string 1 id))))
|
||||
(error "kill: bad signal spec `%s'" id))))
|
||||
((< id 0)
|
||||
(setq signum (abs id)))
|
||||
(t
|
||||
(signal-process id signum)))))
|
||||
(setq args (cdr args)))
|
||||
nil))
|
||||
|
||||
(defun eshell-read-process-name (prompt)
|
||||
"Read the name of a process from the minibuffer, using completion.
|
||||
The prompt will be set to PROMPT."
|
||||
(completing-read prompt
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (proc)
|
||||
(cons (process-name proc) t)))
|
||||
(process-list)) nil t))
|
||||
|
||||
(defun eshell-insert-process (process)
|
||||
"Insert the name of PROCESS into the current buffer at point."
|
||||
(interactive
|
||||
(list (get-process
|
||||
(eshell-read-process-name "Name of process: "))))
|
||||
(insert-and-inherit "#<process " (process-name process) ">"))
|
||||
|
||||
(defsubst eshell-record-process-object (object)
|
||||
"Record OBJECT as now running."
|
||||
(if (and (processp object)
|
||||
eshell-current-subjob-p)
|
||||
(eshell-interactive-print
|
||||
(format "[%s] %d\n" (process-name object) (process-id object))))
|
||||
(setq eshell-process-list
|
||||
(cons (list object eshell-current-handles
|
||||
eshell-current-subjob-p nil nil)
|
||||
eshell-process-list)))
|
||||
|
||||
(defun eshell-remove-process-entry (entry)
|
||||
"Record the process ENTRY as fully completed."
|
||||
(if (and (processp (car entry))
|
||||
(nth 2 entry)
|
||||
eshell-done-messages-in-minibuffer)
|
||||
(message (format "[%s]+ Done %s" (process-name (car entry))
|
||||
(process-command (car entry)))))
|
||||
(setq eshell-process-list
|
||||
(delq entry eshell-process-list)))
|
||||
|
||||
(defun eshell-gather-process-output (command args)
|
||||
"Gather the output from COMMAND + ARGS."
|
||||
(unless (and (file-executable-p command)
|
||||
(file-regular-p command))
|
||||
(error "%s: not an executable file" command))
|
||||
(let* ((delete-exited-processes
|
||||
(if eshell-current-subjob-p
|
||||
eshell-delete-exited-processes
|
||||
delete-exited-processes))
|
||||
(process-environment (eshell-environment-variables))
|
||||
(proc (apply 'start-process
|
||||
(file-name-nondirectory command) nil
|
||||
;; `start-process' can't deal with relative
|
||||
;; filenames
|
||||
(append (list (expand-file-name command)) args)))
|
||||
decoding encoding changed)
|
||||
(eshell-record-process-object proc)
|
||||
(set-process-buffer proc (current-buffer))
|
||||
(if (eshell-interactive-output-p)
|
||||
(set-process-filter proc 'eshell-output-filter)
|
||||
(set-process-filter proc 'eshell-insertion-filter))
|
||||
(set-process-sentinel proc 'eshell-sentinel)
|
||||
(run-hook-with-args 'eshell-exec-hook proc)
|
||||
(when (fboundp 'process-coding-system)
|
||||
(let ((coding-systems (process-coding-system proc)))
|
||||
(setq decoding (car coding-systems)
|
||||
encoding (cdr coding-systems)))
|
||||
;; If start-process decided to use some coding system for
|
||||
;; decoding data sent from the process and the coding system
|
||||
;; doesn't specify EOL conversion, we had better convert CRLF
|
||||
;; to LF.
|
||||
(if (vectorp (coding-system-eol-type decoding))
|
||||
(setq decoding (coding-system-change-eol-conversion decoding 'dos)
|
||||
changed t))
|
||||
;; Even if start-process left the coding system for encoding
|
||||
;; data sent from the process undecided, we had better use the
|
||||
;; same one as what we use for decoding. But, we should
|
||||
;; suppress EOL conversion.
|
||||
(if (and decoding (not encoding))
|
||||
(setq encoding (coding-system-change-eol-conversion decoding 'unix)
|
||||
changed t))
|
||||
(if changed
|
||||
(set-process-coding-system proc decoding encoding)))
|
||||
proc))
|
||||
|
||||
(defun eshell-insertion-filter (proc string)
|
||||
"Insert a string into the eshell buffer, or a process/file/buffer.
|
||||
PROC is the process for which we're inserting output. STRING is the
|
||||
output."
|
||||
(when (buffer-live-p (process-buffer proc))
|
||||
(set-buffer (process-buffer proc))
|
||||
(let ((entry (assq proc eshell-process-list)))
|
||||
(when entry
|
||||
(setcar (nthcdr 3 entry)
|
||||
(concat (nth 3 entry) string))
|
||||
(unless (nth 4 entry) ; already being handled?
|
||||
(while (nth 3 entry)
|
||||
(let ((data (nth 3 entry)))
|
||||
(setcar (nthcdr 3 entry) nil)
|
||||
(setcar (nthcdr 4 entry) t)
|
||||
(eshell-output-object data nil (cadr entry))
|
||||
(setcar (nthcdr 4 entry) nil))))))))
|
||||
|
||||
(defun eshell-sentinel (proc string)
|
||||
"Generic sentinel for command processes. Reports only signals.
|
||||
PROC is the process that's exiting. STRING is the exit message."
|
||||
(when (buffer-live-p (process-buffer proc))
|
||||
(set-buffer (process-buffer proc))
|
||||
(unwind-protect
|
||||
(let* ((entry (assq proc eshell-process-list)))
|
||||
; (if (not entry)
|
||||
; (error "Sentinel called for unowned process `%s'"
|
||||
; (process-name proc))
|
||||
(when entry
|
||||
(unwind-protect
|
||||
(progn
|
||||
(unless (string= string "run")
|
||||
(unless (string-match "^\\(finished\\|exited\\)" string)
|
||||
(eshell-insertion-filter proc string))
|
||||
(eshell-close-handles (process-exit-status proc) 'nil
|
||||
(cadr entry))))
|
||||
(eshell-remove-process-entry entry))))
|
||||
(run-hook-with-args 'eshell-kill-hook proc string))))
|
||||
|
||||
(defun eshell-process-interact (func &optional all query)
|
||||
"Interact with a process, using PROMPT if more than one, via FUNC.
|
||||
If ALL is non-nil, background processes will be interacted with as well.
|
||||
If QUERY is non-nil, query the user with QUERY before calling FUNC."
|
||||
(let (defunct result)
|
||||
(eshell-for entry eshell-process-list
|
||||
(if (and (memq (process-status (car entry))
|
||||
'(run stop open closed))
|
||||
(or all
|
||||
(not (nth 2 entry)))
|
||||
(or (not query)
|
||||
(y-or-n-p (format query (process-name (car entry))))))
|
||||
(setq result (funcall func (car entry))))
|
||||
(unless (memq (process-status (car entry))
|
||||
'(run stop open closed))
|
||||
(setq defunct (cons entry defunct))))
|
||||
;; clean up the process list; this can get dirty if an error
|
||||
;; occurred that brought the user into the debugger, and then they
|
||||
;; quit, so that the sentinel was never called.
|
||||
(eshell-for d defunct
|
||||
(eshell-remove-process-entry d))
|
||||
result))
|
||||
|
||||
(defcustom eshell-kill-process-wait-time 5
|
||||
"*Seconds to wait between sending termination signals to a subprocess."
|
||||
:type 'integer
|
||||
:group 'eshell-proc)
|
||||
|
||||
(defcustom eshell-kill-process-signals '(SIGINT SIGQUIT SIGKILL)
|
||||
"*Signals used to kill processes when an Eshell buffer exits.
|
||||
Eshell calls each of these signals in order when an Eshell buffer is
|
||||
killed; if the process is still alive afterwards, Eshell waits a
|
||||
number of seconds defined by `eshell-kill-process-wait-time', and
|
||||
tries the next signal in the list."
|
||||
:type '(repeat symbol)
|
||||
:group 'eshell-proc)
|
||||
|
||||
(defcustom eshell-kill-processes-on-exit nil
|
||||
"*If non-nil, kill active processes when exiting an Eshell buffer.
|
||||
Emacs will only kill processes owned by that Eshell buffer.
|
||||
|
||||
If nil, ownership of background and foreground processes reverts to
|
||||
Emacs itself, and will die only if the user exits Emacs, calls
|
||||
`kill-process', or terminates the processes externally.
|
||||
|
||||
If `ask', Emacs prompts the user before killing any processes.
|
||||
|
||||
If `every', it prompts once for every process.
|
||||
|
||||
If t, it kills all buffer-owned processes without asking.
|
||||
|
||||
Processes are first sent SIGHUP, then SIGINT, then SIGQUIT, then
|
||||
SIGKILL. The variable `eshell-kill-process-wait-time' specifies how
|
||||
long to delay between signals."
|
||||
:type '(choice (const :tag "Kill all, don't ask" t)
|
||||
(const :tag "Ask before killing" ask)
|
||||
(const :tag "Ask for each process" every)
|
||||
(const :tag "Don't kill subprocesses" nil))
|
||||
:group 'eshell-proc)
|
||||
|
||||
(defun eshell-round-robin-kill (&optional query)
|
||||
"Kill current process by trying various signals in sequence.
|
||||
See the variable `eshell-kill-processes-on-exit'."
|
||||
(let ((sigs eshell-kill-process-signals))
|
||||
(while sigs
|
||||
(eshell-process-interact
|
||||
(function
|
||||
(lambda (proc)
|
||||
(signal-process (process-id proc) (car sigs)))) t query)
|
||||
(setq query nil)
|
||||
(if (not eshell-process-list)
|
||||
(setq sigs nil)
|
||||
(sleep-for eshell-kill-process-wait-time)
|
||||
(setq sigs (cdr sigs))))))
|
||||
|
||||
(defun eshell-query-kill-processes ()
|
||||
"Kill processes belonging to the current Eshell buffer, possibly w/ query."
|
||||
(when (and eshell-kill-processes-on-exit
|
||||
eshell-process-list)
|
||||
(save-window-excursion
|
||||
(list-processes)
|
||||
(if (or (not (eq eshell-kill-processes-on-exit 'ask))
|
||||
(y-or-n-p (format "Kill processes owned by `%s'? "
|
||||
(buffer-name))))
|
||||
(eshell-round-robin-kill
|
||||
(if (eq eshell-kill-processes-on-exit 'every)
|
||||
"Kill Eshell child process `%s'? ")))
|
||||
(let ((buf (get-buffer "*Process List*")))
|
||||
(if (and buf (buffer-live-p buf))
|
||||
(kill-buffer buf)))
|
||||
(message nil))))
|
||||
|
||||
(custom-add-option 'eshell-exit-hook 'eshell-query-kill-processes)
|
||||
|
||||
(defun eshell-interrupt-process ()
|
||||
"Interrupt a process."
|
||||
(interactive)
|
||||
(unless (eshell-process-interact 'interrupt-process)
|
||||
(run-hook-with-args 'eshell-kill-hook nil "interrupt")))
|
||||
|
||||
(defun eshell-kill-process ()
|
||||
"Kill a process."
|
||||
(interactive)
|
||||
(unless (eshell-process-interact 'kill-process)
|
||||
(run-hook-with-args 'eshell-kill-hook nil "killed")))
|
||||
|
||||
(defun eshell-quit-process ()
|
||||
"Send quit signal to process."
|
||||
(interactive)
|
||||
(unless (eshell-process-interact 'quit-process)
|
||||
(run-hook-with-args 'eshell-kill-hook nil "quit")))
|
||||
|
||||
(defun eshell-stop-process ()
|
||||
"Send STOP signal to process."
|
||||
(interactive)
|
||||
(unless (eshell-process-interact 'stop-process)
|
||||
(run-hook-with-args 'eshell-kill-hook nil "stopped")))
|
||||
|
||||
(defun eshell-continue-process ()
|
||||
"Send CONTINUE signal to process."
|
||||
(interactive)
|
||||
(unless (eshell-process-interact 'continue-process)
|
||||
;; jww (1999-09-17): this signal is not dealt with yet. For
|
||||
;; example, `eshell-reset' will be called, and so will
|
||||
;; `eshell-resume-eval'.
|
||||
(run-hook-with-args 'eshell-kill-hook nil "continue")))
|
||||
|
||||
(defun eshell-send-eof-to-process ()
|
||||
"Send EOF to process."
|
||||
(interactive)
|
||||
(eshell-send-input nil nil t)
|
||||
(eshell-process-interact 'process-send-eof))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; esh-proc.el ends here
|
242
lisp/eshell/esh-test.el
Normal file
242
lisp/eshell/esh-test.el
Normal file
@ -0,0 +1,242 @@
|
||||
;;; esh-test --- Eshell test suite
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'esh-test)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-test nil
|
||||
"This module is meant to ensure that Eshell is working correctly."
|
||||
:tag "Eshell test suite"
|
||||
:group 'eshell)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; The purpose of this module is to verify that Eshell works as
|
||||
;; expected. To run it on your system, use the command
|
||||
;; \\[eshell-test].
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'esh-mode)
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defface eshell-test-ok-face
|
||||
'((((class color) (background light)) (:foreground "Green" :bold t))
|
||||
(((class color) (background dark)) (:foreground "Green" :bold t)))
|
||||
"*The face used to highlight OK result strings."
|
||||
:group 'eshell-test)
|
||||
|
||||
(defface eshell-test-failed-face
|
||||
'((((class color) (background light)) (:foreground "OrangeRed" :bold t))
|
||||
(((class color) (background dark)) (:foreground "OrangeRed" :bold t))
|
||||
(t (:bold t)))
|
||||
"*The face used to highlight FAILED result strings."
|
||||
:group 'eshell-test)
|
||||
|
||||
(defcustom eshell-show-usage-metrics nil
|
||||
"*If non-nil, display different usage metrics for each Eshell command."
|
||||
:set (lambda (symbol value)
|
||||
(if value
|
||||
(add-hook 'eshell-mode-hook 'eshell-show-usage-metrics)
|
||||
(remove-hook 'eshell-mode-hook 'eshell-show-usage-metrics))
|
||||
(set symbol value))
|
||||
:type '(choice (const :tag "No metrics" nil)
|
||||
(const :tag "Cons cells consumed" t)
|
||||
(const :tag "Time elapsed" 0))
|
||||
:group 'eshell-test)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile
|
||||
(defvar test-buffer))
|
||||
|
||||
(defun eshell-insert-command (text &optional func)
|
||||
"Insert a command at the end of the buffer."
|
||||
(goto-char eshell-last-output-end)
|
||||
(insert-and-inherit text)
|
||||
(funcall (or func 'eshell-send-input)))
|
||||
|
||||
(defun eshell-match-result (regexp)
|
||||
"Insert a command at the end of the buffer."
|
||||
(goto-char eshell-last-input-end)
|
||||
(looking-at regexp))
|
||||
|
||||
(defun eshell-command-result-p (text regexp &optional func)
|
||||
"Insert a command at the end of the buffer."
|
||||
(eshell-insert-command text func)
|
||||
(eshell-match-result regexp))
|
||||
|
||||
(defvar eshell-test-failures nil)
|
||||
|
||||
(defun eshell-run-test (module funcsym label command)
|
||||
"Test whether FORM evaluates to a non-nil value."
|
||||
(when (let ((sym (intern-soft (concat "eshell-" (symbol-name module)))))
|
||||
(or (memq sym (eshell-subgroups 'eshell))
|
||||
(eshell-using-module sym)))
|
||||
(with-current-buffer test-buffer
|
||||
(insert-before-markers
|
||||
(format "%-70s " (substring label 0 (min 70 (length label)))))
|
||||
(insert-before-markers " ....")
|
||||
(eshell-redisplay))
|
||||
(let ((truth (eval command)))
|
||||
(with-current-buffer test-buffer
|
||||
(delete-backward-char 6)
|
||||
(insert-before-markers
|
||||
"[" (let (str)
|
||||
(if truth
|
||||
(progn
|
||||
(setq str " OK ")
|
||||
(put-text-property 0 6 'face
|
||||
'eshell-test-ok-face str))
|
||||
(setq str "FAILED")
|
||||
(setq eshell-test-failures (1+ eshell-test-failures))
|
||||
(put-text-property 0 6 'face
|
||||
'eshell-test-failed-face str))
|
||||
str) "]")
|
||||
(add-text-properties (line-beginning-position) (point)
|
||||
(list 'test-func funcsym))
|
||||
(eshell-redisplay)))))
|
||||
|
||||
(defun eshell-test-goto-func ()
|
||||
"Jump to the function that defines a particular test."
|
||||
(interactive)
|
||||
(let ((fsym (get-text-property (point) 'test-func)))
|
||||
(when fsym
|
||||
(let* ((def (symbol-function fsym))
|
||||
(library (locate-library (symbol-file fsym)))
|
||||
(name (substring (symbol-name fsym)
|
||||
(length "eshell-test--")))
|
||||
(inhibit-redisplay t))
|
||||
(find-file library)
|
||||
(goto-char (point-min))
|
||||
(re-search-forward (concat "^(eshell-deftest\\s-+\\w+\\s-+"
|
||||
name))
|
||||
(beginning-of-line)))))
|
||||
|
||||
(defun eshell-run-one-test (&optional arg)
|
||||
"Jump to the function that defines a particular test."
|
||||
(interactive "P")
|
||||
(let ((fsym (get-text-property (point) 'test-func)))
|
||||
(when fsym
|
||||
(beginning-of-line)
|
||||
(delete-region (point) (line-end-position))
|
||||
(let ((test-buffer (current-buffer)))
|
||||
(set-buffer (let ((inhibit-redisplay t))
|
||||
(save-window-excursion (eshell t))))
|
||||
(funcall fsym)
|
||||
(unless arg
|
||||
(kill-buffer (current-buffer)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun eshell-test (&optional arg)
|
||||
"Test Eshell to verify that it works as expected."
|
||||
(interactive "P")
|
||||
(let* ((begin (eshell-time-to-seconds (current-time)))
|
||||
(test-buffer (get-buffer-create "*eshell test*")))
|
||||
(set-buffer (let ((inhibit-redisplay t))
|
||||
(save-window-excursion (eshell t))))
|
||||
(with-current-buffer test-buffer
|
||||
(erase-buffer)
|
||||
(setq major-mode 'eshell-test-mode)
|
||||
(setq mode-name "EShell Test")
|
||||
(set (make-local-variable 'eshell-test-failures) 0)
|
||||
(local-set-key [(control ?c) (control ?c)] 'eshell-test-goto-func)
|
||||
(local-set-key [(control ?c) (control ?r)] 'eshell-run-one-test)
|
||||
(local-set-key [(control ?m)] 'eshell-test-goto-func)
|
||||
(local-set-key [return] 'eshell-test-goto-func)
|
||||
|
||||
(insert "Testing Eshell under "
|
||||
(format "GNU Emacs %s (%s%s)"
|
||||
emacs-version
|
||||
system-configuration
|
||||
(cond ((featurep 'motif) ", Motif")
|
||||
((featurep 'x-toolkit) ", X toolkit")
|
||||
(t ""))) "\n")
|
||||
(switch-to-buffer test-buffer)
|
||||
(delete-other-windows))
|
||||
(eshell-for funcname
|
||||
(sort (all-completions "eshell-test--" obarray 'functionp)
|
||||
'string-lessp)
|
||||
(with-current-buffer test-buffer
|
||||
(insert "\n"))
|
||||
(funcall (intern-soft funcname)))
|
||||
(with-current-buffer test-buffer
|
||||
(insert (format "\n\n--- %s --- (completed in %d seconds)\n"
|
||||
(current-time-string)
|
||||
(- (eshell-time-to-seconds (current-time))
|
||||
begin)))
|
||||
(message "Eshell test suite completed: %s failure%s"
|
||||
(if (> eshell-test-failures 0)
|
||||
(number-to-string eshell-test-failures)
|
||||
"No")
|
||||
(if (= eshell-test-failures 1) "" "s"))))
|
||||
(goto-char eshell-last-output-end)
|
||||
(unless arg
|
||||
(kill-buffer (current-buffer))))
|
||||
|
||||
|
||||
(defvar eshell-metric-before-command 0)
|
||||
(defvar eshell-metric-after-command 0)
|
||||
|
||||
(defun eshell-show-usage-metrics ()
|
||||
"If run at Eshell mode startup, metrics are shown after each command."
|
||||
(set (make-local-variable 'eshell-metric-before-command)
|
||||
(if (eq eshell-show-usage-metrics t)
|
||||
0
|
||||
(current-time)))
|
||||
(set (make-local-variable 'eshell-metric-after-command)
|
||||
(if (eq eshell-show-usage-metrics t)
|
||||
0
|
||||
(current-time)))
|
||||
|
||||
(make-local-hook 'eshell-pre-command-hook)
|
||||
(add-hook 'eshell-pre-command-hook
|
||||
(function
|
||||
(lambda ()
|
||||
(setq eshell-metric-before-command
|
||||
(if (eq eshell-show-usage-metrics t)
|
||||
(car (memory-use-counts))
|
||||
(current-time))))) nil t)
|
||||
|
||||
(make-local-hook 'eshell-post-command-hook)
|
||||
(add-hook 'eshell-post-command-hook
|
||||
(function
|
||||
(lambda ()
|
||||
(setq eshell-metric-after-command
|
||||
(if (eq eshell-show-usage-metrics t)
|
||||
(car (memory-use-counts))
|
||||
(current-time)))
|
||||
(eshell-interactive-print
|
||||
(concat
|
||||
(int-to-string
|
||||
(if (eq eshell-show-usage-metrics t)
|
||||
(- eshell-metric-after-command
|
||||
eshell-metric-before-command 7)
|
||||
(- (eshell-time-to-seconds
|
||||
eshell-metric-after-command)
|
||||
(eshell-time-to-seconds
|
||||
eshell-metric-before-command))))
|
||||
"\n"))))
|
||||
nil t))
|
||||
|
||||
;;; esh-test.el ends here
|
179
lisp/eshell/esh-toggle.el
Normal file
179
lisp/eshell/esh-toggle.el
Normal file
@ -0,0 +1,179 @@
|
||||
;;; esh-toggle --- toggle to and from the *eshell* buffer
|
||||
|
||||
;; Copyright (C) 1997, 1998 Mikael Sjödin (mic@docs.uu.se)
|
||||
|
||||
;; Author: Mikael Sjödin <mic@docs.uu.se>
|
||||
;; John Wiegley <johnw@gnu.org>
|
||||
;; Created: 19 Nov 1998
|
||||
;; Version: 2.0
|
||||
;; Keywords: processes
|
||||
;; X-URL: http://www.emacs.org/~johnw/eshell.html
|
||||
|
||||
;; This program is free software; you can redistribute it and/or
|
||||
;; modify it under the terms of the GNU General Public License as
|
||||
;; published by the Free Software Foundation; either version 2, or (at
|
||||
;; your option) any later version.
|
||||
|
||||
;; This program 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Provides the command eshell-toggle which toggles between the
|
||||
;; *eshell* buffer and whatever buffer you are editing.
|
||||
;;
|
||||
;; This is done in an "intelligent" way. Features are:
|
||||
;;
|
||||
;; - Starts a eshell if non is existing.
|
||||
;;
|
||||
;; - Minimum distortion of your window configuration.
|
||||
;;
|
||||
;; - When done in the eshell-buffer you are returned to the same
|
||||
;; window configuration you had before you toggled to the eshell.
|
||||
;;
|
||||
;; - If you desire, you automagically get a "cd" command in the
|
||||
;; eshell to the directory where your current buffers file exists;
|
||||
;; just call eshell-toggle-cd instead of eshell-toggle.
|
||||
;;
|
||||
;; - You can convinently choose if you want to have the eshell in
|
||||
;; another window or in the whole frame. Just invoke eshell-toggle
|
||||
;; again to get the eshell in the whole frame.
|
||||
;;
|
||||
;; This file has been tested under Emacs 20.2.
|
||||
;;
|
||||
;; To use, call the functions `eshell-toggle' or `eshell-toggle-cd'.
|
||||
;; It's most helpful to bind these to a key.
|
||||
|
||||
;;; Thanks to:
|
||||
|
||||
;; Christian Stern <Christian.Stern@physik.uni-regensburg.de> for
|
||||
;; helpful sugestions.
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defvar eshell-toggle-goto-eob t
|
||||
"*If non-nil `eshell-toggle' moves point to end of Eshell buffer.
|
||||
When `eshell-toggle-cd' is called the point is always moved to the
|
||||
end of the eshell-buffer")
|
||||
|
||||
(defvar eshell-toggle-automatic-cd t
|
||||
"*If non-nil `eshell-toggle-cd' will send a \"cd\" to Eshell.
|
||||
If nil `eshell-toggle-cd' will only insert the \"cd\" command in the
|
||||
eshell-buffer. Leaving it to the user to press RET to send the
|
||||
command to the eshell.")
|
||||
|
||||
;;; User Functions:
|
||||
|
||||
;;;###autoload
|
||||
(defun eshell-toggle-cd ()
|
||||
"Calls `eshell-toggle' with a prefix argument.
|
||||
See the command `eshell-toggle'"
|
||||
(interactive)
|
||||
(eshell-toggle t))
|
||||
|
||||
;;;###autoload
|
||||
(defun eshell-toggle (make-cd)
|
||||
"Toggles between the *eshell* buffer and the current buffer.
|
||||
With a prefix ARG also insert a \"cd DIR\" command into the eshell,
|
||||
where DIR is the directory of the current buffer.
|
||||
|
||||
Call twice in a row to get a full screen window for the *eshell*
|
||||
buffer.
|
||||
|
||||
When called in the *eshell* buffer returns you to the buffer you were
|
||||
editing before caling the first time.
|
||||
|
||||
Options: `eshell-toggle-goto-eob'"
|
||||
(interactive "P")
|
||||
;; Try to descide on one of three possibilities:
|
||||
;; 1. If not in eshell-buffer, switch to it.
|
||||
;; 2. If in eshell-buffer and called twice in a row, delete other
|
||||
;; windows
|
||||
;; 3. If in eshell-buffer and not called twice in a row, return to
|
||||
;; state before going to the eshell-buffer
|
||||
(if (eq major-mode 'eshell-mode)
|
||||
(if (and (or (eq last-command 'eshell-toggle)
|
||||
(eq last-command 'eshell-toggle-cd))
|
||||
(not (eq (count-windows) 1)))
|
||||
(delete-other-windows)
|
||||
(eshell-toggle-buffer-return-from-eshell))
|
||||
(eshell-toggle-buffer-goto-eshell make-cd)))
|
||||
|
||||
;;; Internal Functions:
|
||||
|
||||
(defvar eshell-toggle-pre-eshell-win-conf nil
|
||||
"Contains window config before the *eshell* buffer was selected")
|
||||
|
||||
(defun eshell-toggle-buffer-return-from-eshell ()
|
||||
"Restores window config used before switching the *eshell* buffer.
|
||||
If no configuration has been stored, just bury the *eshell* buffer."
|
||||
(if (window-configuration-p eshell-toggle-pre-eshell-win-conf)
|
||||
(progn
|
||||
(set-window-configuration eshell-toggle-pre-eshell-win-conf)
|
||||
(setq eshell-toggle-pre-eshell-win-conf nil)
|
||||
(bury-buffer (get-buffer "*eshell*")))
|
||||
(bury-buffer)))
|
||||
|
||||
(defun eshell-toggle-buffer-goto-eshell (make-cd)
|
||||
"Switches other window to the *eshell* buffer.
|
||||
If no *eshell* buffer exists start a new eshell and switch to it in
|
||||
other window. If argument MAKE-CD is non-nil, insert a \"cd DIR\"
|
||||
command into the eshell, where DIR is the directory of the current
|
||||
buffer.
|
||||
Stores the window cofiguration before creating and/or switching window."
|
||||
(setq eshell-toggle-pre-eshell-win-conf (current-window-configuration))
|
||||
(let ((eshell-buffer (get-buffer "*eshell*"))
|
||||
(cd-command
|
||||
;; Find out which directory we are in (the method differs for
|
||||
;; different buffers)
|
||||
(or (and make-cd
|
||||
(buffer-file-name)
|
||||
(file-name-directory (buffer-file-name))
|
||||
(concat "cd " (file-name-directory (buffer-file-name))))
|
||||
(and make-cd
|
||||
list-buffers-directory
|
||||
(concat "cd " list-buffers-directory)))))
|
||||
;; Switch to an existin eshell if one exists, otherwise switch to
|
||||
;; another window and start a new eshell
|
||||
(if eshell-buffer
|
||||
(switch-to-buffer-other-window eshell-buffer)
|
||||
(eshell-toggle-buffer-switch-to-other-window)
|
||||
;; Sometimes an error is generated when I call `eshell' (it has
|
||||
;; to do with my eshell-mode-hook which inserts text into the
|
||||
;; newly created eshell-buffer and thats not allways a good
|
||||
;; idea).
|
||||
(condition-case the-error
|
||||
(eshell)
|
||||
(error (switch-to-buffer "*eshell*"))))
|
||||
(if (or cd-command eshell-toggle-goto-eob)
|
||||
(goto-char (point-max)))
|
||||
(if cd-command
|
||||
(progn
|
||||
(insert cd-command)
|
||||
(if eshell-toggle-automatic-cd
|
||||
(eshell-send-input))))))
|
||||
|
||||
(defun eshell-toggle-buffer-switch-to-other-window ()
|
||||
"Switches to other window.
|
||||
If the current window is the only window in the current frame, create
|
||||
a new window and switch to it. (This is less intrusive to the current
|
||||
window configuration then `switch-buffer-other-window')"
|
||||
(let ((this-window (selected-window)))
|
||||
(other-window 1)
|
||||
;; If we did not switch window then we only have one window and
|
||||
;; need to create a new one.
|
||||
(if (eq this-window (selected-window))
|
||||
(progn
|
||||
(split-window-vertically)
|
||||
(other-window 1)))))
|
||||
|
||||
(provide 'esh-toggle)
|
||||
|
||||
;;; esh-toggle.el ends here
|
635
lisp/eshell/esh-var.el
Normal file
635
lisp/eshell/esh-var.el
Normal file
@ -0,0 +1,635 @@
|
||||
;;; esh-var --- handling of variables
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'esh-var)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell-var nil
|
||||
"Variable interpolation is introduced whenever the '$' character
|
||||
appears unquoted in any argument (except when that argument is
|
||||
surrounded by single quotes) . It may be used to interpolate a
|
||||
variable value, a subcommand, or even the result of a Lisp form."
|
||||
:tag "Variable handling"
|
||||
:group 'eshell)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; These are the possible variable interpolation syntaxes. Also keep
|
||||
;; in mind that if an argument looks like a number, it will be
|
||||
;; converted to a number. This is not significant when invoking
|
||||
;; external commands, but it's important when calling Lisp functions.
|
||||
;;
|
||||
;; $VARIABLE
|
||||
;;
|
||||
;; Interval the value of an environment variable, or a Lisp variable
|
||||
;;
|
||||
;; $ALSO-VAR
|
||||
;;
|
||||
;; "-" is a legal part of a variable name.
|
||||
;;
|
||||
;; $<MYVAR>-TOO
|
||||
;;
|
||||
;; Only "MYVAR" is part of the variable name in this case.
|
||||
;;
|
||||
;; $#VARIABLE
|
||||
;;
|
||||
;; Returns the length of the value of VARIABLE. This could also be
|
||||
;; done using the `length' Lisp function.
|
||||
;;
|
||||
;; $(lisp)
|
||||
;;
|
||||
;; Returns result of lisp evaluation. Note: Used alone like this, it
|
||||
;; is identical to just saying (lisp); but with the variable expansion
|
||||
;; form, the result may be interpolated a larger string, such as
|
||||
;; '$(lisp)/other'.
|
||||
;;
|
||||
;; ${command}
|
||||
;;
|
||||
;; Returns the value of an eshell subcommand. See the note above
|
||||
;; regarding Lisp evaluations.
|
||||
;;
|
||||
;; $ANYVAR[10]
|
||||
;;
|
||||
;; Return the 10th element of ANYVAR. If ANYVAR's value is a string,
|
||||
;; it will be split in order to make it a list. The splitting will
|
||||
;; occur at whitespace.
|
||||
;;
|
||||
;; $ANYVAR[: 10]
|
||||
;;
|
||||
;; As above, except that splitting occurs at the colon now.
|
||||
;;
|
||||
;; $ANYVAR[: 10 20]
|
||||
;;
|
||||
;; As above, but instead of returning just a string, it now returns a
|
||||
;; list of two strings. If the result is being interpolated into a
|
||||
;; larger string, this list will be flattened into one big string,
|
||||
;; with each element separated by a space.
|
||||
;;
|
||||
;; $ANYVAR["\\\\" 10]
|
||||
;;
|
||||
;; Separate on backslash characters. Actually, the first argument --
|
||||
;; if it doesn't have the form of a number, or a plain variable name
|
||||
;; -- can be any regular expression. So to split on numbers, use
|
||||
;; '$ANYVAR["[0-9]+" 10 20]'.
|
||||
;;
|
||||
;; $ANYVAR[hello]
|
||||
;;
|
||||
;; Calls `assoc' on ANYVAR with 'hello', expecting it to be an alist.
|
||||
;;
|
||||
;; $#ANYVAR[hello]
|
||||
;;
|
||||
;; Returns the length of the cdr of the element of ANYVAR who car is
|
||||
;; equal to "hello".
|
||||
;;
|
||||
;; There are also a few special variables defined by Eshell. '$$' is
|
||||
;; the value of the last command (t or nil, in the case of an external
|
||||
;; command). This makes it possible to chain results:
|
||||
;;
|
||||
;; /tmp $ echo /var/spool/mail/johnw
|
||||
;; /var/spool/mail/johnw
|
||||
;; /tmp $ dirname $$
|
||||
;; /var/spool/mail/
|
||||
;; /tmp $ cd $$
|
||||
;; /var/spool/mail $
|
||||
;;
|
||||
;; '$_' refers to the last argument of the last command. And $?
|
||||
;; contains the exit code of the last command (0 or 1 for Lisp
|
||||
;; functions, based on successful completion).
|
||||
|
||||
(require 'env)
|
||||
(require 'ring)
|
||||
|
||||
;;; User Variables:
|
||||
|
||||
(defcustom eshell-var-load-hook '(eshell-var-initialize)
|
||||
"*A list of functions to call when loading `eshell-var'."
|
||||
:type 'hook
|
||||
:group 'eshell-var)
|
||||
|
||||
(defcustom eshell-prefer-lisp-variables nil
|
||||
"*If non-nil, prefer Lisp variables to environment variables."
|
||||
:type 'boolean
|
||||
:group 'eshell-var)
|
||||
|
||||
(defcustom eshell-complete-export-definition t
|
||||
"*If non-nil, completing names for `export' shows current definition."
|
||||
:type 'boolean
|
||||
:group 'eshell-var)
|
||||
|
||||
(defcustom eshell-variable-name-regexp "[A-Za-z0-9_-]+"
|
||||
"*A regexp identifying what constitutes a variable name reference.
|
||||
Note that this only applies for '$NAME'. If the syntax '$<NAME>' is
|
||||
used, then NAME can contain any character, including angle brackets,
|
||||
if they are quoted with a backslash."
|
||||
:type 'regexp
|
||||
:group 'eshell-var)
|
||||
|
||||
(defcustom eshell-variable-aliases-list
|
||||
'(;; for eshell.el
|
||||
("COLUMNS" (lambda (indices) (window-width)) t)
|
||||
("LINES" (lambda (indices) (window-height)) t)
|
||||
|
||||
;; for eshell-cmd.el
|
||||
("_" (lambda (indices)
|
||||
(if (not indices)
|
||||
(car (last eshell-last-arguments))
|
||||
(eshell-apply-indices eshell-last-arguments
|
||||
indices))))
|
||||
("?" eshell-last-command-status)
|
||||
("$" eshell-last-command-result)
|
||||
("0" eshell-command-name)
|
||||
("1" (lambda (indices) (nth 0 eshell-command-arguments)))
|
||||
("2" (lambda (indices) (nth 1 eshell-command-arguments)))
|
||||
("3" (lambda (indices) (nth 2 eshell-command-arguments)))
|
||||
("4" (lambda (indices) (nth 3 eshell-command-arguments)))
|
||||
("5" (lambda (indices) (nth 4 eshell-command-arguments)))
|
||||
("6" (lambda (indices) (nth 5 eshell-command-arguments)))
|
||||
("7" (lambda (indices) (nth 6 eshell-command-arguments)))
|
||||
("8" (lambda (indices) (nth 7 eshell-command-arguments)))
|
||||
("9" (lambda (indices) (nth 8 eshell-command-arguments)))
|
||||
("*" (lambda (indices)
|
||||
(if (not indices)
|
||||
eshell-command-arguments
|
||||
(eshell-apply-indices eshell-command-arguments
|
||||
indices)))))
|
||||
"*This list provides aliasing for variable references.
|
||||
It is very similar in concept to what `eshell-user-aliases-list' does
|
||||
for commands. Each member of this defines defines the name of a
|
||||
command, and the Lisp value to return for that variable if it is
|
||||
accessed via the syntax '$NAME'.
|
||||
|
||||
If the value is a function, that function will be called with two
|
||||
arguments: the list of the indices that was used in the reference, and
|
||||
whether the user is requesting the length of the ultimate element.
|
||||
For example, a reference of '$NAME[10][20]' would result in the
|
||||
function for alias `NAME' being called (assuming it were aliased to a
|
||||
function), and the arguments passed to this function would be the list
|
||||
'(10 20)', and nil."
|
||||
:type '(repeat (list string sexp
|
||||
(choice (const :tag "Copy to environment" t)
|
||||
(const :tag "Use only in Eshell" nil))))
|
||||
:group 'eshell-var)
|
||||
|
||||
(put 'eshell-variable-aliases-list 'risky-local-variable t)
|
||||
|
||||
;;; Functions:
|
||||
|
||||
(defun eshell-var-initialize ()
|
||||
"Initialize the variable handle code."
|
||||
;; Break the association with our parent's environment. Otherwise,
|
||||
;; changing a variable will affect all of Emacs.
|
||||
(set (make-local-variable 'process-environment) (eshell-copy-environment))
|
||||
|
||||
(define-key eshell-command-map [(meta ?v)] 'eshell-insert-envvar)
|
||||
|
||||
(set (make-local-variable 'eshell-special-chars-inside-quoting)
|
||||
(append eshell-special-chars-inside-quoting '(?$)))
|
||||
(set (make-local-variable 'eshell-special-chars-outside-quoting)
|
||||
(append eshell-special-chars-outside-quoting '(?$)))
|
||||
|
||||
(make-local-hook 'eshell-parse-argument-hook)
|
||||
(add-hook 'eshell-parse-argument-hook 'eshell-interpolate-variable t t)
|
||||
|
||||
(make-local-hook 'eshell-prepare-command-hook)
|
||||
(add-hook 'eshell-prepare-command-hook
|
||||
'eshell-handle-local-variables nil t)
|
||||
|
||||
(when (eshell-using-module 'eshell-cmpl)
|
||||
(make-local-hook 'pcomplete-try-first-hook)
|
||||
(add-hook 'pcomplete-try-first-hook
|
||||
'eshell-complete-variable-reference nil t)
|
||||
(add-hook 'pcomplete-try-first-hook
|
||||
'eshell-complete-variable-assignment nil t)))
|
||||
|
||||
(defun eshell-handle-local-variables ()
|
||||
"Allow for the syntax 'VAR=val <command> <args>'."
|
||||
;; strip off any null commands, which can only happen if a variable
|
||||
;; evaluates to nil, such as "$var x", where `var' is nil. The
|
||||
;; command name in that case becomes `x', for compatibility with
|
||||
;; most regular shells (the difference is that they do an
|
||||
;; interpolation pass before the argument parsing pass, but Eshell
|
||||
;; does both at the same time).
|
||||
(while (and (not eshell-last-command-name)
|
||||
eshell-last-arguments)
|
||||
(setq eshell-last-command-name (car eshell-last-arguments)
|
||||
eshell-last-arguments (cdr eshell-last-arguments)))
|
||||
(let ((setvar "\\`\\([A-Za-z_][A-Za-z0-9_]*\\)=\\(.*\\)\\'")
|
||||
(command (eshell-stringify eshell-last-command-name))
|
||||
(args eshell-last-arguments))
|
||||
;; local variable settings (such as 'CFLAGS=-O2 make') are handled
|
||||
;; by making the whole command into a subcommand, and calling
|
||||
;; setenv immediately before the command is invoked. This means
|
||||
;; that 'BLAH=x cd blah' won't work exactly as expected, but that
|
||||
;; is by no means a typical use of local environment variables.
|
||||
(if (and command (string-match setvar command))
|
||||
(throw
|
||||
'eshell-replace-command
|
||||
(list
|
||||
'eshell-as-subcommand
|
||||
(append
|
||||
(list 'progn)
|
||||
(let ((l (list t)))
|
||||
(while (string-match setvar command)
|
||||
(nconc
|
||||
l (list
|
||||
(list 'setenv (match-string 1 command)
|
||||
(match-string 2 command)
|
||||
(= (length (match-string 2 command)) 0))))
|
||||
(setq command (eshell-stringify (car args))
|
||||
args (cdr args)))
|
||||
(cdr l))
|
||||
(list (list 'eshell-named-command
|
||||
command (list 'quote args)))))))))
|
||||
|
||||
(defun eshell-interpolate-variable ()
|
||||
"Parse a variable interpolation.
|
||||
This function is explicit for adding to `eshell-parse-argument-hook'."
|
||||
(when (and (eq (char-after) ?$)
|
||||
(not (= (1+ (point)) (point-max))))
|
||||
(forward-char)
|
||||
(list 'eshell-escape-arg
|
||||
(eshell-parse-variable))))
|
||||
|
||||
(defun eshell/define (var-alias definition)
|
||||
"Define an VAR-ALIAS using DEFINITION."
|
||||
(if (not definition)
|
||||
(setq eshell-variable-aliases-list
|
||||
(delq (assoc var-alias eshell-variable-aliases-list)
|
||||
eshell-variable-aliases-list))
|
||||
(let ((def (assoc var-alias eshell-variable-aliases-list))
|
||||
(alias-def
|
||||
(list var-alias
|
||||
(list 'quote (if (= (length definition) 1)
|
||||
(car definition)
|
||||
definition)))))
|
||||
(if def
|
||||
(setq eshell-variable-aliases-list
|
||||
(delq (assoc var-alias eshell-variable-aliases-list)
|
||||
eshell-variable-aliases-list)))
|
||||
(setq eshell-variable-aliases-list
|
||||
(cons alias-def
|
||||
eshell-variable-aliases-list))))
|
||||
nil)
|
||||
|
||||
(defun eshell/export (&rest sets)
|
||||
"This alias allows the 'export' command to act as bash users expect."
|
||||
(while sets
|
||||
(if (string-match "^\\([^=]+\\)=\\(.*\\)" (car sets))
|
||||
(setenv (match-string 1 (car sets))
|
||||
(match-string 2 (car sets))))
|
||||
(setq sets (cdr sets))))
|
||||
|
||||
(defun pcomplete/eshell-mode/export ()
|
||||
"Completion function for Eshell's `export'."
|
||||
(while (pcomplete-here
|
||||
(if eshell-complete-export-definition
|
||||
process-environment
|
||||
(eshell-envvar-names)))))
|
||||
|
||||
(defun eshell/setq (&rest args)
|
||||
"Allow command-ish use of `setq'."
|
||||
(let (last-value)
|
||||
(while args
|
||||
(let ((sym (intern (car args)))
|
||||
(val (cadr args)))
|
||||
(setq last-value (set sym val)
|
||||
args (cddr args))))
|
||||
last-value))
|
||||
|
||||
(defun pcomplete/eshell-mode/setq ()
|
||||
"Completion function for Eshell's `setq'."
|
||||
(while (and (pcomplete-here (all-completions pcomplete-stub
|
||||
obarray 'boundp))
|
||||
(pcomplete-here))))
|
||||
|
||||
(defun eshell/env (&rest args)
|
||||
"Implemention of `env' in Lisp."
|
||||
(eshell-init-print-buffer)
|
||||
(eshell-eval-using-options
|
||||
"env" args
|
||||
'((?h "help" nil nil "show this usage screen")
|
||||
:external "env"
|
||||
:usage "<no arguments>")
|
||||
(eshell-for setting (sort (eshell-environment-variables)
|
||||
'string-lessp)
|
||||
(eshell-buffered-print setting "\n"))
|
||||
(eshell-flush)))
|
||||
|
||||
(defun eshell-insert-envvar (envvar-name)
|
||||
"Insert ENVVAR-NAME into the current buffer at point."
|
||||
(interactive
|
||||
(list (read-envvar-name "Name of environment variable: " t)))
|
||||
(insert-and-inherit "$" envvar-name))
|
||||
|
||||
(defun eshell-envvar-names (&optional environment)
|
||||
"Return a list of currently visible environment variable names."
|
||||
(mapcar (function
|
||||
(lambda (x)
|
||||
(substring x 0 (string-match "=" x))))
|
||||
(or environment process-environment)))
|
||||
|
||||
(defun eshell-environment-variables ()
|
||||
"Return a `process-environment', fully updated.
|
||||
This involves setting any variable aliases which affect the
|
||||
environment, as specified in `eshell-variable-aliases-list'."
|
||||
(let ((process-environment (eshell-copy-environment)))
|
||||
(eshell-for var-alias eshell-variable-aliases-list
|
||||
(if (nth 2 var-alias)
|
||||
(setenv (car var-alias)
|
||||
(eshell-stringify
|
||||
(or (eshell-get-variable (car var-alias)) "")))))
|
||||
process-environment))
|
||||
|
||||
(defun eshell-parse-variable ()
|
||||
"Parse the next variable reference at point.
|
||||
The variable name could refer to either an environment variable, or a
|
||||
Lisp variable. The priority order depends on the setting of
|
||||
`eshell-prefer-lisp-variables'.
|
||||
|
||||
Its purpose is to call `eshell-parse-variable-ref', and then to
|
||||
process any indices that come after the variable reference."
|
||||
(let* ((get-len (when (eq (char-after) ?#)
|
||||
(forward-char) t))
|
||||
value indices)
|
||||
(setq value (eshell-parse-variable-ref)
|
||||
indices (and (not (eobp))
|
||||
(eq (char-after) ?\[)
|
||||
(eshell-parse-indices))
|
||||
value (list 'let
|
||||
(list (list 'indices
|
||||
(list 'quote indices)))
|
||||
value))
|
||||
(if get-len
|
||||
(list 'length value)
|
||||
value)))
|
||||
|
||||
(defun eshell-parse-variable-ref ()
|
||||
"Eval a variable reference.
|
||||
Returns a Lisp form which, if evaluated, will return the value of the
|
||||
variable.
|
||||
|
||||
Possible options are:
|
||||
|
||||
NAME an environment or Lisp variable value
|
||||
<LONG-NAME> disambiguates the length of the name
|
||||
{COMMAND} result of command is variable's value
|
||||
(LISP-FORM) result of Lisp form is variable's value"
|
||||
(let (end)
|
||||
(cond
|
||||
((eq (char-after) ?{)
|
||||
(let ((end (eshell-find-delimiter ?\{ ?\})))
|
||||
(if (not end)
|
||||
(throw 'eshell-incomplete ?\{)
|
||||
(prog1
|
||||
(list 'eshell-convert
|
||||
(list 'eshell-command-to-value
|
||||
(list 'eshell-as-subcommand
|
||||
(eshell-parse-command
|
||||
(cons (1+ (point)) end)))))
|
||||
(goto-char (1+ end))))))
|
||||
((memq (char-after) '(?\' ?\"))
|
||||
(let ((name (if (eq (char-after) ?\')
|
||||
(eshell-parse-literal-quote)
|
||||
(eshell-parse-double-quote))))
|
||||
(if name
|
||||
(list 'eshell-get-variable (eval name) 'indices))))
|
||||
((eq (char-after) ?<)
|
||||
(let ((end (eshell-find-delimiter ?\< ?\>)))
|
||||
(if (not end)
|
||||
(throw 'eshell-incomplete ?\<)
|
||||
(let* ((temp (make-temp-name temporary-file-directory))
|
||||
(cmd (concat (buffer-substring (1+ (point)) end)
|
||||
" > " temp)))
|
||||
(prog1
|
||||
(list
|
||||
'let (list (list 'eshell-current-handles
|
||||
(list 'eshell-create-handles temp
|
||||
(list 'quote 'overwrite))))
|
||||
(list
|
||||
'progn
|
||||
(list 'eshell-as-subcommand
|
||||
(eshell-parse-command cmd))
|
||||
(list 'ignore
|
||||
(list 'nconc 'eshell-this-command-hook
|
||||
(list 'list
|
||||
(list 'function
|
||||
(list 'lambda nil
|
||||
(list 'delete-file temp))))))
|
||||
(list 'quote temp)))
|
||||
(goto-char (1+ end)))))))
|
||||
((eq (char-after) ?\()
|
||||
(condition-case err
|
||||
(list 'eshell-command-to-value
|
||||
(list 'eshell-lisp-command
|
||||
(list 'quote (read (current-buffer)))))
|
||||
(end-of-file
|
||||
(throw 'eshell-incomplete ?\())))
|
||||
((assoc (char-to-string (char-after))
|
||||
eshell-variable-aliases-list)
|
||||
(forward-char)
|
||||
(list 'eshell-get-variable
|
||||
(char-to-string (char-before)) 'indices))
|
||||
((looking-at eshell-variable-name-regexp)
|
||||
(prog1
|
||||
(list 'eshell-get-variable (match-string 0) 'indices)
|
||||
(goto-char (match-end 0))))
|
||||
(t
|
||||
(error "Invalid variable reference")))))
|
||||
|
||||
(eshell-deftest var interp-cmd
|
||||
"Interpolate command result"
|
||||
(eshell-command-result-p "+ ${+ 1 2} 3" "6\n"))
|
||||
|
||||
(eshell-deftest var interp-lisp
|
||||
"Interpolate Lisp form evalution"
|
||||
(eshell-command-result-p "+ $(+ 1 2) 3" "6\n"))
|
||||
|
||||
(eshell-deftest var interp-concat
|
||||
"Interpolate and concat command"
|
||||
(eshell-command-result-p "+ ${+ 1 2}3 3" "36\n"))
|
||||
|
||||
(eshell-deftest var interp-concat-lisp
|
||||
"Interpolate and concat Lisp form"
|
||||
(eshell-command-result-p "+ $(+ 1 2)3 3" "36\n"))
|
||||
|
||||
(eshell-deftest var interp-concat2
|
||||
"Interpolate and concat two commands"
|
||||
(eshell-command-result-p "+ ${+ 1 2}${+ 1 2} 3" "36\n"))
|
||||
|
||||
(eshell-deftest var interp-concat-lisp2
|
||||
"Interpolate and concat two Lisp forms"
|
||||
(eshell-command-result-p "+ $(+ 1 2)$(+ 1 2) 3" "36\n"))
|
||||
|
||||
(defun eshell-parse-indices ()
|
||||
"Parse and return a list of list of indices."
|
||||
(let (indices)
|
||||
(while (eq (char-after) ?\[)
|
||||
(let ((end (eshell-find-delimiter ?\[ ?\])))
|
||||
(if (not end)
|
||||
(throw 'eshell-incomplete ?\[)
|
||||
(forward-char)
|
||||
(let (eshell-glob-function)
|
||||
(setq indices (cons (eshell-parse-arguments (point) end)
|
||||
indices)))
|
||||
(goto-char (1+ end)))))
|
||||
(nreverse indices)))
|
||||
|
||||
(defun eshell-get-variable (name &optional indices)
|
||||
"Get the value for the variable NAME."
|
||||
(let* ((alias (assoc name eshell-variable-aliases-list))
|
||||
(var (if alias
|
||||
(cadr alias)
|
||||
name)))
|
||||
(if (and alias (functionp var))
|
||||
(funcall var indices)
|
||||
(eshell-apply-indices
|
||||
(cond
|
||||
((stringp var)
|
||||
(let ((sym (intern-soft var)))
|
||||
(if (and sym (boundp sym)
|
||||
(or eshell-prefer-lisp-variables
|
||||
(not (getenv var))))
|
||||
(symbol-value sym)
|
||||
(getenv var))))
|
||||
((symbolp var)
|
||||
(symbol-value var))
|
||||
(t
|
||||
(error "Unknown variable `%s'" (eshell-stringify var))))
|
||||
indices))))
|
||||
|
||||
(defun eshell-apply-indices (value indices)
|
||||
"Apply to VALUE all of the given INDICES, returning the sub-result.
|
||||
The format of INDICES is:
|
||||
|
||||
((INT-OR-NAME-OR-OTHER INT-OR-NAME INT-OR-NAME ...)
|
||||
...)
|
||||
|
||||
Each member of INDICES represents a level of nesting. If the first
|
||||
member of a sublist is not an integer or name, and the value it's
|
||||
reference is a string, that will be used as the regexp with which is
|
||||
to divide the string into sub-parts. The default is whitespace.
|
||||
Otherwise, each INT-OR-NAME refers to an element of the list value.
|
||||
Integers imply a direct index, and names, an associate lookup using
|
||||
`assoc'.
|
||||
|
||||
For example, to retrieve the second element of a user's record in
|
||||
'/etc/passwd', the variable reference would look like:
|
||||
|
||||
${egrep johnw /etc/passwd}[: 2]"
|
||||
(while indices
|
||||
(let ((refs (car indices)))
|
||||
(when (stringp value)
|
||||
(let (separator)
|
||||
(if (not (or (not (stringp (caar indices)))
|
||||
(string-match
|
||||
(concat "^" eshell-variable-name-regexp "$")
|
||||
(caar indices))))
|
||||
(setq separator (caar indices)
|
||||
refs (cdr refs)))
|
||||
(setq value
|
||||
(mapcar 'eshell-convert
|
||||
(split-string value separator)))))
|
||||
(cond
|
||||
((< (length refs) 0)
|
||||
(error "Illegal array variable index: %s"
|
||||
(eshell-stringify refs)))
|
||||
((= (length refs) 1)
|
||||
(setq value (eshell-index-value value (car refs))))
|
||||
(t
|
||||
(let ((new-value (list t)))
|
||||
(while refs
|
||||
(nconc new-value
|
||||
(list (eshell-index-value value
|
||||
(car refs))))
|
||||
(setq refs (cdr refs)))
|
||||
(setq value (cdr new-value))))))
|
||||
(setq indices (cdr indices)))
|
||||
value)
|
||||
|
||||
(defun eshell-index-value (value index)
|
||||
"Reference VALUE using the given INDEX."
|
||||
(if (stringp index)
|
||||
(cdr (assoc index value))
|
||||
(cond
|
||||
((ring-p value)
|
||||
(if (> index (ring-length value))
|
||||
(error "Index exceeds length of ring")
|
||||
(ring-ref value index)))
|
||||
((listp value)
|
||||
(if (> index (length value))
|
||||
(error "Index exceeds length of list")
|
||||
(nth index value)))
|
||||
((vectorp value)
|
||||
(if (> index (length value))
|
||||
(error "Index exceeds length of vector")
|
||||
(aref value index)))
|
||||
(t
|
||||
(error "Invalid data type for indexing")))))
|
||||
|
||||
;;;_* Variable name completion
|
||||
|
||||
(defun eshell-complete-variable-reference ()
|
||||
"If there is a variable reference, complete it."
|
||||
(let ((arg (pcomplete-actual-arg)) index)
|
||||
(when (setq index
|
||||
(string-match
|
||||
(concat "\\$\\(" eshell-variable-name-regexp
|
||||
"\\)?\\'") arg))
|
||||
(setq pcomplete-stub (substring arg (1+ index)))
|
||||
(throw 'pcomplete-completions (eshell-variables-list)))))
|
||||
|
||||
(defun eshell-variables-list ()
|
||||
"Generate list of applicable variables."
|
||||
(let ((argname pcomplete-stub)
|
||||
completions)
|
||||
(eshell-for alias eshell-variable-aliases-list
|
||||
(if (string-match (concat "^" argname) (car alias))
|
||||
(setq completions (cons (car alias) completions))))
|
||||
(sort
|
||||
(append
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (varname)
|
||||
(let ((value (eshell-get-variable varname)))
|
||||
(if (and value
|
||||
(stringp value)
|
||||
(file-directory-p value))
|
||||
(concat varname (char-to-string directory-sep-char))
|
||||
varname))))
|
||||
(eshell-envvar-names (eshell-environment-variables)))
|
||||
(all-completions argname obarray 'boundp)
|
||||
completions)
|
||||
'string-lessp)))
|
||||
|
||||
(defun eshell-complete-variable-assignment ()
|
||||
"If there is a variable assignment, allow completion of entries."
|
||||
(let ((arg (pcomplete-actual-arg)) pos)
|
||||
(when (string-match (concat "\\`" eshell-variable-name-regexp "=") arg)
|
||||
(setq pos (match-end 0))
|
||||
(if (string-match "\\(:\\)[^:]*\\'" arg)
|
||||
(setq pos (match-end 1)))
|
||||
(setq pcomplete-stub (substring arg pos))
|
||||
(throw 'pcomplete-completions (pcomplete-entries)))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;; esh-var.el ends here
|
495
lisp/eshell/eshell.el
Normal file
495
lisp/eshell/eshell.el
Normal file
@ -0,0 +1,495 @@
|
||||
;;; eshell --- the Emacs command shell
|
||||
|
||||
;; Copyright (C) 1999, 2000 Free Sofware Foundation
|
||||
|
||||
;; Author: John Wiegley <johnw@gnu.org>
|
||||
;; Keywords: processes
|
||||
;; X-URL: http://www.emacs.org/~johnw/eshell.html
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; 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
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
(provide 'eshell)
|
||||
|
||||
(eval-when-compile (require 'esh-maint))
|
||||
|
||||
(defgroup eshell nil
|
||||
"Eshell is a command shell implemented entirely in Emacs Lisp. It
|
||||
invokes no external processes beyond those requested by the user. It
|
||||
is intended to be a functional replacement for command shells such as
|
||||
bash, zsh, rc, 4dos; since Emacs itself is capable of handling most of
|
||||
the tasks accomplished by such tools."
|
||||
:tag "The Emacs shell"
|
||||
:link '(info-link "(eshell.info)The Emacs shell")
|
||||
:group 'applications)
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;;_* What does Eshell offer you?
|
||||
;;
|
||||
;; Despite the sheer fact that running an Emacs shell can be fun, here
|
||||
;; are a few of the unique features offered by Eshell:
|
||||
;;
|
||||
;; @ Integration with the Emacs Lisp programming environment
|
||||
;;
|
||||
;; @ A high degree of configurability
|
||||
;;
|
||||
;; @ The ability to have the same shell on every system Emacs has been
|
||||
;; ported to. Since Eshell imposes no external requirements, and
|
||||
;; relies upon only the Lisp functions exposed by Emacs, it is quite
|
||||
;; operating system independent. Several of the common UNIX
|
||||
;; commands, such as ls, mv, rm, ln, etc., have been implemented in
|
||||
;; Lisp in order to provide a more consistent work environment.
|
||||
;;
|
||||
;; For those who might be using an older version of Eshell, version
|
||||
;; 2.1 represents an entirely new, module-based architecture. It
|
||||
;; supports most of the features offered by modern shells. Here is a
|
||||
;; brief list of some of its more visible features:
|
||||
;;
|
||||
;; @ Command argument completion (tcsh, zsh)
|
||||
;; @ Input history management (bash)
|
||||
;; @ Intelligent output scrolling
|
||||
;; @ Psuedo-devices (such as "/dev/clip" for copying to the clipboard)
|
||||
;; @ Extended globbing (zsh)
|
||||
;; @ Argument and globbing predication (zsh)
|
||||
;; @ I/O redirection to buffers, files, symbols, processes, etc.
|
||||
;; @ Many niceties otherwise seen only in 4DOS
|
||||
;; @ Alias functions, both Lisp and Eshell-syntax
|
||||
;; @ Piping, sequenced commands, background jobs, etc...
|
||||
;;
|
||||
;;;_* Eshell is free software
|
||||
;;
|
||||
;; Eshell is free software; you can redistribute it and/or modify it
|
||||
;; under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
;;
|
||||
;; This program 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
|
||||
;; along with Eshell; see the file COPYING. If not, write to the Free
|
||||
;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
;; 02111-1307, USA.
|
||||
;;
|
||||
;;;_* How to begin
|
||||
;;
|
||||
;; To start using Eshell, add the following to your .emacs file:
|
||||
;;
|
||||
;; (load "eshell-auto")
|
||||
;;
|
||||
;; This will define all of the necessary autoloads.
|
||||
;;
|
||||
;; Now type `M-x eshell'. See the INSTALL file for full installation
|
||||
;; instructions.
|
||||
;;
|
||||
;;;_* Philosophy
|
||||
;;
|
||||
;; A shell is a layer which metaphorically surrounds the kernel, or
|
||||
;; heart of an operating system. This kernel can be seen as an engine
|
||||
;; of pure functionality, waiting to serve, while the user programs
|
||||
;; take advantage of that functionality to accomplish their purpose.
|
||||
;;
|
||||
;; The shell's role is to make that functionality accessible to the
|
||||
;; user in an unformed state. Very roughly, it associates kernel
|
||||
;; functionality with textual commands, allowing the user to interact
|
||||
;; with the operating system via linguistic constructs. Process
|
||||
;; invocation is perhaps the most significant form this takes, using
|
||||
;; the kernel's `fork' and `exec' functions.
|
||||
;;
|
||||
;; Other programs also interact with the functionality of the kernel,
|
||||
;; but these user applications typically offer a specific range of
|
||||
;; functionality, and thus are not classed as "shells" proper.
|
||||
;; (What they lose in quiddity, they gain in rigidity).
|
||||
;;
|
||||
;; Emacs is also a user application, but it does make the
|
||||
;; functionality of the kernel accessible through an interpreted
|
||||
;; language -- namely, Lisp. For that reason, there is little
|
||||
;; preventing Emacs from serving the same role as a modern shell. It
|
||||
;; too can manipulate the kernel in an unpredetermined way to cause
|
||||
;; system changes. All it's missing is the shell-ish linguistic
|
||||
;; model.
|
||||
;;
|
||||
;; Enter Eshell. Eshell translates "shell-like" syntax into Lisp
|
||||
;; in order to exercise the kernel in the same manner as typical
|
||||
;; system shells. There is a fundamental difference here, however,
|
||||
;; although it may seem subtle at first...
|
||||
;;
|
||||
;; Shells like csh and Bourne shell were written several decades ago,
|
||||
;; in different times, under more restrictive circumstances. This
|
||||
;; confined perspective shows itself in the paradigm used by nearly
|
||||
;; all command-line shells since. They are linear in conception, byte
|
||||
;; stream-based, sequential, and confined to movement within a single
|
||||
;; host machine.
|
||||
;;
|
||||
;; Emacs, on the other hand, is more than just a limited translator
|
||||
;; that can invoke subprocesses and redirect file handles. It also
|
||||
;; manages character buffers, windowing frames, network connections,
|
||||
;; registers, bookmarks, processes, etc. In other words, it's a very
|
||||
;; multi-dimensional environment, within which eshell emulates a highly
|
||||
;; linear methodology.
|
||||
;;
|
||||
;; Taking a moment, let's look at how this could affect the future of
|
||||
;; a shell allowed to develop in such a wider field of play:
|
||||
;;
|
||||
;; @ There is no reason why directory movement should be linear, and
|
||||
;; confined to a single file-system. Emacs, through w3 and ange-ftp,
|
||||
;; has access to the entire Web. Why not allow a user to cd to
|
||||
;; multiple directories simultaneously, for example? It might make
|
||||
;; some tasks easier, such as diff'ing files separated by very long
|
||||
;; pathnames.
|
||||
;;
|
||||
;; @ Data sources are available from anywhere Emacs can derive
|
||||
;; information from: not just from files or the output of other
|
||||
;; processes.
|
||||
;;
|
||||
;; @ Multiple shell invocations all share the same environment -- even
|
||||
;; the same process list! It would be possible to have "process
|
||||
;; views", so that one buffer is watching standard output, another
|
||||
;; standard error, and another the result of standard output grep'd
|
||||
;; through a regular expression...
|
||||
;;
|
||||
;; @ It is not necessary to "leave" the shell, losing all input and
|
||||
;; output history, environment variables, directory stack, etc.
|
||||
;; Emacs could save the contents of your eshell environment, and
|
||||
;; restore all of it (or at least as much as possible) each time you
|
||||
;; restart. This could occur automatically, without requiring
|
||||
;; complex initialization scripts.
|
||||
;;
|
||||
;; @ Typos occur all of the time; many of them are repeats of common
|
||||
;; errors, such as 'dri' for `dir'. Since executing non-existent
|
||||
;; programs is rarely the intention of the user, eshell could prompt
|
||||
;; for the replacement string, and then record that in a database of
|
||||
;; known misspellings. (Note: The typo at the beginning of this
|
||||
;; paragraph wasn't discovered until two months after I wrote the
|
||||
;; text; it was not intentional).
|
||||
;;
|
||||
;; @ Emacs' register and bookmarking facilities can be used for
|
||||
;; remembering where you've been, and what you've seen -- to varying
|
||||
;; levels of persistence. They could perhaps even be tied to
|
||||
;; specific "moments" during eshell execution, which would include
|
||||
;; the environment at that time, as well as other variables.
|
||||
;; Although this would require functionality orthogonal to Emacs'
|
||||
;; own bookmarking facilities, the interface used could be made to
|
||||
;; operate very similarly.
|
||||
;;
|
||||
;; This presents a brief idea of what the fuller dimensionality of an
|
||||
;; Emacs shell could offer. It's not just the language of a shell
|
||||
;; that determines how it's used, but also the Weltanschauung
|
||||
;; underlying its design -- and which is felt behind even the smallest
|
||||
;; feature. I would hope the freedom provided by using Emacs as a
|
||||
;; parent environment will invite rich ideas from others. It
|
||||
;; certainly feels as though all I've done so far is to tie down the
|
||||
;; horse, so to speak, so that he will run at a man's pace.
|
||||
;;
|
||||
;;;_* Influences
|
||||
;;
|
||||
;; The author of Eshell has been a long-time user of the following
|
||||
;; shells, all of which contributed to Eshell's design:
|
||||
;;
|
||||
;; @ rc
|
||||
;; @ bash
|
||||
;; @ zsh
|
||||
;; @ sh
|
||||
;; @ 4nt
|
||||
;; @ csh
|
||||
|
||||
;;;_* User Options
|
||||
;;
|
||||
;; The following user options modify the behavior of Eshell overall.
|
||||
|
||||
(load "esh-util" nil t)
|
||||
|
||||
(defsubst eshell-add-to-window-buffer-names ()
|
||||
"Add `eshell-buffer-name' to `same-window-buffer-names'."
|
||||
(add-to-list 'same-window-buffer-names eshell-buffer-name))
|
||||
|
||||
(defsubst eshell-remove-from-window-buffer-names ()
|
||||
"Remove `eshell-buffer-name' from `same-window-buffer-names'."
|
||||
(setq same-window-buffer-names
|
||||
(delete eshell-buffer-name same-window-buffer-names)))
|
||||
|
||||
(defcustom eshell-load-hook nil
|
||||
"*A hook run once Eshell has been loaded."
|
||||
:type 'hook
|
||||
:group 'eshell)
|
||||
|
||||
(defcustom eshell-unload-hook
|
||||
'(eshell-remove-from-window-buffer-names
|
||||
eshell-unload-all-modules)
|
||||
"*A hook run when Eshell is unloaded from memory."
|
||||
:type 'hook
|
||||
:group 'eshell)
|
||||
|
||||
(defcustom eshell-buffer-name "*eshell*"
|
||||
"*The basename used for Eshell buffers."
|
||||
:set (lambda (symbol value)
|
||||
;; remove the old value of `eshell-buffer-name', if present
|
||||
(if (boundp 'eshell-buffer-name)
|
||||
(eshell-remove-from-window-buffer-names))
|
||||
(set symbol value)
|
||||
;; add the new value
|
||||
(eshell-add-to-window-buffer-names)
|
||||
value)
|
||||
:type 'string
|
||||
:group 'eshell)
|
||||
|
||||
(eshell-deftest mode same-window-buffer-names
|
||||
"`eshell-buffer-name' is a member of `same-window-buffer-names'"
|
||||
(member eshell-buffer-name same-window-buffer-names))
|
||||
|
||||
(defcustom eshell-directory-name "~/.eshell/"
|
||||
"*The directory where Eshell control files should be kept."
|
||||
:type 'directory
|
||||
:group 'eshell)
|
||||
|
||||
(eshell-deftest mode eshell-directory-exists
|
||||
"`eshell-directory-name' exists and is writable"
|
||||
(file-writable-p eshell-directory-name))
|
||||
|
||||
(eshell-deftest mode eshell-directory-modes
|
||||
"`eshell-directory-name' has correct access protections"
|
||||
(or (eshell-under-windows-p)
|
||||
(= (file-modes eshell-directory-name)
|
||||
eshell-private-directory-modes)))
|
||||
|
||||
(defcustom eshell-prefer-to-shell nil
|
||||
"*If non-nil, \\[shell-command] will use Eshell instead of shell-mode."
|
||||
:set (lambda (symbol value)
|
||||
;; modifying the global keymap directly is odious, but how
|
||||
;; else to achieve the takeover?
|
||||
(if value
|
||||
(progn
|
||||
(define-key global-map [(meta ?!)] 'eshell-command)
|
||||
;;; (define-key global-map [(meta ?|)] 'eshell-command-on-region)
|
||||
)
|
||||
(define-key global-map [(meta ?!)] 'shell-command)
|
||||
;;; (define-key global-map [(meta ?|)] 'shell-command-on-region)
|
||||
)
|
||||
(set symbol value))
|
||||
:type 'boolean
|
||||
:require 'eshell
|
||||
:group 'eshell)
|
||||
|
||||
;;;_* Running Eshell
|
||||
;;
|
||||
;; There are only three commands used to invoke Eshell. The first two
|
||||
;; are intended for interactive use, while the third is meant for
|
||||
;; programmers. They are:
|
||||
|
||||
;;;###autoload
|
||||
(defun eshell (&optional arg)
|
||||
"Create an interactive Eshell buffer.
|
||||
The buffer used for Eshell sessions is determined by the value of
|
||||
`eshell-buffer-name'. If there is already an Eshell session active in
|
||||
that buffer, Emacs will simply switch to it. Otherwise, a new session
|
||||
will begin. A new session is always created if the the prefix
|
||||
argument ARG is specified. Returns the buffer selected (or created)."
|
||||
(interactive "P")
|
||||
(assert eshell-buffer-name)
|
||||
(let ((buf (if arg
|
||||
(generate-new-buffer eshell-buffer-name)
|
||||
(get-buffer-create eshell-buffer-name))))
|
||||
;; Simply calling `pop-to-buffer' will not mimic the way that
|
||||
;; shell-mode buffers appear, since they always reuse the same
|
||||
;; window that that command was invoked from. To achieve this,
|
||||
;; it's necessary to add `eshell-buffer-name' to the variable
|
||||
;; `same-window-buffer-names', which is done when Eshell is loaded
|
||||
(assert (and buf (buffer-live-p buf)))
|
||||
(pop-to-buffer buf)
|
||||
(unless (fboundp 'eshell-mode)
|
||||
(error "`eshell-auto' must be loaded before Eshell can be used"))
|
||||
(unless (eq major-mode 'eshell-mode)
|
||||
(eshell-mode))
|
||||
(assert (eq major-mode 'eshell-mode))
|
||||
buf))
|
||||
|
||||
(defun eshell-return-exits-minibuffer ()
|
||||
(define-key eshell-mode-map [(control ?g)] 'abort-recursive-edit)
|
||||
(define-key eshell-mode-map [return] 'exit-minibuffer)
|
||||
(define-key eshell-mode-map [(control ?m)] 'exit-minibuffer)
|
||||
(define-key eshell-mode-map [(control ?j)] 'exit-minibuffer)
|
||||
(define-key eshell-mode-map [(meta return)] 'exit-minibuffer)
|
||||
(define-key eshell-mode-map [(meta control ?m)] 'exit-minibuffer))
|
||||
|
||||
;;;###autoload
|
||||
(defun eshell-command (&optional command arg)
|
||||
"Execute the Eshell command string COMMAND.
|
||||
With prefix ARG, insert output into the current buffer at point."
|
||||
(interactive)
|
||||
(require 'esh-cmd)
|
||||
(setq arg current-prefix-arg)
|
||||
(unwind-protect
|
||||
(let ((eshell-non-interactive-p t))
|
||||
(add-hook 'minibuffer-setup-hook 'eshell-mode)
|
||||
(add-hook 'eshell-mode-hook 'eshell-return-exits-minibuffer)
|
||||
(setq command (read-from-minibuffer "Emacs shell command: ")))
|
||||
(remove-hook 'eshell-mode-hook 'eshell-return-exits-minibuffer)
|
||||
(remove-hook 'minibuffer-setup-hook 'eshell-mode))
|
||||
(unless command
|
||||
(error "No command specified!"))
|
||||
;; redirection into the current buffer is achieved by adding an
|
||||
;; output redirection to the end of the command, of the form
|
||||
;; 'COMMAND >>> #<buffer BUFFER>'. This will not interfere with
|
||||
;; other redirections, since multiple redirections merely cause the
|
||||
;; output to be copied to multiple target locations
|
||||
(if arg
|
||||
(setq command
|
||||
(concat command
|
||||
(format " >>> #<buffer %s>"
|
||||
(buffer-name (current-buffer))))))
|
||||
(save-excursion
|
||||
(require 'esh-mode)
|
||||
(let ((buf (set-buffer (generate-new-buffer " *eshell cmd*")))
|
||||
(eshell-non-interactive-p t))
|
||||
(eshell-mode)
|
||||
(let* ((proc (eshell-eval-command
|
||||
(list 'eshell-commands
|
||||
(eshell-parse-command command))))
|
||||
intr
|
||||
(bufname (if (and proc (listp proc))
|
||||
"*EShell Async Command Output*"
|
||||
(setq intr t)
|
||||
"*EShell Command Output*")))
|
||||
(if (buffer-live-p (get-buffer bufname))
|
||||
(kill-buffer bufname))
|
||||
(rename-buffer bufname)
|
||||
;; things get a little coarse here, since the desire is to
|
||||
;; make the output as attractive as possible, with no
|
||||
;; extraneous newlines
|
||||
(when intr
|
||||
(if (eshell-interactive-process)
|
||||
(eshell-wait-for-process (eshell-interactive-process)))
|
||||
(assert (not (eshell-interactive-process)))
|
||||
(goto-char (point-max))
|
||||
(while (and (bolp) (not (bobp)))
|
||||
(delete-backward-char 1)))
|
||||
(assert (and buf (buffer-live-p buf)))
|
||||
(unless arg
|
||||
(let ((len (if (not intr) 2
|
||||
(count-lines (point-min) (point-max)))))
|
||||
(cond
|
||||
((= len 0)
|
||||
(message "(There was no command output)")
|
||||
(kill-buffer buf))
|
||||
((= len 1)
|
||||
(message (buffer-string))
|
||||
(kill-buffer buf))
|
||||
(t
|
||||
(save-selected-window
|
||||
(select-window (display-buffer buf))
|
||||
(goto-char (point-min))
|
||||
;; cause the output buffer to take up as little screen
|
||||
;; real-estate as possible, if temp buffer resizing is
|
||||
;; enabled
|
||||
(and intr temp-buffer-resize-mode
|
||||
(resize-temp-buffer-window)))))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun eshell-command-result (command &optional status-var)
|
||||
"Execute the given Eshell COMMAND, and return the result.
|
||||
The result might be any Lisp object.
|
||||
If STATUS-VAR is a symbol, it will be set to the exit status of the
|
||||
command. This is the only way to determine whether the value returned
|
||||
corresponding to a successful execution."
|
||||
;; a null command produces a null, successful result
|
||||
(if (not command)
|
||||
(ignore
|
||||
(if (and status-var (symbolp status-var))
|
||||
(set status-var 0)))
|
||||
(with-temp-buffer
|
||||
(require 'esh-mode)
|
||||
(let ((eshell-non-interactive-p t))
|
||||
(eshell-mode)
|
||||
(let ((result (eshell-do-eval
|
||||
(list 'eshell-commands
|
||||
(list 'eshell-command-to-value
|
||||
(eshell-parse-command command))) t)))
|
||||
(assert (eq (car result) 'quote))
|
||||
(if (and status-var (symbolp status-var))
|
||||
(set status-var eshell-last-command-status))
|
||||
(cadr result))))))
|
||||
|
||||
(eshell-deftest mode simple-command-result
|
||||
"`eshell-command-result' works with a simple command."
|
||||
(= (eshell-command-result "+ 1 2") 3))
|
||||
|
||||
;;;_* Reporting bugs
|
||||
;;
|
||||
;; Since Eshell has not yet been in use by a wide audience, and since
|
||||
;; the number of possible configurations is quite large, it is certain
|
||||
;; that many bugs slipped past the rigors of testing it was put
|
||||
;; through. If you do encounter a bug, on any system, please report
|
||||
;; it -- in addition to any particular oddities in your configuration
|
||||
;; -- so that the problem may be corrected for the benefit of others.
|
||||
|
||||
(defconst eshell-report-bug-address "johnw@gnu.org"
|
||||
"E-mail address to send Eshell bug reports to.")
|
||||
|
||||
;;;###autoload
|
||||
(defun eshell-report-bug (topic)
|
||||
"Report a bug in Eshell.
|
||||
Prompts for the TOPIC. Leaves you in a mail buffer.
|
||||
Please include any configuration details that might be involved."
|
||||
(interactive "sBug Subject: ")
|
||||
(compose-mail eshell-report-bug-address topic)
|
||||
(goto-char (point-min))
|
||||
(re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
|
||||
(forward-line 1)
|
||||
(let ((signature (buffer-substring (point) (point-max))))
|
||||
;; Discourage users from writing non-English text.
|
||||
(set-buffer-multibyte nil)
|
||||
(delete-region (point) (point-max))
|
||||
(insert signature)
|
||||
(backward-char (length signature)))
|
||||
(insert "emacs-version: " (emacs-version))
|
||||
(insert "\n\nThere appears to be a bug in Eshell.\n\n"
|
||||
"Please describe exactly what actions "
|
||||
"triggered the bug and the precise\n"
|
||||
"symptoms of the bug:\n\n")
|
||||
;; This is so the user has to type something in order to send
|
||||
;; the report easily.
|
||||
(use-local-map (nconc (make-sparse-keymap) (current-local-map))))
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defun eshell-unload-all-modules ()
|
||||
"Unload all modules that were loaded by Eshell, if possible.
|
||||
If the user has require'd in any of the modules, or customized a
|
||||
variable with a :require tag (such as `eshell-prefer-to-shell'), it
|
||||
will be impossible to unload Eshell completely without restarting
|
||||
Emacs."
|
||||
;; if the user set `eshell-prefer-to-shell' to t, but never loaded
|
||||
;; Eshell, then `eshell-subgroups' will be unbound
|
||||
(when (fboundp 'eshell-subgroups)
|
||||
(eshell-for module (eshell-subgroups 'eshell)
|
||||
;; this really only unloads as many modules as possible,
|
||||
;; since other `require' references (such as by customizing
|
||||
;; `eshell-prefer-to-shell' to a non-nil value) might make it
|
||||
;; impossible to unload Eshell completely
|
||||
(if (featurep module)
|
||||
(ignore-errors
|
||||
(message "Unloading %s..." (symbol-name module))
|
||||
(unload-feature module)
|
||||
(message "Unloading %s...done" (symbol-name module)))))
|
||||
(message "Unloading eshell...done")))
|
||||
|
||||
(run-hooks 'eshell-load-hook)
|
||||
|
||||
;;; eshell.el ends here
|
1189
lisp/pcomplete.el
Normal file
1189
lisp/pcomplete.el
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user