;; 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.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Commentary:
(require 'comint))
(require 'executable)
+(defvar font-lock-comment-face)
+(defvar font-lock-set-defaults)
+(defvar font-lock-string-face)
(defgroup sh nil
- "Shell programming utilities"
+ "Shell programming utilities."
:group 'unix
:group 'languages)
(defgroup sh-script nil
- "Shell script mode"
+ "Shell script mode."
:group 'sh
:prefix "sh-")
?\" "\"\""
?\' "\"'"
?\` "\"`"
+ ;; ?$ might also have a ". p" syntax. Both "'" and ". p" seem
+ ;; to work fine. This is needed so that dabbrev-expand
+ ;; $VARNAME works.
+ ?$ "'"
?! "_"
?% "_"
?: "_"
\f
;; Font-Lock support
-(defface sh-heredoc-face
+(defface sh-heredoc
'((((min-colors 88) (class color)
(background dark))
(:foreground "yellow1" :weight bold))
(:weight bold)))
"Face to show a here-document"
:group 'sh-indentation)
-(defvar sh-heredoc-face 'sh-heredoc-face)
+;; backward-compatibility alias
+(put 'sh-heredoc-face 'face-alias 'sh-heredoc)
+(defvar sh-heredoc-face 'sh-heredoc)
(defface sh-escaped-newline '((t :inherit font-lock-string-face))
"Face used for (non-escaped) backslash at end of a line in Shell-script mode."
:group 'sh-script
:version "22.1")
-(defvar sh-font-lock-keywords
+(defvar sh-font-lock-keywords-var
'((csh sh-append shell
("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
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)))
+ (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t))
+ ("\\(?:^\\s *\\|[[();&|]\\s *\\|\\(?:\\s +-[ao]\\|if\\|else\\|then\\|while\\|do\\)\\s +\\)\\(!\\)"
+ 1 font-lock-negation-char-face))
;; The next entry is only used for defining the others
- (shell sh-append executable-font-lock-keywords
+ (shell
;; Using font-lock-string-face here confuses sh-get-indent-info.
("\\(^\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\\\)$" 3 'sh-escaped-newline)
("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
("^\\(\\sw+\\):" 1 font-lock-variable-name-face)))
"Default expressions to highlight in Shell Script modes. See `sh-feature'.")
-(defvar sh-font-lock-keywords-1
+(defvar sh-font-lock-keywords-var-1
'((sh "[ \t]in\\>"))
"Subdued level highlighting for Shell Script modes.")
-(defvar sh-font-lock-keywords-2 ()
+(defvar sh-font-lock-keywords-var-2 ()
"Gaudy level highlighting for Shell Script modes.")
;; These are used for the syntax table stuff (derived from cperl-mode).
:group 'sh-indentation)
(defcustom sh-popup-occur-buffer nil
- "*Controls when `sh-learn-buffer-indent' pops the *indent* buffer.
+ "*Controls when `sh-learn-buffer-indent' pops the `*indent*' buffer.
If t it is always shown. If nil, it is shown only when there
are conflicts."
:type '(choice
(defcustom sh-basic-offset 4
"*The default indentation increment.
-This value is used for the + and - symbols in an indentation variable."
+This value is used for the `+' and `-' symbols in an indentation variable."
:type 'integer
:group 'sh-indentation)
:menu-tag "/ Indent left half sh-basic-offset")))
(defcustom sh-indent-for-else 0
- "*How much to indent an else relative to an if. Usually 0."
+ "*How much to indent an `else' relative to its `if'. Usually 0."
:type `(choice
(integer :menu-tag "A number (positive=>indent right)"
:tag "A number")
sh-symbol-list))
(defcustom sh-indent-for-fi 0
- "*How much to indent a fi relative to an if. Usually 0."
+ "*How much to indent a `fi' relative to its `if'. Usually 0."
:type `(choice ,@ sh-number-or-symbol-list )
:group 'sh-indentation)
-(defcustom sh-indent-for-done '0
- "*How much to indent a done relative to its matching stmt. Usually 0."
+(defcustom sh-indent-for-done 0
+ "*How much to indent a `done' relative to its matching stmt. Usually 0."
:type `(choice ,@ sh-number-or-symbol-list )
:group 'sh-indentation)
(defcustom sh-indent-after-else '+
- "*How much to indent a statement after an else statement."
+ "*How much to indent a statement after an `else' statement."
:type `(choice ,@ sh-number-or-symbol-list )
:group 'sh-indentation)
(defcustom sh-indent-after-if '+
- "*How much to indent a statement after an if statement.
-This includes lines after else and elif statements, too, but
-does not affect then else elif or fi statements themselves."
+ "*How much to indent a statement after an `if' statement.
+This includes lines after `else' and `elif' statements, too, but
+does not affect the `else', `elif' or `fi' statements themselves."
:type `(choice ,@ sh-number-or-symbol-list )
:group 'sh-indentation)
(defcustom sh-indent-for-then 0
- "*How much to indent a then relative to an if."
+ "*How much to indent a `then' relative to its `if'."
:type `(choice ,@ sh-number-or-symbol-list )
:group 'sh-indentation)
-(defcustom sh-indent-for-do '*
- "*How much to indent a do statement.
-This is relative to the statement before the do, i.e. the
-while until or for statement."
+(defcustom sh-indent-for-do 0
+ "*How much to indent a `do' statement.
+This is relative to the statement before the `do', typically a
+`while', `until', `for', `repeat' or `select' statement."
:type `(choice ,@ sh-number-or-symbol-list)
:group 'sh-indentation)
-(defcustom sh-indent-after-do '*
- "*How much to indent a line after a do statement.
-This is used when the do is the first word of the line.
-This is relative to the statement before the do, e.g. a
-while for repeat or select statement."
+(defcustom sh-indent-after-do '+
+ "*How much to indent a line after a `do' statement.
+This is used when the `do' is the first word of the line.
+This is relative to the statement before the `do', typically a
+`while', `until', `for', `repeat' or `select' statement."
:type `(choice ,@ sh-number-or-symbol-list)
:group 'sh-indentation)
(defcustom sh-indent-after-loop-construct '+
"*How much to indent a statement after a loop construct.
-This variable is used when the keyword \"do\" is on the same line as the
-loop statement (e.g. \"until\", \"while\" or \"for\").
-If the do is on a line by itself, then `sh-indent-after-do' is used instead."
+This variable is used when the keyword `do' is on the same line as the
+loop statement (e.g., `until', `while' or `for').
+If the `do' is on a line by itself, then `sh-indent-after-do' is used instead."
:type `(choice ,@ sh-number-or-symbol-list)
:group 'sh-indentation)
(defcustom sh-indent-after-done 0
- "*How much to indent a statement after a \"done\" keyword.
-Normally this is 0, which aligns the \"done\" to the matching
+ "*How much to indent a statement after a `done' keyword.
+Normally this is 0, which aligns the `done' to the matching
looping construct line.
-Setting it non-zero allows you to have the \"do\" statement on a line
+Setting it non-zero allows you to have the `do' statement on a line
by itself and align the done under to do."
:type `(choice ,@ sh-number-or-symbol-list)
:group 'sh-indentation)
(defcustom sh-indent-for-case-label '+
"*How much to indent a case label statement.
-This is relative to the line containing the case statement."
+This is relative to the line containing the `case' statement."
:type `(choice ,@ sh-number-or-symbol-list)
:group 'sh-indentation)
(defcustom sh-indent-for-case-alt '++
"*How much to indent statements after the case label.
-This is relative to the line containing the case statement."
+This is relative to the line containing the `case' statement."
:type `(choice ,@ sh-number-or-symbol-list)
:group 'sh-indentation)
(defcustom sh-indent-after-open '+
"*How much to indent after a line with an opening parenthesis or brace.
-For an open paren after a function `sh-indent-after-function' is used."
+For an open paren after a function, `sh-indent-after-function' is used."
:type `(choice ,@ sh-number-or-symbol-list)
:group 'sh-indentation)
;; These 2 are for the rc shell:
(defcustom sh-indent-after-switch '+
- "*How much to indent a case statement relative to the switch statement.
+ "*How much to indent a `case' statement relative to the `switch' statement.
This is for the rc shell."
:type `(choice ,@ sh-number-or-symbol-list)
:group 'sh-indentation)
(defcustom sh-indent-after-case '+
- "*How much to indent a statement relative to the case statement.
+ "*How much to indent a statement relative to the `case' statement.
This is for the rc shell."
:type `(choice ,@ sh-number-or-symbol-list)
:group 'sh-indentation)
paragraph-start (concat page-delimiter "\\|$")
paragraph-separate paragraph-start
comment-start "# "
+ comment-start-skip "#+[\t ]*"
+ local-abbrev-table sh-mode-abbrev-table
comint-dynamic-complete-functions sh-dynamic-complete-functions
;; we can't look if previous line ended with `\'
comint-prompt-regexp "^[ \t]*"
+ imenu-case-fold-search nil
font-lock-defaults
`((sh-font-lock-keywords
sh-font-lock-keywords-1 sh-font-lock-keywords-2)
(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))
+ (string-match "\\.m?spec\\'" buffer-file-name))
"rpm")))))
(sh-set-shell (or interpreter sh-shell-file) nil nil))
- (run-hooks 'sh-mode-hook))
+ (run-mode-hooks 'sh-mode-hook))
;;;###autoload
(defalias 'shell-script-mode 'sh-mode)
(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
+ (sh-feature sh-font-lock-keywords-var
(when (stringp (sh-feature sh-assignment-regexp))
(lambda (list)
`((,(sh-feature sh-assignment-regexp)
1 font-lock-variable-name-face)
,@keywords
- ,@list)))))
+ ,@list
+ ,@executable-font-lock-keywords)))))
(defun sh-font-lock-keywords-1 (&optional builtins)
"Function to get better fontification including keywords."
"\\>")
(2 font-lock-keyword-face nil t)
(6 font-lock-builtin-face))
- ,@(sh-feature sh-font-lock-keywords-2)))
+ ,@(sh-feature sh-font-lock-keywords-var-2)))
(,(concat keywords "\\)\\>")
2 font-lock-keyword-face)
- ,@(sh-feature sh-font-lock-keywords-1)))))
+ ,@(sh-feature sh-font-lock-keywords-var-1)))))
(defun sh-font-lock-keywords-2 ()
"Function to get better fontification including keywords and builtins."
("case" sh-handle-this-rc-case sh-handle-prev-rc-case))))
+
(defun sh-set-shell (shell &optional no-query-flag insert-flag)
"Set this buffer's shell to SHELL (a string).
When used interactively, insert the proper starting #!-line,
(if (eq tem t)
(setq require-final-newline mode-require-final-newline)))
(setq
- comment-start-skip "#+[\t ]*"
- local-abbrev-table sh-mode-abbrev-table
mode-line-process (format "[%s]" sh-shell)
sh-shell-variables nil
sh-shell-variables-initialized nil
- imenu-generic-expression (sh-feature sh-imenu-generic-expression)
- imenu-case-fold-search nil)
+ imenu-generic-expression (sh-feature sh-imenu-generic-expression))
(make-local-variable 'sh-mode-syntax-table)
(let ((tem (sh-feature sh-mode-syntax-table-input)))
(setq sh-mode-syntax-table
(message "Indentation setup for shell type %s" sh-shell))
(message "No indentation for this shell type.")
(setq indent-line-function 'sh-basic-indent-line))
+ (when font-lock-mode
+ (setq font-lock-set-defaults nil)
+ (font-lock-set-defaults)
+ (font-lock-fontify-buffer))
(run-hooks 'sh-set-shell-hook))
-
(defun sh-feature (alist &optional function)
"Index ALIST by the current shell.
If ALIST isn't a list where every element is a cons, it is returned as is.
one shell to be derived from another shell.
The value thus determined is physically replaced into the alist.
-Optional FUNCTION is applied to the determined value and the result is cached
-in ALIST."
+If FUNCTION is non-nil, it is called with one argument,
+the value thus obtained, and the result is used instead."
(or (if (consp alist)
+ ;; Check for something that isn't a valid alist.
(let ((l alist))
(while (and l (consp (car l)))
(setq l (cdr l)))
(if l alist)))
- (if function
- (cdr (assoc (setq function (cons sh-shell function)) alist)))
- (let ((sh-shell sh-shell)
- elt val)
- (while (and sh-shell
- (not (setq elt (assq sh-shell alist))))
- (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 alist)))
- (if (and (consp (setq val (cdr elt)))
- (memq (car val) '(sh-append sh-modify)))
- (setcdr elt
- (setq val
- (apply (car val)
- (let ((sh-shell (car (cdr val))))
- (if (assq sh-shell alist)
- (sh-feature alist)
- (eval sh-shell)))
- (cddr val)))))
- (if function
- (nconc alist
- (list (cons function
- (setq sh-shell (car function)
- val (funcall (cdr function) val))))))
- val)))
+
+ (let ((orig-sh-shell sh-shell))
+ (let ((sh-shell sh-shell)
+ elt val)
+ (while (and sh-shell
+ (not (setq elt (assq sh-shell alist))))
+ (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 alist)))
+ (setq val (cdr elt))
+ (if (and (consp val)
+ (memq (car val) '(sh-append sh-modify)))
+ (setq val
+ (apply (car val)
+ ;; Refer to the value for a different shell,
+ ;; as a kind of inheritance.
+ (let ((sh-shell (car (cdr val))))
+ (sh-feature alist))
+ (cddr val))))
+ (if function
+ (setq sh-shell orig-sh-shell
+ val (funcall function val)))
+ val))))
;; Continuation lines are handled specially
(if (sh-this-is-a-continuation)
(progn
- ;; We assume the line being continued is already
- ;; properly indented...
- ;; (setq prev-line-end (sh-prev-line))
- (setq align-point (sh-prev-line nil))
- (setq result (list '(+ sh-indent-for-continuation)))
+ (setq result
+ (if (save-excursion
+ (beginning-of-line)
+ (not (memq (char-before (- (point) 2)) '(?\s ?\t))))
+ ;; By convention, if the continuation \ is not
+ ;; preceded by a SPC or a TAB it means that the line
+ ;; is cut at a place where spaces cannot be freely
+ ;; added/removed. I.e. do not indent the line.
+ (list '(= nil))
+ ;; We assume the line being continued is already
+ ;; properly indented...
+ ;; (setq prev-line-end (sh-prev-line))
+ (setq align-point (sh-prev-line nil))
+ (list '(+ sh-indent-for-continuation))))
(setq have-result t))
(beginning-of-line)
(skip-chars-forward " \t")
(sh-debug "result is now: %s" result)
(or result
- (if prev-line-end
- (setq result (list (list t prev-line-end)))
- (setq result (list (list '= 'sh-first-lines-indent)))
- ))
+ (setq result (list (if prev-line-end
+ (list t prev-line-end)
+ (list '= 'sh-first-lines-indent)))))
(if (eq result t)
(setq result nil))
(if (looking-at "[\"'`]")
(sh-safe-forward-sexp)
;; (> (skip-chars-forward "^ \t\n\"'`") 0)
- (> (skip-chars-forward "-_a-zA-Z\$0-9") 0)
+ (> (skip-chars-forward "-_a-zA-Z$0-9") 0)
))
(buffer-substring start (point))
))
(goto-char where))
(prog1
(buffer-substring (point)
- (progn (skip-chars-forward "^ \t\n;")(point)))
+ (progn (skip-chars-forward "^ \t\n;&")(point)))
(unless and-move
(goto-char start)))))
(defun sh-mark-init (buffer)
"Initialize a BUFFER to be used by `sh-mark-line'."
- (save-excursion
- (set-buffer (get-buffer-create buffer))
+ (with-current-buffer (get-buffer-create buffer)
(erase-buffer)
- (occur-mode)
- ))
+ (occur-mode)))
(defun sh-mark-line (message point buffer &optional add-linenum occur-point)
(let ((var (car learned-var)))
(sh-mark-line (format " %s %s" var (symbol-value var))
(nth 2 learned-var) out-buffer)))
- (save-excursion
- (set-buffer out-buffer)
+ (with-current-buffer out-buffer
(goto-char (point-min))
(insert
(format "Indentation values for buffer %s.\n" name)
t means to return a list of all possible completions of STRING.
`lambda' means to return t if STRING is a valid completion as it stands."
(let ((sh-shell-variables
- (save-excursion
- (set-buffer sh-add-buffer)
+ (with-current-buffer sh-add-buffer
(or sh-shell-variables-initialized
(sh-shell-initialize-variables))
(nconc (mapcar (lambda (var)
(delim (replace-regexp-in-string "['\"]" ""
sh-here-document-word)))
(insert sh-here-document-word)
- (or (eolp) (looking-at "[ \t]") (insert ? ))
+ (or (eolp) (looking-at "[ \t]") (insert ?\s))
(end-of-line 1)
(while
(sh-quoted-p)