?\" "\"\""
?\' "\"'"
?\` "\"`"
+ ;; ?$ might also have a ". p" syntax. Both "'" and ". p" seem
+ ;; to work fine. This is needed so that dabbrev-expand
+ ;; $VARNAME works.
+ ?$ "'"
?! "_"
?% "_"
?: "_"
-(defvar sh-here-document-word "EOF"
+(defcustom sh-here-document-word "EOF"
"Word to delimit here documents.
-If the first character of this string is \"-\", this character will
-be removed from the string when it is used to close the here document.
-This convention is used by the Bash shell, for example, to indicate
-that leading tabs inside the here document should be ignored.
-Note that Emacs currently has no support for indenting inside here
-documents - you must insert literal tabs by hand.")
+If the first character of this string is \"-\", this is taken as
+part of the redirection operator, rather than part of the
+word (that is, \"<<-\" instead of \"<<\"). This is a feature
+used by some shells (for example Bash) to indicate that leading
+tabs inside the here document should be ignored. In this case,
+Emacs indents the initial body and end of the here document with
+tabs, to the same level as the start (note that apart from this
+there is no support for indentation of here documents). This
+will only work correctly if `sh-basic-offset' is a multiple of
+`tab-width'.
+
+Any quote characters or leading whitespace in the word are
+removed when closing the here document."
+ :type 'string
+ :group 'sh-script)
+
(defvar sh-test
'((sh "[ ]" . 3)
;; but it *did* have an asterisk in the docstring!
(defcustom sh-builtins
'((bash sh-append posix
- "." "alias" "bg" "bind" "builtin" "compgen" "complete"
+ "." "alias" "bg" "bind" "builtin" "caller" "compgen" "complete"
"declare" "dirs" "disown" "enable" "fc" "fg" "help" "history"
"jobs" "kill" "let" "local" "popd" "printf" "pushd" "shopt"
"source" "suspend" "typeset" "unalias")
\f
;; Font-Lock support
-(defface sh-heredoc-face
- '((((class color)
+(defface sh-heredoc
+ '((((min-colors 88) (class color)
+ (background dark))
+ (:foreground "yellow1" :weight bold))
+ (((class color)
(background dark))
(:foreground "yellow" :weight bold))
(((class color)
(: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
'((csh sh-append shell
;; 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
;; Using font-lock-string-face here confuses sh-get-indent-info.
- ("\\\\$" 0 font-lock-warning-face)
+ ("\\(^\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\\\)$" 3 'sh-escaped-newline)
("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
font-lock-variable-name-face))
: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)
(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)
(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))
))
(defun sh-var-value (var &optional ignore-error)
"Return the value of variable VAR, interpreting symbols.
It can also return t or nil.
-If an illegal value is found, throw an error unless Optional argument
+If an invalid value is found, throw an error unless Optional argument
IGNORE-ERROR is non-nil."
(let ((val (symbol-value var)))
(cond
(let ((sh-add-buffer (current-buffer)))
(list (completing-read "Variable: " 'sh-add-completer)
(prefix-numeric-value current-prefix-arg))))
- (insert (sh-feature '((bash . "$[ ")
+ (insert (sh-feature '((bash . "$(( ")
(ksh88 . "$(( ")
(posix . "$(( ")
(rc . "`{expr $")
(sh-remember-variable var)
(if (< delta 0) " - " " + ")
(number-to-string (abs delta))
- (sh-feature '((bash . " ]")
+ (sh-feature '((bash . " ))")
(ksh88 . " ))")
(posix . " ))")
(rc . "}")
"esac" >
\n "done"
> \n
- "shift " (sh-add "OPTIND" -1) \n))
+ "shift " (sh-add "OPTIND" -1) \n
+ "OPTIND=1" \n))
(match-string 1))))))
-
(defun sh-maybe-here-document (arg)
"Insert self. Without prefix, following unquoted `<' inserts here document.
The document is bounded by `sh-here-document-word'."
(save-excursion
(backward-char 2)
(sh-quoted-p))
- (progn
+ (let ((tabs (if (string-match "\\`-" sh-here-document-word)
+ (make-string (/ (current-indentation) tab-width) ?\t)
+ ""))
+ (delim (replace-regexp-in-string "['\"]" ""
+ sh-here-document-word)))
(insert sh-here-document-word)
(or (eolp) (looking-at "[ \t]") (insert ? ))
(end-of-line 1)
(while
(sh-quoted-p)
(end-of-line 2))
- (newline)
+ (insert ?\n tabs)
(save-excursion
- (insert ?\n (substring
- sh-here-document-word
- (if (string-match "^-" sh-here-document-word) 1 0)))))))
+ (insert ?\n tabs (replace-regexp-in-string
+ "\\`-?[ \t]*" "" delim))))))
\f
;; various other commands
This function does not modify the last line of the region if the region ends
right at the start of the following line; it does not modify blank lines
-at the start of the region. So you can put the region around an entire
+at the start of the region. So you can put the region around an entire
shell command and conveniently use this command."
(interactive "r\nP")
(save-excursion