X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/bba90ab24e80476efcad6b6a770fd5fda522a621..bd2d43dc7461cc3d8cdcecf069fc275baf60c842:/lisp/progmodes/sh-script.el diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index da30edf272..84b0c8b1af 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el @@ -1,11 +1,10 @@ -;;; sh-script.el --- shell-script editing commands for Emacs +;;; sh-script.el --- shell-script editing commands for Emacs -*- lexical-binding:t -*- -;; Copyright (C) 1993-1997, 1999, 2001-2013 Free Software Foundation, -;; Inc. +;; Copyright (C) 1993-1997, 1999, 2001-2014 Free Software Foundation, Inc. ;; Author: Daniel Pfeiffer ;; Version: 2.0f -;; Maintainer: FSF +;; Maintainer: emacs-devel@gnu.org ;; Keywords: languages, unix ;; This file is part of GNU Emacs. @@ -228,6 +227,7 @@ '((ash . sh) (bash . jsh) (bash2 . jsh) + (dash . ash) (dtksh . ksh) (es . rc) (itcsh . tcsh) @@ -255,6 +255,7 @@ rc Plan 9 Shell es Extensible Shell sh Bourne Shell ash Almquist Shell + dash Debian Almquist Shell jsh Bourne Shell with Job Control bash GNU Bourne Again Shell ksh88 Korn Shell '88 @@ -267,6 +268,7 @@ sh Bourne Shell posix IEEE 1003.2 Shell Standard wsh ? Shell" :type '(repeat (cons symbol symbol)) + :version "24.4" ; added dash :group 'sh-script) @@ -335,11 +337,11 @@ shell it really is." . ((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. @@ -353,6 +355,28 @@ See `sh-feature' and `imenu-generic-expression'." :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 @@ -475,6 +499,9 @@ This is buffer-local in every such buffer.") (define-key map "\C-c+" 'sh-add) (define-key map "\C-\M-x" 'sh-execute-region) (define-key map "\C-c\C-x" 'executable-interpret) + (define-key map "\C-c\C-n" 'sh-send-line-or-region-and-step) + (define-key map "\C-c\C-d" 'sh-cd-here) + (define-key map "\C-c\C-z" 'sh-show-shell) (define-key map [remap delete-backward-char] 'backward-delete-char-untabify) @@ -648,7 +675,9 @@ removed when closing the here document." "." "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") + "source" "suspend" "typeset" "unalias" + ;; bash4 + "mapfile" "readarray") ;; The next entry is only used for defining the others (bourne sh-append shell @@ -712,6 +741,7 @@ implemented as aliases. See `sh-feature'." :type '(repeat (cons (symbol :tag "Shell") (choice (repeat string) (sexp :format "Evaluate: %v")))) + :version "24.4" ; bash4 additions :group 'sh-script) @@ -915,6 +945,7 @@ See `sh-feature'.") (rpm sh-append rpm2 ("%{?\\(\\sw+\\)" 1 font-lock-keyword-face)) (rpm2 sh-append shell + ("^Summary:\\(.*\\)$" (1 font-lock-doc-face t)) ("^\\(\\sw+\\):" 1 font-lock-variable-name-face))) "Default expressions to highlight in Shell Script modes. See `sh-feature'.") @@ -946,11 +977,14 @@ See `sh-feature'.") (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)))))))) + (or + (pcase (char-after) + ;; ((...)) or $((...)) or $[...] or ${...}. Nested + ;; parenthesis can occur inside the first of these forms, so + ;; parse backward recursively. + (`?\( (eq ?\( (char-before))) + ((or `?\{ `?\[) (eq ?\$ (char-before)))) + (sh--inside-noncommand-expression (1- (point)))))))) (defun sh-font-lock-open-heredoc (start string eol) "Determine the syntax of the \\n after a <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") @@ -1928,7 +2076,7 @@ Point should be before the newline." ";") (let ((semi (sh-smie--rc-newline-semi-p))) (forward-line 1) - (if semi ";" + (if (or semi (eobp)) ";" (sh-smie-rc-forward-token)))) (forward-comment (point-max)) (cond @@ -1950,14 +2098,13 @@ Point should be before the newline." (not (save-excursion (goto-char pos) - (sh-smie--keyword-p tok)))) + (sh-smie--keyword-p)))) " word ") (t tok))))))) (defun sh-smie-rc-backward-token () ;; FIXME: Code duplication with sh-smie-sh-backward-token. - (let ((bol (line-beginning-position)) - pos tok) + (let ((bol (line-beginning-position))) (forward-comment (- (point))) (cond ((and (bolp) (not (bobp)) @@ -1989,7 +2136,7 @@ Point should be before the newline." ;; ((equal tok ")") "case-)") ((and tok (string-match "\\`[a-z]" tok) (assoc tok smie-grammar) - (not (save-excursion (sh-smie--keyword-p tok)))) + (not (save-excursion (sh-smie--keyword-p)))) " word ") (t tok))))))) @@ -1997,8 +2144,9 @@ Point should be before the newline." (pcase (cons kind token) (`(:elem . basic) sh-indentation) ;; (`(:after . "case") (or sh-indentation smie-indent-basic)) - (`(:after . ";") (if (smie-rule-parent-p "case") - (smie-rule-parent sh-indentation))) + (`(:after . ";") + (if (smie-rule-parent-p "case") + (smie-rule-parent (sh-var-value 'sh-indent-after-case)))) (`(:before . "{") (save-excursion (when (sh-smie--rc-after-special-arg-p) @@ -2013,6 +2161,7 @@ Point should be before the newline." ;; with "(exp)", which is rarely the right thing to do, but is better ;; than nothing. (`(:list-intro . ,(or `"for" `"if" `"while")) t) + ;; sh-indent-after-switch: handled implicitly by the default { rule. )) ;;; End of SMIE code. @@ -2087,11 +2236,18 @@ the visited file executable, and NO-QUERY-FLAG (the second argument) controls whether to query about making the visited file executable. Calls the value of `sh-set-shell-hook' if set." - (interactive (list (completing-read (format "Shell \(default %s\): " - sh-shell-file) - interpreter-mode-alist - (lambda (x) (eq (cdr x) 'sh-mode)) - nil nil nil sh-shell-file) + (interactive (list (completing-read + (format "Shell \(default %s\): " + sh-shell-file) + ;; This used to use interpreter-mode-alist, but that is + ;; no longer appropriate now that uses regexps. + ;; Maybe there could be a separate variable that lists + ;; the shells, used here and to construct i-mode-alist. + ;; But the following is probably good enough: + (append (mapcar (lambda (e) (symbol-name (car e))) + sh-ancestor-alist) + '("csh" "rc" "sh")) + nil nil nil nil sh-shell-file) (eq executable-query 'function) t)) (if (string-match "\\.exe\\'" shell) @@ -2119,14 +2275,14 @@ Calls the value of `sh-set-shell-hook' if set." (sh-feature sh-indent-supported)) (progn (message "Setting up indent for shell type %s" sh-shell) - (if sh-use-smie - (let ((mksym (lambda (name) - (intern (format "sh-smie-%s-%s" - sh-indent-supported-here name))))) - (smie-setup (symbol-value (funcall mksym "grammar")) - (funcall mksym "rules") - :forward-token (funcall mksym "forward-token") - :backward-token (funcall mksym "backward-token"))) + (let ((mksym (lambda (name) + (intern (format "sh-smie-%s-%s" + sh-indent-supported-here name))))) + (smie-setup (symbol-value (funcall mksym "grammar")) + (funcall mksym "rules") + :forward-token (funcall mksym "forward-token") + :backward-token (funcall mksym "backward-token"))) + (unless sh-use-smie (setq-local parse-sexp-lookup-properties t) (setq-local sh-kw-alist (sh-feature sh-kw)) (let ((regexp (sh-feature sh-kws-for-done))) @@ -2146,6 +2302,7 @@ Calls the value of `sh-set-shell-hook' if set." (setq font-lock-set-defaults nil) (font-lock-set-defaults) (font-lock-fontify-buffer)) + (setq sh-shell-process nil) (run-hooks 'sh-set-shell-hook)) @@ -2378,7 +2535,6 @@ which in this buffer is currently %s. (defun sh-read-variable (var) "Read a new value for indentation variable VAR." - (interactive "*variable? ") ;; to test (let ((minibuffer-help-form `(sh-help-string-for-variable (quote ,var))) val) @@ -2968,6 +3124,7 @@ This takes into account that there may be nested open..close pairings. OPEN and CLOSE are regexps denoting the tokens to be matched. Optional parameter DEPTH (usually 1) says how many to look for." (let ((parse-sexp-ignore-comments t) + (forward-sexp-function nil) prev) (setq depth (or depth 1)) (save-excursion @@ -3024,12 +3181,9 @@ IGNORE-ERROR is non-nil." ((eq val '/) (/ (- sh-basic-offset) 2)) (t - (if ignore-error - (progn - (message "Don't know how to handle %s's value of %s" var val) - 0) - (error "Don't know how to handle %s's value of %s" var val)) - )))) + (funcall (if ignore-error #'message #'error) + "Don't know how to handle %s's value of %s" var val) + 0)))) (defun sh-set-var-value (var value &optional no-symbol) "Set variable VAR to VALUE. @@ -3154,33 +3308,35 @@ If variable `sh-blink' is non-nil then momentarily go to the line we are indenting relative to, if applicable." (interactive "P") (sh-must-support-indent) - (let* ((info (sh-get-indent-info)) - (var (sh-get-indent-var-for-line info)) - (curr-indent (current-indentation)) - val msg) - (if (stringp var) - (message "%s" (setq msg var)) - (setq val (sh-calculate-indent info)) - - (if (eq curr-indent val) - (setq msg (format "%s is %s" var (symbol-value var))) - (setq msg - (if val - (format "%s (%s) would change indent from %d to: %d" - var (symbol-value var) curr-indent val) - (format "%s (%s) would leave line as is" - var (symbol-value var))) - )) - (if (and arg var) - (describe-variable var))) - (if sh-blink - (let ((info (sh-get-indent-info))) - (if (and info (listp (car info)) - (eq (car (car info)) t)) - (sh-blink (nth 1 (car info)) msg) - (message "%s" msg))) - (message "%s" msg)) - )) + (if sh-use-smie + (smie-config-show-indent) + (let* ((info (sh-get-indent-info)) + (var (sh-get-indent-var-for-line info)) + (curr-indent (current-indentation)) + val msg) + (if (stringp var) + (message "%s" (setq msg var)) + (setq val (sh-calculate-indent info)) + + (if (eq curr-indent val) + (setq msg (format "%s is %s" var (symbol-value var))) + (setq msg + (if val + (format "%s (%s) would change indent from %d to: %d" + var (symbol-value var) curr-indent val) + (format "%s (%s) would leave line as is" + var (symbol-value var))) + )) + (if (and arg var) + (describe-variable var))) + (if sh-blink + (let ((info (sh-get-indent-info))) + (if (and info (listp (car info)) + (eq (car (car info)) t)) + (sh-blink (nth 1 (car info)) msg) + (message "%s" msg))) + (message "%s" msg)) + ))) (defun sh-set-indent () "Set the indentation for the current line. @@ -3188,34 +3344,36 @@ If the current line is controlled by an indentation variable, prompt for a new value for it." (interactive) (sh-must-support-indent) - (let* ((info (sh-get-indent-info)) - (var (sh-get-indent-var-for-line info)) - val old-val indent-val) - (if (stringp var) - (message "Cannot set indent - %s" var) - (setq old-val (symbol-value var)) - (setq val (sh-read-variable var)) - (condition-case nil - (progn - (set var val) - (setq indent-val (sh-calculate-indent info)) - (if indent-val - (message "Variable: %s Value: %s would indent to: %d" - var (symbol-value var) indent-val) - (message "Variable: %s Value: %s would leave line as is." - var (symbol-value var))) - ;; I'm not sure about this, indenting it now? - ;; No. Because it would give the impression that an undo would - ;; restore thing, but the value has been altered. - ;; (sh-indent-line) - ) - (error - (set var old-val) - (message "Bad value for %s, restoring to previous value %s" - var old-val) - (sit-for 1) - nil)) - ))) + (if sh-use-smie + (smie-config-set-indent) + (let* ((info (sh-get-indent-info)) + (var (sh-get-indent-var-for-line info)) + val old-val indent-val) + (if (stringp var) + (message "Cannot set indent - %s" var) + (setq old-val (symbol-value var)) + (setq val (sh-read-variable var)) + (condition-case nil + (progn + (set var val) + (setq indent-val (sh-calculate-indent info)) + (if indent-val + (message "Variable: %s Value: %s would indent to: %d" + var (symbol-value var) indent-val) + (message "Variable: %s Value: %s would leave line as is." + var (symbol-value var))) + ;; I'm not sure about this, indenting it now? + ;; No. Because it would give the impression that an undo would + ;; restore thing, but the value has been altered. + ;; (sh-indent-line) + ) + (error + (set var old-val) + (message "Bad value for %s, restoring to previous value %s" + var old-val) + (sit-for 1) + nil)) + )))) (defun sh-learn-line-indent (arg) @@ -3229,55 +3387,57 @@ If the value can be represented by one of the symbols then do so unless optional argument ARG (the prefix when interactive) is non-nil." (interactive "*P") (sh-must-support-indent) - ;; I'm not sure if we show allow learning on an empty line. - ;; Though it might occasionally be useful I think it usually - ;; would just be confusing. - (if (save-excursion - (beginning-of-line) - (looking-at "\\s-*$")) - (message "sh-learn-line-indent ignores empty lines.") - (let* ((info (sh-get-indent-info)) - (var (sh-get-indent-var-for-line info)) - ival sval diff new-val - (no-symbol arg) - (curr-indent (current-indentation))) - (cond - ((stringp var) - (message "Cannot learn line - %s" var)) - ((eq var 'sh-indent-comment) - ;; This is arbitrary... - ;; - if curr-indent is 0, set to curr-indent - ;; - else if it has the indentation of a "normal" line, - ;; then set to t - ;; - else set to curr-indent. - (setq sh-indent-comment - (if (= curr-indent 0) - 0 - (let* ((sh-indent-comment t) - (val2 (sh-calculate-indent info))) - (if (= val2 curr-indent) - t - curr-indent)))) - (message "%s set to %s" var (symbol-value var)) - ) - ((numberp (setq sval (sh-var-value var))) - (setq ival (sh-calculate-indent info)) - (setq diff (- curr-indent ival)) - - (sh-debug "curr-indent: %d ival: %d diff: %d var:%s sval %s" - curr-indent ival diff var sval) - (setq new-val (+ sval diff)) -;;; I commented out this because someone might want to replace -;;; a value of `+' with the current value of sh-basic-offset -;;; or vice-versa. -;;; (if (= 0 diff) -;;; (message "No change needed!") - (sh-set-var-value var new-val no-symbol) - (message "%s set to %s" var (symbol-value var)) - ) - (t - (debug) - (message "Cannot change %s" var)))))) + (if sh-use-smie + (smie-config-set-indent) + ;; I'm not sure if we show allow learning on an empty line. + ;; Though it might occasionally be useful I think it usually + ;; would just be confusing. + (if (save-excursion + (beginning-of-line) + (looking-at "\\s-*$")) + (message "sh-learn-line-indent ignores empty lines.") + (let* ((info (sh-get-indent-info)) + (var (sh-get-indent-var-for-line info)) + ival sval diff new-val + (no-symbol arg) + (curr-indent (current-indentation))) + (cond + ((stringp var) + (message "Cannot learn line - %s" var)) + ((eq var 'sh-indent-comment) + ;; This is arbitrary... + ;; - if curr-indent is 0, set to curr-indent + ;; - else if it has the indentation of a "normal" line, + ;; then set to t + ;; - else set to curr-indent. + (setq sh-indent-comment + (if (= curr-indent 0) + 0 + (let* ((sh-indent-comment t) + (val2 (sh-calculate-indent info))) + (if (= val2 curr-indent) + t + curr-indent)))) + (message "%s set to %s" var (symbol-value var)) + ) + ((numberp (setq sval (sh-var-value var))) + (setq ival (sh-calculate-indent info)) + (setq diff (- curr-indent ival)) + + (sh-debug "curr-indent: %d ival: %d diff: %d var:%s sval %s" + curr-indent ival diff var sval) + (setq new-val (+ sval diff)) + ;; I commented out this because someone might want to replace + ;; a value of `+' with the current value of sh-basic-offset + ;; or vice-versa. + ;;(if (= 0 diff) + ;; (message "No change needed!") + (sh-set-var-value var new-val no-symbol) + (message "%s set to %s" var (symbol-value var)) + ) + (t + (debug) + (message "Cannot change %s" var))))))) @@ -3309,26 +3469,23 @@ so that `occur-next' and `occur-prev' will work." ) (goto-char (point-max)) (setq start (point)) - (insert line) - (if occur-point - (setq occur-point (point))) - (insert message) - (if point - (add-text-properties - start (point) - '(mouse-face highlight - help-echo "mouse-2: go to the line where I learned this"))) - (insert "\n") - (if point - (progn - (put-text-property start (point) 'occur-target m1) - (if occur-point - (put-text-property start occur-point - 'occur-match t)) - )) - ))) - - + (let ((inhibit-read-only t)) + (insert line) + (if occur-point + (setq occur-point (point))) + (insert message) + (if point + (add-text-properties + start (point) + '(mouse-face highlight + help-echo "mouse-2: go to the line where I learned this"))) + (insert "\n") + (when point + (put-text-property start (point) 'occur-target m1) + (if occur-point + (put-text-property start occur-point + 'occur-match t)) + ))))) ;; Is this really worth having? (defvar sh-learned-buffer-hook nil @@ -3352,7 +3509,7 @@ so that `occur-next' and `occur-prev' will work." ;; Originally this was sh-learn-region-indent (beg end) ;; However, in practice this was awkward so I changed it to -;; use the whole buffer. Use narrowing if needbe. +;; use the whole buffer. Use narrowing if need be. (defun sh-learn-buffer-indent (&optional arg) "Learn how to indent the buffer the way it currently is. @@ -3378,202 +3535,204 @@ removed in the future. This command can often take a long time to run." (interactive "P") (sh-must-support-indent) - (save-excursion - (goto-char (point-min)) - (let ((learned-var-list nil) - (out-buffer "*indent*") - (num-diffs 0) - previous-set-info - (max 17) - vec - msg - (comment-col nil) ;; number if all same, t if seen diff values - (comments-always-default t) ;; nil if we see one not default - initial-msg - (specified-basic-offset (and arg (numberp arg) - (> arg 0))) - (linenum 0) - suggested) - (setq vec (make-vector max 0)) - (sh-mark-init out-buffer) - - (if specified-basic-offset - (progn - (setq sh-basic-offset arg) - (setq initial-msg - (format "Using specified sh-basic-offset of %d" - sh-basic-offset))) - (setq initial-msg - (format "Initial value of sh-basic-offset: %s" - sh-basic-offset))) - - (while (< (point) (point-max)) - (setq linenum (1+ linenum)) - ;; (if (zerop (% linenum 10)) - (message "line %d" linenum) - ;; ) - (unless (looking-at "\\s-*$") ;; ignore empty lines! - (let* ((sh-indent-comment t) ;; info must return default indent - (info (sh-get-indent-info)) - (var (sh-get-indent-var-for-line info)) - sval ival diff new-val - (curr-indent (current-indentation))) - (cond - ((null var) - nil) - ((stringp var) - nil) - ((numberp (setq sval (sh-var-value var 'no-error))) - ;; the numberp excludes comments since sval will be t. - (setq ival (sh-calculate-indent)) - (setq diff (- curr-indent ival)) - (setq new-val (+ sval diff)) - (sh-set-var-value var new-val 'no-symbol) - (unless (looking-at "\\s-*#") ;; don't learn from comments - (if (setq previous-set-info (assoc var learned-var-list)) - (progn - ;; it was already there, is it same value ? - (unless (eq (symbol-value var) - (nth 1 previous-set-info)) - (sh-mark-line - (format "Variable %s was set to %s" - var (symbol-value var)) - (point) out-buffer t t) - (sh-mark-line - (format " but was previously set to %s" - (nth 1 previous-set-info)) - (nth 2 previous-set-info) out-buffer t) - (setq num-diffs (1+ num-diffs)) - ;; (delete previous-set-info learned-var-list) - (setcdr previous-set-info - (list (symbol-value var) (point))) - ) - ) - (setq learned-var-list - (append (list (list var (symbol-value var) - (point))) - learned-var-list))) - (if (numberp new-val) - (progn - (sh-debug - "This line's indent value: %d" new-val) - (if (< new-val 0) - (setq new-val (- new-val))) - (if (< new-val max) - (aset vec new-val (1+ (aref vec new-val)))))) - )) - ((eq var 'sh-indent-comment) - (unless (= curr-indent (sh-calculate-indent info)) - ;; this is not the default indentation - (setq comments-always-default nil) - (if comment-col ;; then we have see one before - (or (eq comment-col curr-indent) - (setq comment-col t)) ;; seen a different one - (setq comment-col curr-indent)) - )) - (t - (sh-debug "Cannot learn this line!!!") - )) - (sh-debug - "at %s learned-var-list is %s" (point) learned-var-list) - )) - (forward-line 1) - ) ;; while - (if sh-debug - (progn - (setq msg (format - "comment-col = %s comments-always-default = %s" - comment-col comments-always-default)) - ;; (message msg) - (sh-mark-line msg nil out-buffer))) - (cond - ((eq comment-col 0) - (setq msg "\nComments are all in 1st column.\n")) - (comments-always-default - (setq msg "\nComments follow default indentation.\n") - (setq comment-col t)) - ((numberp comment-col) - (setq msg (format "\nComments are in col %d." comment-col))) - (t - (setq msg "\nComments seem to be mixed, leaving them as is.\n") - (setq comment-col nil) - )) - (sh-debug msg) - (sh-mark-line msg nil out-buffer) - - (sh-mark-line initial-msg nil out-buffer t t) - - (setq suggested (sh-guess-basic-offset vec)) - - (if (and suggested (not specified-basic-offset)) - (let ((new-value - (cond - ;; t => set it if we have a single value as a number - ((and (eq sh-learn-basic-offset t) (numberp suggested)) - suggested) - ;; other non-nil => set it if only one value was found - (sh-learn-basic-offset - (if (numberp suggested) - suggested - (if (= (length suggested) 1) - (car suggested)))) - (t - nil)))) - (if new-value - (progn - (setq learned-var-list - (append (list (list 'sh-basic-offset - (setq sh-basic-offset new-value) - (point-max))) - learned-var-list)) - ;; Not sure if we need to put this line in, since - ;; it will appear in the "Learned variable settings". - (sh-mark-line - (format "Changed sh-basic-offset to: %d" sh-basic-offset) - nil out-buffer)) - (sh-mark-line - (if (listp suggested) - (format "Possible value(s) for sh-basic-offset: %s" - (mapconcat 'int-to-string suggested " ")) - (format "Suggested sh-basic-offset: %d" suggested)) - nil out-buffer)))) - - - (setq learned-var-list - (append (list (list 'sh-indent-comment comment-col (point-max))) - learned-var-list)) - (setq sh-indent-comment comment-col) - (let ((name (buffer-name))) - (sh-mark-line "\nLearned variable settings:" nil out-buffer) - (if arg - ;; Set learned variables to symbolic rather than numeric - ;; values where possible. - (dolist (learned-var (reverse learned-var-list)) - (let ((var (car learned-var)) - (val (nth 1 learned-var))) - (when (and (not (eq var 'sh-basic-offset)) - (numberp val)) - (sh-set-var-value var val))))) - (dolist (learned-var (reverse learned-var-list)) - (let ((var (car learned-var))) - (sh-mark-line (format " %s %s" var (symbol-value var)) - (nth 2 learned-var) out-buffer))) - (with-current-buffer out-buffer - (goto-char (point-min)) - (insert - (format "Indentation values for buffer %s.\n" name) - (format "%d indentation variable%s different values%s\n\n" - num-diffs - (if (= num-diffs 1) - " has" "s have") - (if (zerop num-diffs) - "." ":")) - ))) - ;; Are abnormal hooks considered bad form? - (run-hook-with-args 'sh-learned-buffer-hook learned-var-list) - (and (called-interactively-p 'any) - (or sh-popup-occur-buffer (> num-diffs 0)) - (pop-to-buffer out-buffer))))) + (if sh-use-smie + (smie-config-guess) + (save-excursion + (goto-char (point-min)) + (let ((learned-var-list nil) + (out-buffer "*indent*") + (num-diffs 0) + previous-set-info + (max 17) + vec + msg + (comment-col nil) ;; number if all same, t if seen diff values + (comments-always-default t) ;; nil if we see one not default + initial-msg + (specified-basic-offset (and arg (numberp arg) + (> arg 0))) + (linenum 0) + suggested) + (setq vec (make-vector max 0)) + (sh-mark-init out-buffer) + + (if specified-basic-offset + (progn + (setq sh-basic-offset arg) + (setq initial-msg + (format "Using specified sh-basic-offset of %d" + sh-basic-offset))) + (setq initial-msg + (format "Initial value of sh-basic-offset: %s" + sh-basic-offset))) + + (while (< (point) (point-max)) + (setq linenum (1+ linenum)) + ;; (if (zerop (% linenum 10)) + (message "line %d" linenum) + ;; ) + (unless (looking-at "\\s-*$") ;; ignore empty lines! + (let* ((sh-indent-comment t) ;; info must return default indent + (info (sh-get-indent-info)) + (var (sh-get-indent-var-for-line info)) + sval ival diff new-val + (curr-indent (current-indentation))) + (cond + ((null var) + nil) + ((stringp var) + nil) + ((numberp (setq sval (sh-var-value var 'no-error))) + ;; the numberp excludes comments since sval will be t. + (setq ival (sh-calculate-indent)) + (setq diff (- curr-indent ival)) + (setq new-val (+ sval diff)) + (sh-set-var-value var new-val 'no-symbol) + (unless (looking-at "\\s-*#") ;; don't learn from comments + (if (setq previous-set-info (assoc var learned-var-list)) + (progn + ;; it was already there, is it same value ? + (unless (eq (symbol-value var) + (nth 1 previous-set-info)) + (sh-mark-line + (format "Variable %s was set to %s" + var (symbol-value var)) + (point) out-buffer t t) + (sh-mark-line + (format " but was previously set to %s" + (nth 1 previous-set-info)) + (nth 2 previous-set-info) out-buffer t) + (setq num-diffs (1+ num-diffs)) + ;; (delete previous-set-info learned-var-list) + (setcdr previous-set-info + (list (symbol-value var) (point))) + ) + ) + (setq learned-var-list + (append (list (list var (symbol-value var) + (point))) + learned-var-list))) + (if (numberp new-val) + (progn + (sh-debug + "This line's indent value: %d" new-val) + (if (< new-val 0) + (setq new-val (- new-val))) + (if (< new-val max) + (aset vec new-val (1+ (aref vec new-val)))))) + )) + ((eq var 'sh-indent-comment) + (unless (= curr-indent (sh-calculate-indent info)) + ;; this is not the default indentation + (setq comments-always-default nil) + (if comment-col ;; then we have see one before + (or (eq comment-col curr-indent) + (setq comment-col t)) ;; seen a different one + (setq comment-col curr-indent)) + )) + (t + (sh-debug "Cannot learn this line!!!") + )) + (sh-debug + "at %s learned-var-list is %s" (point) learned-var-list) + )) + (forward-line 1) + ) ;; while + (if sh-debug + (progn + (setq msg (format + "comment-col = %s comments-always-default = %s" + comment-col comments-always-default)) + ;; (message msg) + (sh-mark-line msg nil out-buffer))) + (cond + ((eq comment-col 0) + (setq msg "\nComments are all in 1st column.\n")) + (comments-always-default + (setq msg "\nComments follow default indentation.\n") + (setq comment-col t)) + ((numberp comment-col) + (setq msg (format "\nComments are in col %d." comment-col))) + (t + (setq msg "\nComments seem to be mixed, leaving them as is.\n") + (setq comment-col nil) + )) + (sh-debug msg) + (sh-mark-line msg nil out-buffer) + + (sh-mark-line initial-msg nil out-buffer t t) + + (setq suggested (sh-guess-basic-offset vec)) + + (if (and suggested (not specified-basic-offset)) + (let ((new-value + (cond + ;; t => set it if we have a single value as a number + ((and (eq sh-learn-basic-offset t) (numberp suggested)) + suggested) + ;; other non-nil => set it if only one value was found + (sh-learn-basic-offset + (if (numberp suggested) + suggested + (if (= (length suggested) 1) + (car suggested)))) + (t + nil)))) + (if new-value + (progn + (setq learned-var-list + (append (list (list 'sh-basic-offset + (setq sh-basic-offset new-value) + (point-max))) + learned-var-list)) + ;; Not sure if we need to put this line in, since + ;; it will appear in the "Learned variable settings". + (sh-mark-line + (format "Changed sh-basic-offset to: %d" sh-basic-offset) + nil out-buffer)) + (sh-mark-line + (if (listp suggested) + (format "Possible value(s) for sh-basic-offset: %s" + (mapconcat 'int-to-string suggested " ")) + (format "Suggested sh-basic-offset: %d" suggested)) + nil out-buffer)))) + + + (setq learned-var-list + (append (list (list 'sh-indent-comment comment-col (point-max))) + learned-var-list)) + (setq sh-indent-comment comment-col) + (let ((name (buffer-name))) + (sh-mark-line "\nLearned variable settings:" nil out-buffer) + (if arg + ;; Set learned variables to symbolic rather than numeric + ;; values where possible. + (dolist (learned-var (reverse learned-var-list)) + (let ((var (car learned-var)) + (val (nth 1 learned-var))) + (when (and (not (eq var 'sh-basic-offset)) + (numberp val)) + (sh-set-var-value var val))))) + (dolist (learned-var (reverse learned-var-list)) + (let ((var (car learned-var))) + (sh-mark-line (format " %s %s" var (symbol-value var)) + (nth 2 learned-var) out-buffer))) + (with-current-buffer out-buffer + (goto-char (point-min)) + (let ((inhibit-read-only t)) + (insert + (format "Indentation values for buffer %s.\n" name) + (format "%d indentation variable%s different values%s\n\n" + num-diffs + (if (= num-diffs 1) + " has" "s have") + (if (zerop num-diffs) + "." ":")))))) + ;; Are abnormal hooks considered bad form? + (run-hook-with-args 'sh-learned-buffer-hook learned-var-list) + (and (called-interactively-p 'any) + (or sh-popup-occur-buffer (> num-diffs 0)) + (pop-to-buffer out-buffer)))))) (defun sh-guess-basic-offset (vec) "See if we can determine a reasonable value for `sh-basic-offset'. @@ -3589,11 +3748,11 @@ Return values: (i 1) (totals (make-vector max 0))) (while (< i max) - (aset totals i (+ (aref totals i) (* 4 (aref vec i)))) + (cl-incf (aref totals i) (* 4 (aref vec i))) (if (zerop (% i 2)) - (aset totals i (+ (aref totals i) (aref vec (/ i 2))))) + (cl-incf (aref totals i) (aref vec (/ i 2)))) (if (< (* i 2) max) - (aset totals i (+ (aref totals i) (aref vec (* i 2))))) + (cl-incf (aref totals i) (aref vec (* i 2)))) (setq i (1+ i))) (let ((x nil) @@ -3602,10 +3761,10 @@ Return values: (setq i 1) (while (< i max) (if (/= (aref totals i) 0) - (setq x (append x (list (cons i (aref totals i)))))) + (push (cons i (aref totals i)) x)) (setq i (1+ i))) - (setq x (sort x (lambda (a b) (> (cdr a) (cdr b))))) + (setq x (sort (nreverse x) (lambda (a b) (> (cdr a) (cdr b))))) (setq tot (apply '+ (append totals nil))) (sh-debug (format "vec: %s\ntotals: %s\ntot: %d" vec totals tot)) @@ -4111,7 +4270,8 @@ The document is bounded by `sh-here-document-word'." (or (not (looking-back "[^<]<<")) (save-excursion (backward-char 2) - (sh-quoted-p)) + (or (sh-quoted-p) + (sh--inside-noncommand-expression (point)))) (nth 8 (syntax-ppss)) (let ((tabs (if (string-match "\\`-" sh-here-document-word) (make-string (/ (current-indentation) tab-width) ?\t)