Commits

Anonymous committed 8df5be3

* sh-script.el: Synch with FSF 20.7.
Jose's patch "Re: New sh-script.el based on FSF Emacs 20.7",
<87og45mmqw.fsf@sodan.org>

  • Participants
  • Parent commits ae81615

Comments (0)

Files changed (2)

+2000-07-11  Jose Romildo Malaquias <romildo@urano.iceb.ufop.br>
+
+	* sh-script.el: Synch with FSF 20.7.
+
 1999-12-10  Gunnar Evermann  <ge204@eng.cam.ac.uk>
 
 	* sh-script.el (sh-ancestor-alist): Add bash2.
 ;;; sh-script.el --- shell-script editing commands for Emacs
 
-;; Copyright (C) 1993, 1994, 1995, 1996 by Free Software Foundation, Inc.
+;; Copyright (C) 1993, 94, 95, 96, 1997 by Free Software Foundation, Inc.
 
-;; Author: Daniel.Pfeiffer@Informatik.START.dbp.de, fax (+49 69) 7588-2389
+;; Author: Daniel Pfeiffer <occitan@esperanto.org>
 ;; Version: 2.0e
 ;; Maintainer: FSF
 ;; Keywords: languages, unix
 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 ;; 02111-1307, USA.
 
-;;; Synched up with: FSF 19.34.
+;;; Synched up with: FSF 20.7.
 
 ;;; Commentary:
 
 (require 'executable)
 (require 'skeleton)
 
+(defcustom sh-mode-hook nil
+  "*Hook run by `sh-mode'."
+  :type 'hook
+  :group 'sh)
+
+(defcustom sh-set-shell-hook nil
+  "*Hook run by `sh-set-shell'."
+  :type 'hook
+  :group 'sh)
+
 (defgroup sh nil
-  "Shell programming mode."
+  "Shell programming utilities"
   :group 'unix
   :group 'languages)
 
+(defgroup sh-script nil
+  "Shell script mode"
+  :group 'sh
+  :prefix "sh-")
 
 ;;; interpreter-mode-alist is not compatible between Emacs and XEmacs.
 ;;; So fake it.
 the name of the interpreter specified in the first line.
 If it matches, mode MODE is selected.")
 
-(defcustom sh-mode-hook nil
-  "*Hook run by `sh-mode'."
-  :type 'hook
-  :group 'sh)
-
-(defcustom sh-set-shell-hook nil
-  "*Hook run by `sh-set-shell'."
-  :type 'hook
-  :group 'sh)
-
 (defcustom sh-ancestor-alist
   '((ash . sh)
     (bash . jsh)
-    (bash2 . bash)
+    (bash2 . jsh)
     (dtksh . ksh)
     (es . rc)
     (itcsh . tcsh)
     (tcsh . csh)
     (wksh . ksh88)
     (wsh . sh)
-    (zsh . ksh88))
+    (zsh . ksh88)
+    (rpm . sh))
   "*Alist showing the direct ancestor of various shells.
 This is the basis for `sh-feature'.  See also `sh-alias-alist'.
 By default we have the following three hierarchies:
   posix		IEEE 1003.2 Shell Standard
   wsh		? Shell"
   :type '(repeat (cons symbol symbol))
-  :group 'sh)
+  :group 'sh-script)
 
 
 (defcustom sh-alias-alist
 	       (ksh . pdksh)))
 	 ;; for the time being
 	 '((ksh . ksh88)
+           (bash2 . bash)
 	   (sh5 . sh)))
   "*Alist for transforming shell names to what they really are.
 Use this where the name of the executable doesn't correspond to the type of
 shell it really is."
   :type '(repeat (cons symbol symbol))
-  :group 'sh)
+  :group 'sh-script)
 
 
-(defcustom sh-shell-file (or (getenv "SHELL") "/bin/sh")
+(defcustom sh-shell-file
+  (or
+   ;; On MSDOS and Windows, collapse $SHELL to lower-case and remove
+   ;; the executable extension, so comparisons with the list of
+   ;; known shells work.
+   (and (memq system-type '(ms-dos windows-nt))
+	(let* ((shell (getenv "SHELL"))
+	       (shell-base
+		(and shell (file-name-nondirectory shell))))
+	  ;; shell-script mode doesn't support DOS/Windows shells,
+	  ;; so use the default instead.
+	  (if (or (null shell)
+		  (member (downcase shell-base)
+			  '("command.com" "cmd.exe" "4dos.com" "ndos.com"
+			    "cmdproxy.exe")))
+	      "/bin/sh"
+	    (file-name-sans-extension (downcase shell)))))
+   (getenv "SHELL")
+   "/bin/sh")
   "*The executable file name for the shell being programmed."
   :type 'string
-  :group 'sh)
+  :group 'sh-script)
 
 
 (defcustom sh-shell-arg
 			       (cons :format "Evaluate: %v"
 				     (const :format "" eval)
 				     sexp))))
-  :group 'sh)
+  :group 'sh-script)
+
+(defcustom sh-imenu-generic-expression
+  (list
+   (cons 'sh
+	 (concat
+	  "\\(^\\s-*function\\s-+[A-Za-z_][A-Za-z_0-9]*\\)"
+	  "\\|"
+	  "\\(^\\s-*[A-Za-z_][A-Za-z_0-9]*\\s-*()\\)")))
+  "*Regular expression for recognizing shell function definitions.
+See `sh-feature'."
+  :type '(repeat (cons (symbol :tag "Shell")
+		       regexp))
+  :group 'sh-script
+  :version "20.3")
 
 (defvar sh-shell-variables nil
   "Alist of shell variable names that should be included in completion.
 
 (defun sh-canonicalize-shell (shell)
   "Convert a shell name SHELL to the one we should handle it as."
+  (if (string-match "\\.exe\\'" shell)
+      (setq shell (substring shell 0 (match-beginning 0))))
   (or (symbolp shell)
       (setq shell (intern shell)))
   (or (cdr (assq shell sh-alias-alist))
 
 
 (defvar sh-mode-syntax-table
-  '((csh eval identity sh)
-    (sh eval sh-mode-syntax-table ()
-	;; #'s meanings depend on context which can't be expressed here
-	;; ?\# "<"
-	;; ?\^l ">#"
-	;; ?\n ">#"
+  '((sh eval sh-mode-syntax-table ()
+	?\# "<"
+	?\^l ">#"
+	?\n ">#"
 	?\" "\"\""
 	?\' "\"'"
-	?\` ".`"
-	?$ "_"
+	?\` "\"`"
 	?! "_"
 	?% "_"
 	?: "_"
 	?. "_"
 	?^ "_"
 	?~ "_")
-    (rc eval sh-mode-syntax-table sh
-	?\" "_"
-	?\` "."))
+    (csh eval identity sh)
+    (rc eval identity sh))
   "Syntax-table used in Shell-Script mode.  See `sh-feature'.")
 
 
     (substitute-key-definition 'newline-and-indent 'sh-newline-and-indent
 			       map (current-global-map))
 ;; GDF - Don't mess around with the DEL bindings    
-;;    (substitute-key-definition 'delete-backward-char
-;;			       'backward-delete-char-untabify
-;;			       map (current-global-map))
+;     (substitute-key-definition 'delete-backward-char
+; 			       'backward-delete-char-untabify
+; 			       map (current-global-map))
     (define-key map "\C-c:" 'sh-set-shell)
     (substitute-key-definition 'beginning-of-defun
 			       'sh-beginning-of-compound-command
     comint-dynamic-complete-filename)
   "*Functions for doing TAB dynamic completion."
   :type '(repeat function)
-  :group 'sh)
+  :group 'sh-script)
 
 
 (defcustom sh-require-final-newline
 			       (cons :format "Evaluate: %v"
 				     (const :format "" eval)
 				     sexp))))
-  :group 'sh)
-
-
-(defcustom sh-comment-prefix
-  '((csh . "\\(^\\|[^$]\\|\\$[^{]\\)")
-    (rc eval identity csh)
-    (sh . "\\(^\\|[ \t|&;()]\\)"))
-  "*Regexp matching what may come before a comment `#'.
-This must contain one \\(grouping\\) since it is the basis for fontifying
-comments as well as for `comment-start-skip'.
-See `sh-feature'."
-  :type '(repeat (cons (symbol :tag "Shell")
-		       (choice regexp
-			       (cons :format "Evaluate: %v"
-				     (const :format "" eval)
-				     sexp))))
-  :group 'sh)
+  :group 'sh-script)
 
 
 (defcustom sh-assignment-regexp
 			       (cons :format "Evaluate: %v"
 				     (const :format "" eval)
 				     sexp))))
