Require individual files if needed when compiling, rather than
[bpt/emacs.git] / lisp / eshell / esh-cmd.el
index 24b51a1..247d6c7 100644 (file)
@@ -1,6 +1,7 @@
-;;; esh-cmd --- command invocation
+;;; esh-cmd.el --- command invocation
 
-;; Copyright (C) 1999, 2000 Free Software Foundation
+;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004,
+;;   2005, 2006, 2007 Free Software Foundation, Inc.
 
 ;; Author: John Wiegley <johnw@gnu.org>
 
@@ -8,7 +9,7 @@
 
 ;; 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)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 
 ;; 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-cmd)
-
-(eval-when-compile (require 'esh-maint))
-
-(defgroup eshell-cmd nil
-  "Executing an Eshell command is as simple as typing it in and
-pressing <RET>.  There are several different kinds of commands,
-however."
-  :tag "Command invocation"
-  :link '(info-link "(eshell)Command invocation")
-  :group 'eshell)
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
@@ -63,11 +52,6 @@ however."
 ;; functions always take precedence, set
 ;; `eshell-prefer-lisp-functions' to t.
 
-(defcustom eshell-prefer-lisp-functions nil
-  "*If non-nil, prefer Lisp functions to external commands."
-  :type 'boolean
-  :group 'eshell-cmd)
-
 ;;;_* Alias functions
 ;;
 ;; Whenever a command is specified using a simple name, such as 'ls',
@@ -111,17 +95,44 @@ however."
 ;;
 ;; Lisp arguments are identified using the following regexp:
 
+;;;_* Command hooks
+;;
+;; There are several hooks involved with command execution, which can
+;; be used either to change or augment Eshell's behavior.
+
+
+;;; Code:
+
+(require 'esh-util)
+(unless (featurep 'xemacs)
+  (require 'eldoc))
+(require 'esh-arg)
+(require 'esh-proc)
+(require 'esh-ext)
+
+(eval-when-compile
+  (require 'pcomplete))
+
+
+(defgroup eshell-cmd nil
+  "Executing an Eshell command is as simple as typing it in and
+pressing <RET>.  There are several different kinds of commands,
+however."
+  :tag "Command invocation"
+  ;; :link '(info-link "(eshell)Command invocation")
+  :group 'eshell)
+
+(defcustom eshell-prefer-lisp-functions nil
+  "*If non-nil, prefer Lisp functions to external commands."
+  :type 'boolean
+  :group 'eshell-cmd)
+
 (defcustom eshell-lisp-regexp "\\([(`]\\|#'\\)"
   "*A regexp which, if matched at beginning of an argument, means Lisp.
 Such arguments will be passed to `read', and then evaluated."
   :type 'regexp
   :group 'eshell-cmd)
 
-;;;_* Command hooks
-;;
-;; There are several hooks involved with command execution, which can
-;; be used either to change or augment Eshell's behavior.
-
 (defcustom eshell-pre-command-hook nil
   "*A hook run before each interactive command is invoked."
   :type 'hook
@@ -203,7 +214,7 @@ which may be modified directly.  Any return value is ignored."
   :type 'hook
   :group 'eshell-cmd)
 
-(defcustom eshell-complex-commands nil
+(defcustom eshell-complex-commands '("ls")
   "*A list of commands names or functions, that determine complexity.
 That is, if a command is defined by a function named eshell/NAME,
 and NAME is part of this list, it is invoked as a complex command.
@@ -218,15 +229,6 @@ return non-nil if the command is complex."
                         (function :tag "Predicate")))
   :group 'eshell-cmd)
 
-;;; Code:
-
-(require 'esh-util)
-(unless (eshell-under-xemacs-p)
-  (require 'eldoc))
-(require 'esh-arg)
-(require 'esh-proc)
-(require 'esh-ext)
-
 ;;; User Variables:
 
 (defcustom eshell-cmd-load-hook '(eshell-cmd-initialize)
@@ -295,28 +297,24 @@ command line.")
   (set (make-local-variable 'eshell-last-command-name) nil)
   (set (make-local-variable 'eshell-last-async-proc) nil)
 
-  (make-local-hook 'eshell-kill-hook)
   (add-hook 'eshell-kill-hook 'eshell-resume-command nil t)
 
   ;; make sure that if a command is over, and no process is being
   ;; waited for, that `eshell-current-command' is set to nil.  This
   ;; situation can occur, for example, if a Lisp function results in
   ;; `debug' being called, and the user then types \\[top-level]
-  (make-local-hook 'eshell-post-command-hook)
   (add-hook 'eshell-post-command-hook
            (function
             (lambda ()
               (setq eshell-current-command nil
                     eshell-last-async-proc nil))) nil t)
 
-  (make-local-hook 'eshell-parse-argument-hook)
   (add-hook 'eshell-parse-argument-hook
            'eshell-parse-subcommand-argument nil t)
   (add-hook 'eshell-parse-argument-hook
            'eshell-parse-lisp-argument nil t)
 
   (when (eshell-using-module 'eshell-cmpl)
-    (make-local-hook 'pcomplete-try-first-hook)
     (add-hook 'pcomplete-try-first-hook
              'eshell-complete-lisp-symbols nil t)))
 
@@ -397,6 +395,18 @@ hooks should be run before and after the command."
        (list 'eshell-commands commands)
       commands)))
 
+(defun eshell-debug-command (tag subform)
+  "Output a debugging message to '*eshell last cmd*'."
+  (let ((buf (get-buffer-create "*eshell last cmd*"))
+       (text (eshell-stringify eshell-current-command)))
+    (save-excursion
+      (set-buffer buf)
+      (if (not tag)
+         (erase-buffer)
+       (insert "\n\C-l\n" tag "\n\n" text
+               (if subform
+                   (concat "\n\n" (eshell-stringify subform)) ""))))))
+
 (defun eshell-debug-show-parsed-args (terms)
   "Display parsed arguments in the debug buffer."
   (ignore
@@ -456,12 +466,14 @@ hooks should be run before and after the command."
 
 (defun eshell-rewrite-named-command (terms)
   "If no other rewriting rule transforms TERMS, assume a named command."
-  (list (if eshell-in-pipeline-p
-           'eshell-named-command*
-         'eshell-named-command)
-       (car terms)
-       (and (cdr terms)
-            (append (list 'list) (cdr terms)))))
+  (let ((sym (if eshell-in-pipeline-p
+                'eshell-named-command*
+              'eshell-named-command))
+       (cmd (car terms))
+       (args (cdr terms)))
+    (if args
+       (list sym cmd (append (list 'list) (cdr terms)))
+      (list sym cmd))))
 
 (eshell-deftest cmd named-command
   "Execute named command"
@@ -498,7 +510,7 @@ the second is ignored."
 (defun eshell-rewrite-for-command (terms)
   "Rewrite a `for' command into its equivalent Eshell command form.
 Because the implementation of `for' relies upon conditional evaluation
-of its argumbent (i.e., use of a Lisp special form), it must be
+of its argument (i.e., use of a Lisp special form), it must be
 implemented via rewriting, rather than as a function."
   (if (and (stringp (car terms))
           (string= (car terms) "for")
@@ -613,10 +625,10 @@ must be implemented via rewriting, rather than as a function."
        (eshell-invokify-arg (cadr terms) nil t)
        (list 'eshell-protect
             (eshell-invokify-arg
-             (if (= (length terms) 5)
-                 (car (last terms 3))
+             (if (= (length terms) 4)
+                 (car (last terms 2))
                (car (last terms))) t))
-       (if (= (length terms) 5)
+       (if (= (length terms) 4)
           (list 'eshell-protect
                 (eshell-invokify-arg
                  (car (last terms)))) t))))
@@ -625,7 +637,9 @@ must be implemented via rewriting, rather than as a function."
   "Return non-nil if the last command was \"successful\".
 For a bit of Lisp code, this means a return value of non-nil.
 For an external command, it means an exit code of 0."
-  (if (string= eshell-last-command-name "#<Lisp>")
+  (if (save-match-data
+       (string-match "#<\\(Lisp object\\|function .*\\)>"
+                     eshell-last-command-name))
       eshell-last-command-result
     (= eshell-last-command-status 0)))
 
@@ -711,7 +725,7 @@ For an external command, it means an exit code of 0."
                                       reversed last-terms-sym)
   "Separate TERMS using SEPARATOR.
 If REVERSED is non-nil, the list of separated term groups will be
-returned in reverse order.  If LAST-TERMS-SYM is a symbol, it's value
+returned in reverse order.  If LAST-TERMS-SYM is a symbol, its value
 will be set to a list of all the separator operators found (or '(list
 nil)' if none)."
   (let ((sub-terms (list t))
@@ -759,7 +773,7 @@ nil)' if none)."
 
 (defmacro eshell-do-subjob (object)
   "Evaluate a command OBJECT as a subjob.
-We indicate thet the process was run in the background by returned it
+We indicate that the process was run in the background by returning it
 ensconced in a list."
   `(let ((eshell-current-subjob-p t))
      ,object))
@@ -955,18 +969,6 @@ at the moment are:
   "Completion for the `debug' command."
   (while (pcomplete-here '("errors" "commands"))))
 
-(defun eshell-debug-command (tag subform)
-  "Output a debugging message to '*eshell last cmd*'."
-  (let ((buf (get-buffer-create "*eshell last cmd*"))
-       (text (eshell-stringify eshell-current-command)))
-    (save-excursion
-      (set-buffer buf)
-      (if (not tag)
-         (erase-buffer)
-       (insert "\n\C-l\n" tag "\n\n" text
-               (if subform
-                   (concat "\n\n" (eshell-stringify subform)) ""))))))
-
 (defun eshell-invoke-directly (command input)
   (let ((base (cadr (nth 2 (nth 2 (cadr command))))) name)
     (if (and (eq (car base) 'eshell-trap-errors)
@@ -1006,11 +1008,12 @@ at the moment are:
     (setq eshell-current-command command)
     (let ((delim (catch 'eshell-incomplete
                   (eshell-resume-eval))))
-      (if delim
-         (error "Unmatched delimiter: %c"
-                (if (listp delim)
-                    (car delim)
-                  delim))))))
+      ;; On systems that don't support async subprocesses, eshell-resume
+      ;; can return t.  Don't treat that as an error.
+      (if (listp delim)
+         (setq delim (car delim)))
+      (if (and delim (not (eq delim t)))
+         (error "Unmatched delimiter: %c" delim)))))
 
 (defun eshell-resume-command (proc status)
   "Resume the current command when a process ends."
@@ -1060,7 +1063,7 @@ at the moment are:
   object)
 
 (defconst function-p-func
-  (if (eshell-under-xemacs-p)
+  (if (fboundp 'compiled-function-p)
       'compiled-function-p
     'byte-code-function-p))
 
@@ -1231,7 +1234,7 @@ be finished later after the completion of an asynchronous subprocess."
   "Identify the COMMAND, and where it is located."
   (eshell-for name (cons command names)
     (let (program alias direct)
-      (if (eq (aref name 0) ?*)
+      (if (eq (aref name 0) eshell-explicit-command-char)
          (setq name (substring name 1)
                direct t))
       (if (and (not direct)
@@ -1246,9 +1249,8 @@ be finished later after the completion of an asynchronous subprocess."
        (setq program (eshell-search-path name))
        (let* ((esym (eshell-find-alias-function name))
               (sym (or esym (intern-soft name))))
-         (if (and sym (fboundp sym)
-                  (or esym eshell-prefer-lisp-functions
-                      (not program)))
+         (if (and (or esym (and sym (fboundp sym)))
+                  (or eshell-prefer-lisp-functions (not direct)))
              (let ((desc (let ((inhibit-redisplay t))
                            (save-window-excursion
                              (prog1
@@ -1265,6 +1267,8 @@ be finished later after the completion of an asynchronous subprocess."
                                name (getenv "PATH")))
        (eshell-printn program)))))
 
+(put 'eshell/which 'eshell-no-numeric-conversions t)
+
 (defun eshell-named-command (command &optional args)
   "Insert output from a plain COMMAND, using ARGS.
 COMMAND may result in an alias being executed, or a plain command."
@@ -1284,7 +1288,7 @@ COMMAND may result in an alias being executed, or a plain command."
 (defun eshell-find-alias-function (name)
   "Check whether a function called `eshell/NAME' exists."
   (let* ((sym (intern-soft (concat "eshell/" name)))
-        (file (symbol-file sym)))
+        (file (symbol-file sym 'defun)))
     ;; If the function exists, but is defined in an eshell module
     ;; that's not currently enabled, don't report it as found
     (if (and file
@@ -1293,8 +1297,10 @@ COMMAND may result in an alias being executed, or a plain command."
               (intern (file-name-sans-extension
                        (file-name-nondirectory
                         (concat "eshell-" (match-string 2 file)))))))
-         (if (and (eshell-using-module module-sym)
-                  (memq module-sym (eshell-subgroups 'eshell)))
+         (if (and (functionp sym)
+                  (or (null module-sym)
+                      (eshell-using-module module-sym)
+                      (memq module-sym (eshell-subgroups 'eshell))))
              sym))
       ;; Otherwise, if it's bound, return it.
       (if (functionp sym)
@@ -1381,13 +1387,28 @@ messages, and errors."
 
 (defun eshell-lisp-command (object &optional args)
   "Insert Lisp OBJECT, using ARGS if a function."
-  (setq eshell-last-arguments args
-       eshell-last-command-name "#<Lisp>")
   (catch 'eshell-external               ; deferred to an external command
     (let* ((eshell-ensure-newline-p (eshell-interactive-output-p))
           (result
            (if (functionp object)
-               (eshell-apply object args)
+               (progn
+                 (setq eshell-last-arguments args
+                       eshell-last-command-name
+                       (concat "#<function " (symbol-name object) ">"))
+                 ;; if any of the arguments are flagged as numbers
+                 ;; waiting for conversion, convert them now
+                 (unless (get object 'eshell-no-numeric-conversions)
+                   (while args
+                     (let ((arg (car args)))
+                       (if (and (stringp arg)
+                                (> (length arg) 0)
+                                (not (text-property-not-all
+                                      0 (length arg) 'number t arg)))
+                           (setcar args (string-to-number arg))))
+                     (setq args (cdr args))))
+                 (eshell-apply object eshell-last-arguments))
+             (setq eshell-last-arguments args
+                   eshell-last-command-name "#<Lisp object>")
              (eshell-eval object))))
       (if (and eshell-ensure-newline-p
               (save-excursion
@@ -1398,4 +1419,7 @@ messages, and errors."
 
 (defalias 'eshell-lisp-command* 'eshell-lisp-command)
 
+(provide 'esh-cmd)
+
+;;; arch-tag: 8e4f3867-a0c5-441f-96ba-ddd142d94366
 ;;; esh-cmd.el ends here