;;; sh-script.el --- shell-script editing commands for Emacs
-;; Copyright (C) 1993-1997, 1999, 2001-2012 Free Software Foundation, Inc.
+;; Copyright (C) 1993-1997, 1999, 2001-2013 Free Software Foundation,
+;; Inc.
;; Author: Daniel Pfeiffer <occitan@esperanto.org>
;; Version: 2.0f
. ((nil
;; function FOO
;; function FOO()
- "^\\s-*function\\s-+\\\([[:alpha:]_][[:alnum:]_]+\\)\\s-*\\(?:()\\)?"
+ "^\\s-*function\\s-+\\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*\\(?:()\\)?"
1)
;; FOO()
(nil
- "^\\s-*\\([[:alpha:]_][[:alnum:]_]+\\)\\s-*()"
+ "^\\s-*\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*()"
1)
)))
"Alist of regular expressions for recognizing shell function definitions.
:group 'sh-script
:version "20.4")
+(defun sh-current-defun-name ()
+ "Find the name of function or variable at point.
+For use in `add-log-current-defun-function'."
+ (save-excursion
+ (end-of-line)
+ (when (re-search-backward
+ (concat "\\(?:"
+ ;; function FOO
+ ;; function FOO()
+ "^\\s-*function\\s-+\\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*\\(?:()\\)?"
+ "\\)\\|\\(?:"
+ ;; FOO()
+ "^\\s-*\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*()"
+ "\\)\\|\\(?:"
+ ;; FOO=
+ "^\\([[:alpha:]_][[:alnum:]_]*\\)="
+ "\\)")
+ nil t)
+ (or (match-string-no-properties 1)
+ (match-string-no-properties 2)
+ (match-string-no-properties 3)))))
+
(defvar sh-shell-variables nil
"Alist of shell variable names that should be included in completion.
These are used for completion in addition to all the variables named
(concat "<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\|[-/~._]\\)+\\)"
sh-escaped-line-re "\\(\n\\)")))
+(defun sh--inside-noncommand-expression (pos)
+ (save-excursion
+ (let ((ppss (syntax-ppss pos)))
+ (when (nth 1 ppss)
+ (goto-char (nth 1 ppss))
+ (pcase (char-after)
+ ;; $((...)) or $[...] or ${...}.
+ (`?\( (and (eq ?\( (char-before))
+ (eq ?\$ (char-before (1- (point))))))
+ ((or `?\{ `?\[) (eq ?\$ (char-before))))))))
+
(defun sh-font-lock-open-heredoc (start string eol)
"Determine the syntax of the \\n after a <<EOF.
START is the position of <<.
be indented (i.e. a <<- was used rather than just <<).
Point is at the beginning of the next line."
(unless (or (memq (char-before start) '(?< ?>))
- (sh-in-comment-or-string start))
+ (sh-in-comment-or-string start)
+ (sh--inside-noncommand-expression start))
;; We're looking at <<STRING, so we add "^STRING$" to the syntactic
;; font-lock keywords to detect the end of this here document.
(let ((str (replace-regexp-in-string "['\"]" "" string))
(defun sh-font-lock-paren (start)
(unless (nth 8 (syntax-ppss))
(save-excursion
- (goto-char start)
- ;; Skip through all patterns
- (while
- (progn
- (while
- (progn
- (forward-comment (- (point-max)))
- (when (and (eolp) (sh-is-quoted-p (point)))
- (forward-char -1)
- t)))
- ;; Skip through one pattern
- (while
- (or (/= 0 (skip-syntax-backward "w_"))
- (/= 0 (skip-chars-backward "-$=?[]*@/\\\\"))
- (and (sh-is-quoted-p (1- (point)))
- (goto-char (- (point) 2)))
- (when (memq (char-before) '(?\" ?\' ?\}))
- (condition-case nil (progn (backward-sexp 1) t)
- (error nil)))))
- ;; Patterns can be preceded by an open-paren (Bug#1320).
- (if (eq (char-before (point)) ?\()
+ (let ((open nil))
+ (goto-char start)
+ ;; Skip through all patterns
+ (while
+ (progn
+ (while
+ (progn
+ (forward-comment (- (point-max)))
+ (when (and (eolp) (sh-is-quoted-p (point)))
+ (forward-char -1)
+ t)))
+ ;; Skip through one pattern
+ (while
+ (or (/= 0 (skip-syntax-backward "w_"))
+ (/= 0 (skip-chars-backward "-$=?[]*@/\\\\"))
+ (and (sh-is-quoted-p (1- (point)))
+ (goto-char (- (point) 2)))
+ (when (memq (char-before) '(?\" ?\' ?\}))
+ (condition-case nil (progn (backward-sexp 1) t)
+ (error nil)))))
+ ;; Patterns can be preceded by an open-paren (bug#1320).
+ (when (eq (char-before (point)) ?\()
+ (backward-char 1)
+ (setq open (point)))
+ (while (progn
+ (forward-comment (- (point-max)))
+ ;; Maybe we've bumped into an escaped newline.
+ (sh-is-quoted-p (point)))
(backward-char 1))
- (while (progn
- (forward-comment (- (point-max)))
- ;; Maybe we've bumped into an escaped newline.
- (sh-is-quoted-p (point)))
- (backward-char 1))
- (when (eq (char-before) ?|)
- (backward-char 1) t)))
- (and (> (point) (1+ (point-min)))
- (progn (backward-char 2)
- (if (> start (line-end-position))
- (put-text-property (point) (1+ start)
- 'syntax-multiline t))
- ;; FIXME: The `in' may just be a random argument to
- ;; a normal command rather than the real `in' keyword.
- ;; I.e. we should look back to try and find the
- ;; corresponding `case'.
- (and (looking-at ";[;&]\\|\\_<in")
- ;; ";; esac )" is a case that looks like a case-pattern
- ;; but it's really just a close paren after a case
- ;; statement. I.e. if we skipped over `esac' just now,
- ;; we're not looking at a case-pattern.
- (not (looking-at "..[ \t\n]+esac[^[:word:]_]"))))
- sh-st-punc))))
+ (when (eq (char-before) ?|)
+ (backward-char 1) t)))
+ (and (> (point) (1+ (point-min)))
+ (progn (backward-char 2)
+ (if (> start (line-end-position))
+ (put-text-property (point) (1+ start)
+ 'syntax-multiline t))
+ ;; FIXME: The `in' may just be a random argument to
+ ;; a normal command rather than the real `in' keyword.
+ ;; I.e. we should look back to try and find the
+ ;; corresponding `case'.
+ (and (looking-at ";[;&]\\|\\_<in")
+ ;; ";; esac )" is a case that looks
+ ;; like a case-pattern but it's really just a close
+ ;; paren after a case statement. I.e. if we skipped
+ ;; over `esac' just now, we're not looking
+ ;; at a case-pattern.
+ (not (looking-at "..[ \t\n]+esac[^[:word:]_]"))))
+ (progn
+ (when open
+ (put-text-property open (1+ open) 'syntax-table sh-st-punc))
+ sh-st-punc))))))
(defun sh-font-lock-backslash-quote ()
(if (eq (save-excursion (nth 3 (syntax-ppss (match-beginning 0)))) ?\')
(make-local-variable 'sh-shell-file)
(make-local-variable 'sh-shell)
- (set (make-local-variable 'skeleton-pair-default-alist)
- sh-skeleton-pair-default-alist)
- (set (make-local-variable 'skeleton-end-hook)
- (lambda () (or (eolp) (newline) (indent-relative))))
-
- (set (make-local-variable 'paragraph-start) (concat page-delimiter "\\|$"))
- (set (make-local-variable 'paragraph-separate) paragraph-start)
- (set (make-local-variable 'comment-start) "# ")
- (set (make-local-variable 'comment-start-skip) "#+[\t ]*")
- (set (make-local-variable 'local-abbrev-table) sh-mode-abbrev-table)
- (set (make-local-variable 'comint-dynamic-complete-functions)
- sh-dynamic-complete-functions)
+ (setq-local skeleton-pair-default-alist
+ sh-skeleton-pair-default-alist)
+ (setq-local skeleton-end-hook
+ (lambda () (or (eolp) (newline) (indent-relative))))
+
+ (setq-local paragraph-start (concat page-delimiter "\\|$"))
+ (setq-local paragraph-separate paragraph-start)
+ (setq-local comment-start "# ")
+ (setq-local comment-start-skip "#+[\t ]*")
+ (setq-local local-abbrev-table sh-mode-abbrev-table)
+ (setq-local comint-dynamic-complete-functions
+ sh-dynamic-complete-functions)
(add-hook 'completion-at-point-functions 'comint-completion-at-point nil t)
;; we can't look if previous line ended with `\'
- (set (make-local-variable 'comint-prompt-regexp) "^[ \t]*")
- (set (make-local-variable 'imenu-case-fold-search) nil)
- (set (make-local-variable 'font-lock-defaults)
- `((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-face-function
- . sh-font-lock-syntactic-face-function)))
- (set (make-local-variable 'syntax-propertize-function)
- #'sh-syntax-propertize-function)
+ (setq-local comint-prompt-regexp "^[ \t]*")
+ (setq-local imenu-case-fold-search nil)
+ (setq font-lock-defaults
+ `((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-face-function
+ . sh-font-lock-syntactic-face-function)))
+ (setq-local syntax-propertize-function #'sh-syntax-propertize-function)
(add-hook 'syntax-propertize-extend-region-functions
#'syntax-propertize-multiline 'append 'local)
(sh-electric-here-document-mode 1)
- (set (make-local-variable 'skeleton-pair-alist) '((?` _ ?`)))
- (set (make-local-variable 'skeleton-pair-filter-function) 'sh-quoted-p)
- (set (make-local-variable 'skeleton-further-elements)
- '((< '(- (min sh-indentation (current-column))))))
- (set (make-local-variable 'skeleton-filter-function) 'sh-feature)
- (set (make-local-variable 'skeleton-newline-indent-rigidly) t)
- (set (make-local-variable 'defun-prompt-regexp)
- (concat "^\\(function[ \t]\\|[[:alnum:]]+[ \t]+()[ \t]+\\)"))
+ (setq-local skeleton-pair-alist '((?` _ ?`)))
+ (setq-local skeleton-pair-filter-function 'sh-quoted-p)
+ (setq-local skeleton-further-elements
+ '((< '(- (min sh-indentation (current-column))))))
+ (setq-local skeleton-filter-function 'sh-feature)
+ (setq-local skeleton-newline-indent-rigidly t)
+ (setq-local defun-prompt-regexp
+ (concat "^\\(function[ \t]\\|[[:alnum:]]+[ \t]+()[ \t]+\\)"))
+ (setq-local add-log-current-defun-function #'sh-current-defun-name)
;; Parse or insert magic number for exec, and set all variables depending
;; on the shell thus determined.
(sh-set-shell
(cmd "|" cmd) (cmd "|&" cmd)
(cmd "&&" cmd) (cmd "||" cmd)
(cmd ";" cmd) (cmd "&" cmd))
- (pattern (pattern "|" pattern))
+ (rpattern (rpattern "|" rpattern))
+ (pattern (rpattern) ("case-(" rpattern))
(branches (branches ";;" branches)
(branches ";&" branches) (branches ";;&" branches) ;bash.
(pattern "case-)" cmd)))
(tok (smie-default-forward-token)))
(cond
((equal tok ")") "case-)")
+ ((equal tok "(") "case-(")
((and tok (string-match "\\`[a-z]" tok)
(assoc tok smie-grammar)
(not
(let ((tok (smie-default-backward-token)))
(cond
((equal tok ")") "case-)")
+ ((equal tok "(") "case-(")
((and tok (string-match "\\`[a-z]" tok)
(assoc tok smie-grammar)
(not (save-excursion (sh-smie--sh-keyword-p tok))))
;; (defconst sh-smie-csh-grammar
;; (smie-prec2->grammar
;; (smie-bnf->prec2
-;; '((exp) ;A constant, or a $var, or a sequence of them…
+;; '((exp) ;A constant, or a $var, or a sequence of them...
;; (elseifcmd (cmd)
;; (cmd "else" "else-if" exp "then" elseifcmd))
;; (cmd ("switch" branches "endsw")
(executable-set-magic shell (sh-feature sh-shell-arg)
no-query-flag insert-flag)))
(setq mode-line-process (format "[%s]" sh-shell))
- (set (make-local-variable 'sh-shell-variables) nil)
- (set (make-local-variable 'sh-shell-variables-initialized) nil)
- (set (make-local-variable 'imenu-generic-expression)
- (sh-feature sh-imenu-generic-expression))
+ (setq-local sh-shell-variables nil)
+ (setq-local sh-shell-variables-initialized nil)
+ (setq-local imenu-generic-expression
+ (sh-feature sh-imenu-generic-expression))
(let ((tem (sh-feature sh-mode-syntax-table-input)))
(when tem
- (set (make-local-variable 'sh-mode-syntax-table)
- (apply 'sh-mode-syntax-table tem))
+ (setq-local sh-mode-syntax-table
+ (apply 'sh-mode-syntax-table tem))
(set-syntax-table sh-mode-syntax-table)))
(dolist (var (sh-feature sh-variables))
(sh-remember-variable var))
- (if (set (make-local-variable 'sh-indent-supported-here)
- (sh-feature sh-indent-supported))
+ (if (setq-local sh-indent-supported-here
+ (sh-feature sh-indent-supported))
(progn
(message "Setting up indent for shell type %s" sh-shell)
(if sh-use-smie
(funcall mksym "rules")
:forward-token (funcall mksym "forward-token")
:backward-token (funcall mksym "backward-token")))
- (set (make-local-variable 'parse-sexp-lookup-properties) t)
- (set (make-local-variable 'sh-kw-alist) (sh-feature sh-kw))
+ (setq-local parse-sexp-lookup-properties t)
+ (setq-local sh-kw-alist (sh-feature sh-kw))
(let ((regexp (sh-feature sh-kws-for-done)))
(if regexp
- (set (make-local-variable 'sh-regexp-for-done)
- (sh-mkword-regexpr (regexp-opt regexp t)))))
+ (setq-local sh-regexp-for-done
+ (sh-mkword-regexpr (regexp-opt regexp t)))))
(message "setting up indent stuff")
;; sh-mode has already made indent-line-function local
;; but do it in case this is called before that.
- (set (make-local-variable 'indent-line-function) 'sh-indent-line))
+ (setq-local indent-line-function 'sh-indent-line))
(if sh-make-vars-local
(sh-make-vars-local))
(message "Indentation setup for shell type %s" sh-shell))