-  :group 'sh)
+  :group 'sh-script)
 
 
 (defcustom sh-indentation 4
   "The width for further indentation in Shell-Script mode."
   :type 'integer
-  :group 'sh)
+  :group 'sh-script)
 
 
 (defcustom sh-remember-variable-min 3
   "*Don't remember variables less than this length for completing reads."
   :type 'integer
-  :group 'sh)
+  :group 'sh-script)
 
 
 (defvar sh-header-marker nil
   "*Regexp to determine the beginning of a shell command.
 The actual command starts at the beginning of the second \\(grouping\\)."
   :type 'regexp
-  :group 'sh)
+  :group 'sh-script)
 
 
 (defcustom sh-end-of-command
   "*Regexp to determine the end of a shell command.
 The actual command ends at the end of the first \\(grouping\\)."
   :type 'regexp
-  :group 'sh)
+  :group 'sh-script)
 
 
 
 			       (cons :format "Evaluate: %v"
 				     (const :format "" eval)
 				     sexp))))
-  :group 'sh)
+  :group 'sh-script)
 
 
 
 			       (cons :format "Evaluate: %v"
 				     (const :format "" eval)
 				     sexp))))
-  :group 'sh)
+  :group 'sh-script)
 
 
 (defcustom sh-other-keywords
 	  "bye" "logout")
 
     ;; The next entry is only used for defining the others
-    (bourne eval sh-append shell
-	    "done" "esac" "fi" "for" "function" "in" "return")
+    (bourne eval sh-append sh
+	    "function")
 
     (csh eval sh-append shell
 	 "breaksw" "default" "end" "endif" "endsw" "foreach" "goto"
     (rc "break" "case" "exec" "exit" "fn" "for" "if" "in" "return" "switch"
 	"while")
 
+    (sh eval sh-append shell
+	"done" "esac" "fi" "for" "in" "return")
+
     ;; The next entry is only used for defining the others
     (shell "break" "case" "continue" "exec" "exit")
 
 			       (cons :format "Evaluate: %v"
 				     (const :format "" eval)
 				     sexp))))
-  :group 'sh)
+  :group 'sh-script)
 
 
 
     (rc eval identity es)
 
     (sh eval sh-append shell
+	;; Variable names.
 	'("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
-	  font-lock-variable-name-face))
+	  font-lock-variable-name-face)
+	;; Function names.
+	'("^\\(\\sw+\\)[ \t]*(" 1 font-lock-function-name-face)
+	'("\\<\\(function\\)\\>[ \t]*\\(\\sw+\\)?"
+	  (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)))
 
     ;; The next entry is only used for defining the others
     (shell eval sh-append executable-font-lock-keywords
 	   '("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
 	   '("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
-	     font-lock-variable-name-face)))
-  "*Rules for highlighting shell scripts.  See `sh-feature'.")
+	     font-lock-variable-name-face))
+    (rpm eval sh-append rpm2
+	 '("%{?\\(\\sw+\\)"  1 font-lock-keyword-face))
+    (rpm2 eval sh-append shell
+	  '("^\\(\\sw+\\):"  1 font-lock-variable-name-face)))
+  "Default expressions to highlight in Shell Script modes.  See `sh-feature'.")
 
 (defvar sh-font-lock-keywords-1
   '((sh "[ \t]in\\>"))
-  "*Additional rules for highlighting shell scripts.  See `sh-feature'.")
+  "Subdued level highlighting for Shell Script modes.")
 
 (defvar sh-font-lock-keywords-2 ()
-  "*Yet more rules for highlighting shell scripts.  See `sh-feature'.")
+  "Gaudy level highlighting for Shell Script modes.")
 
-(defvar sh-font-lock-keywords-only t
-  "*Value of `font-lock-keywords-only' for highlighting shell scripts.
-Default value is `t' because Emacs' syntax is not expressive enough to
-detect that $# does not start a comment.  Thus comments are fontified by
-regexp which means that a single apostrophe in a comment turns everything
-upto the next one or end of buffer into a string.")
+(defconst sh-font-lock-syntactic-keywords
+  ;; Mark a `#' character as having punctuation syntax in a variable reference.
+  ;; Really we should do this properly.  From Chet Ramey and Brian Fox:
+  ;; "A `#' begins a comment when it is unquoted and at the beginning of a
+  ;; word.  In the shell, words are separated by metacharacters."
+  ;; To do this in a regexp would be slow as it would be anchored to the right.
+  ;; But I can't be bothered to write a function to do it properly and
+  ;; efficiently.  So we only do it properly for `#' in variable references and
+  ;; do it efficiently by anchoring the regexp to the left.
+  '(("\\${?[^}#\n\t ]*\\(##?\\)" 1 (1 . nil))))
 
 ;; mode-command and utility functions
 
   (make-local-variable 'skeleton-pair-filter)
   (make-local-variable 'comint-dynamic-complete-functions)
   (make-local-variable 'comint-prompt-regexp)
-  (make-local-variable 'font-lock-keywords)
   (make-local-variable 'font-lock-defaults)
   (make-local-variable 'skeleton-filter)
   (make-local-variable 'skeleton-newline-indent-rigidly)
   (make-local-variable 'sh-shell-variables)
   (make-local-variable 'sh-shell-variables-initialized)
+  (make-local-variable 'imenu-generic-expression)
   (setq major-mode 'sh-mode
 	mode-name "Shell-script"
 	indent-line-function 'sh-indent-line
 	;; we can't look if previous line ended with `\'
 	comint-prompt-regexp "^[ \t]*"
 	font-lock-defaults
-	  `((sh-font-lock-keywords
-	     sh-font-lock-keywords-1
-	     sh-font-lock-keywords-2)
-	    ,sh-font-lock-keywords-only
-	    nil
-	    ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")))
+	'((sh-font-lock-keywords
+	   sh-font-lock-keywords-1 sh-font-lock-keywords-2)
+	  nil nil
+	  ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")) nil
+	  (font-lock-syntactic-keywords . sh-font-lock-syntactic-keywords))
 	skeleton-pair-alist '((?` _ ?`))
 	skeleton-pair-filter 'sh-quoted-p
 	skeleton-further-elements '((< '(- (min sh-indentation
 						(current-column)))))
 	skeleton-filter 'sh-feature
 	skeleton-newline-indent-rigidly t)
-  (save-excursion
-    ;; parse or insert magic number for exec() and set all variables depending
-    ;; on the shell thus determined
-    (goto-char (point-min))
-    (and (zerop (buffer-size))
-	 (not buffer-read-only)
-	 (sh-set-shell sh-shell-file)))
+  (make-local-variable 'parse-sexp-ignore-comments)
+  (setq parse-sexp-ignore-comments t)
+  ;; Parse or insert magic number for exec, and set all variables depending
+  ;; on the shell thus determined.
+  (let ((interpreter
+	 (save-excursion
+	   (goto-char (point-min))
+	   (cond ((looking-at "#![ \t]?\\([^ \t\n]*/bin/env[ \t]\\)?\\([^ \t\n]+\\)")
+		  (match-string 2))
+		 ((and buffer-file-name
+		       (string-match "\\.m?spec$" buffer-file-name))
+		  "rpm")))))
+    (if interpreter
+	(sh-set-shell interpreter nil nil)
+      (progn
+        ;; If we don't know the shell for this file, set the syntax
+        ;; table anyway, for the user's normal choice of shell.
+	(set-syntax-table (or (sh-feature sh-mode-syntax-table)
+			      (standard-syntax-table)))
+        ;; And avoid indent-new-comment-line (at least) losing.
+        (setq comment-start-skip "#+[\t ]*"))))
   (run-hooks 'sh-mode-hook))
 ;;;###autoload
 (defalias 'shell-script-mode 'sh-mode)
 (put 'sh-mode 'font-lock-defaults
      `((sh-font-lock-keywords
 	sh-font-lock-keywords-1
-	sh-font-lock-keywords-2)
-       ,sh-font-lock-keywords-only
-       nil
-       ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w"))))
-     
+	sh-font-lock-keywords-2)))
+;;       ,sh-font-lock-keywords-only
+;;       nil
+;;       ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w"))))
+
 
 (defun sh-font-lock-keywords (&optional keywords)
   "Function to get simple fontification based on `sh-font-lock-keywords'.
 This adds rules for comments and assignments."
   (sh-feature sh-font-lock-keywords
-	      (lambda (list)
-		`((,(concat (sh-feature sh-comment-prefix) "\\(#.*\\)")
-		   2 font-lock-comment-face t)
-		  (,(sh-feature sh-assignment-regexp)
-		   1 font-lock-variable-name-face)
-		  ,@keywords
-		  ,@list))))
+	      (when (stringp (sh-feature sh-assignment-regexp))
+		(lambda (list)
+		  `((,(sh-feature sh-assignment-regexp)
+		     1 font-lock-variable-name-face)
+		    ,@keywords
+		    ,@list)))))
 
 (defun sh-font-lock-keywords-1 (&optional builtins)
   "Function to get better fontification including keywords."
 
 (defun sh-set-shell (shell &optional no-query-flag insert-flag)
   "Set this buffer's shell to SHELL (a string).
-Makes this script executable via `executable-set-magic'.
+Makes this script executable via `executable-set-magic', and sets up the
+proper starting #!-line, if INSERT-FLAG is non-nil.
 Calls the value of `sh-set-shell-hook' if set."
   (interactive (list (completing-read "Name or path of shell: "
 				      ;; XEmacs change
 				      (lambda (x) (eq (cdr x) 'sh-mode)))
 		     (eq executable-query 'function)
 		     t))
-  ;; XEmacs change: we need to strip the extension in case we
-  ;; are running under mswindows
-  (setq sh-shell (intern (file-name-sans-extension
-			  (file-name-nondirectory shell)))
+  (if (string-match "\\.exe\\'" shell)
+      (setq shell (substring shell 0 (match-beginning 0))))
+  (setq sh-shell (intern (file-name-nondirectory shell))
 	sh-shell (or (cdr (assq sh-shell sh-alias-alist))
 		     sh-shell))
-  (setq sh-shell-file (executable-set-magic shell (sh-feature sh-shell-arg)))
+  (if insert-flag
+      (setq sh-shell-file
+	    (executable-set-magic shell (sh-feature sh-shell-arg)
+				  no-query-flag insert-flag)))
   (setq require-final-newline (sh-feature sh-require-final-newline)
 ;;;	local-abbrev-table (sh-feature sh-abbrevs)
 	font-lock-defaults-computed nil
-	;; Next two lines kill XEmacs
-	;font-lock-keywords nil		; force resetting
-	;font-lock-syntax-table nil
-	comment-start-skip (concat (sh-feature sh-comment-prefix) "#+[\t ]*")
+;; Packages should not need to set these variables directly.  sm.
+;	font-lock-keywords nil		; force resetting
+;	font-lock-syntax-table nil
+	comment-start-skip "#+[\t ]*"
 	mode-line-process (format "[%s]" sh-shell)
 	sh-shell-variables nil
 	sh-shell-variables-initialized nil
-	shell (sh-feature sh-variables))
-  (set-syntax-table (sh-feature sh-mode-syntax-table))
-  (while shell
-    (sh-remember-variable (car shell))
-    (setq shell (cdr shell)))
-  (and (boundp 'font-lock-mode)
-       font-lock-mode
-       ;; Gnu Emacs, doesn't work
-       (font-lock-mode (font-lock-mode 0)))
-       ;; (font-lock-fontify-buffer))
+	imenu-generic-expression (sh-feature sh-imenu-generic-expression)
+	imenu-case-fold-search nil)
+  (set-syntax-table (or (sh-feature sh-mode-syntax-table)
+			(standard-syntax-table)))
+  (let ((vars (sh-feature sh-variables)))
+    (while vars
+      (sh-remember-variable (car vars))
+      (setq vars (cdr vars))))
+;; Packages should not need to toggle Font Lock mode.  sm.
+;  (and (boundp 'font-lock-mode)
+;       font-lock-mode
+;       (font-lock-mode (font-lock-mode 0)))
   (run-hooks 'sh-set-shell-hook))
 
 
 
   - Fall back on successive ancestors (see `sh-ancestor-alist') as long as
     the alist contains no value for the current shell.
+    The ultimate default is always `sh'.
 
   - If the value thus looked up is a list starting with `eval' its `cdr' is
     first evaluated.  If that is also a list and the first argument is a
 	(while (and sh-shell
 		    (not (setq elt (assq sh-shell list))))
 	  (setq sh-shell (cdr (assq sh-shell sh-ancestor-alist))))
+	;; If the shell is not known, treat it as sh.
+	(unless elt
+	  (setq elt (assq 'sh list)))
 	(if (and (consp (setq val (cdr elt)))
 		 (eq (car val) 'eval))
 	    (setcdr elt
 
 
 (defun sh-indent-line ()
-  "Indent as far as preceding non-empty line, then by steps of `sh-indentation'.
+  "Indent a line for Sh mode (shell script mode).
+Indent as far as preceding non-empty line, then by steps of `sh-indentation'.
 Lines containing only comments are considered empty."
   (interactive)
   (let ((previous (save-excursion
-		    (while (and (not (bobp))
+		    (while (and (progn (beginning-of-line)
+				       (not (bobp)))
 				(not (eq (point-min) (point-at-bol)))
 				(progn
 				  (forward-line -1)
       > _ \n
       resume:
       < < "esac"))
-(put 'sh-case 'menu-enable '(sh-feature sh-case))
-
-
 
 (define-skeleton sh-for
   "Insert a for loop.  See `sh-feature'."
       "repeat " str "; do"\n
       > _ \n
       < "done"))
-(put 'sh-repeat 'menu-enable '(sh-feature sh-repeat))
+;;;(put 'sh-repeat 'menu-enable '(sh-feature sh-repeat))
 
 
 
 	 "select " str " in " _ "; do" \n
 	 > ?$ str \n
 	 < "done"))
-(put 'sh-select 'menu-enable '(sh-feature sh-select))
+;;;(put 'sh-select 'menu-enable '(sh-feature sh-select))
 
 
 
       "until " str "; do" \n
       > _ \n
       < "done"))
-(put 'sh-until 'menu-enable '(sh-feature sh-until))
+;;;(put 'sh-until 'menu-enable '(sh-feature sh-until))
 
 
 
       < < "esac" \n
       < "done" \n
       "shift " (sh-add "OPTIND" -1)))
-(put 'sh-while-getopts 'menu-enable '(sh-feature sh-while-getopts))
 
 
 
       (goto-char (match-end 1))))
 
 (provide 'sh-script)
+
 ;;; sh-script.el ends here