diff options
Diffstat (limited to 'lisp/progmodes/sh-script.el')
-rw-r--r-- | lisp/progmodes/sh-script.el | 176 |
1 files changed, 104 insertions, 72 deletions
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index 966357c0970..be9f325d93d 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el @@ -286,7 +286,7 @@ naming the shell." :group 'sh-script) (defcustom sh-imenu-generic-expression - '((sh + `((sh . ((nil ;; function FOO ;; function FOO() @@ -295,8 +295,21 @@ naming the shell." ;; FOO() (nil "^\\s-*\\([[:alpha:]_][[:alnum:]_]*\\)\\s-*()" - 1) - ))) + 1))) + (mksh + . ((nil + ;; function FOO + ;; function FOO() + ,(rx bol (* (syntax whitespace)) "function" (+ (syntax whitespace)) + (group (1+ (not (any "\0\t\n \"$&'();<=>\\`|#*?[]/")))) + (* (syntax whitespace)) (? "()")) + 1) + (nil + ;; FOO() + ,(rx bol (* (syntax whitespace)) + (group (1+ (not (any "\0\t\n \"$&'();<=>\\`|#*?[]/")))) + (* (syntax whitespace)) "()") + 1)))) "Alist of regular expressions for recognizing shell function definitions. See `sh-feature' and `imenu-generic-expression'." :type '(alist :key-type (symbol :tag "Shell") @@ -306,7 +319,7 @@ See `sh-feature' and `imenu-generic-expression'." :value-type (repeat :tag "Regexp, index..." sexp))) :group 'sh-script - :version "20.4") + :version "29.1") (defun sh-current-defun-name () "Find the name of function or variable at point. @@ -402,45 +415,42 @@ This is buffer-local in every such buffer.") (rpm . (,sh-mode-syntax-table ?\' "."))) "Syntax-table used in Shell-Script mode. See `sh-feature'.") -(defvar sh-mode-map - (let ((map (make-sparse-keymap))) - (define-key map "\C-c(" 'sh-function) - (define-key map "\C-c\C-w" 'sh-while) - (define-key map "\C-c\C-u" 'sh-until) - (define-key map "\C-c\C-t" 'sh-tmp-file) - (define-key map "\C-c\C-s" 'sh-select) - (define-key map "\C-c\C-r" 'sh-repeat) - (define-key map "\C-c\C-o" 'sh-while-getopts) - (define-key map "\C-c\C-l" 'sh-indexed-loop) - (define-key map "\C-c\C-i" 'sh-if) - (define-key map "\C-c\C-f" 'sh-for) - (define-key map "\C-c\C-c" 'sh-case) - (define-key map "\C-c?" #'smie-config-show-indent) - (define-key map "\C-c=" #'smie-config-set-indent) - (define-key map "\C-c<" #'smie-config-set-indent) - (define-key map "\C-c>" #'smie-config-guess) - (define-key map "\C-c\C-\\" 'sh-backslash-region) - - (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) - (define-key map "\C-c:" 'sh-set-shell) - (define-key map [remap backward-sentence] 'sh-beginning-of-command) - (define-key map [remap forward-sentence] 'sh-end-of-command) - map) - "Keymap used in Shell-Script mode.") +(defvar-keymap sh-mode-map + :doc "Keymap used in Shell-Script mode." + "C-c (" #'sh-function + "C-c C-w" #'sh-while + "C-c C-u" #'sh-until + "C-c C-t" #'sh-tmp-file + "C-c C-s" #'sh-select + "C-c C-r" #'sh-repeat + "C-c C-o" #'sh-while-getopts + "C-c C-l" #'sh-indexed-loop + "C-c C-i" #'sh-if + "C-c C-f" #'sh-for + "C-c C-c" #'sh-case + "C-c ?" #'smie-config-show-indent + "C-c =" #'smie-config-set-indent + "C-c <" #'smie-config-set-indent + "C-c >" #'smie-config-guess + "C-c C-\\" #'sh-backslash-region + + "C-c +" #'sh-add + "C-M-x" #'sh-execute-region + "C-c C-x" #'executable-interpret + "C-c C-n" #'sh-send-line-or-region-and-step + "C-c C-d" #'sh-cd-here + "C-c C-z" #'sh-show-shell + "C-c :" #'sh-set-shell + + "<remap> <delete-backward-char>" #'backward-delete-char-untabify + "<remap> <backward-sentence>" #'sh-beginning-of-command + "<remap> <forward-sentence>" #'sh-end-of-command) (easy-menu-define sh-mode-menu sh-mode-map "Menu for Shell-Script mode." '("Sh-Script" ["Backslash region" sh-backslash-region - :help "Insert, align, or delete end-of-line backslashes on the lines in the region."] + :help "Insert, align, or delete end-of-line backslashes on the lines in the region"] ["Set shell type..." sh-set-shell :help "Set this buffer's shell to SHELL (a string)"] ["Execute script..." executable-interpret @@ -458,7 +468,7 @@ This is buffer-local in every such buffer.") ["Select Statement" sh-select :help "Insert a select statement "] ["Indexed Loop" sh-indexed-loop - :help "Insert an indexed loop from 1 to n."] + :help "Insert an indexed loop from 1 to n"] ["Options Loop" sh-while-getopts :help "Insert a while getopts loop."] ["While Loop" sh-while @@ -482,7 +492,7 @@ This is buffer-local in every such buffer.") ["Show indentation" smie-config-show-indent :help "Show the how the current line would be indented"] ["Learn buffer indentation" smie-config-guess - :help "Learn how to indent the buffer the way it currently is."])) + :help "Learn how to indent the buffer the way it currently is"])) (defvar sh-skeleton-pair-default-alist '((?\( _ ?\)) (?\)) (?\[ ?\s _ ?\s ?\]) (?\]) @@ -628,7 +638,8 @@ removed when closing the here document." (wksh sh-append ksh88) (zsh sh-append ksh88 - "autoload" "bindkey" "builtin" "chdir" "compctl" "declare" "dirs" + "autoload" "always" + "bindkey" "builtin" "chdir" "compctl" "declare" "dirs" "disable" "disown" "echotc" "enable" "functions" "getln" "hash" "history" "integer" "limit" "local" "log" "popd" "pushd" "r" "readonly" "rehash" "sched" "setopt" "source" "suspend" "true" @@ -643,7 +654,12 @@ implemented as aliases. See `sh-feature'." :version "24.4" ; bash4 additions :group 'sh-script) - +(defcustom sh-indent-statement-after-and t + "How to indent statements following && in Shell-Script mode. +If t, indent to align with &&. +If nil, indent to align with the previous line's indentation." + :type 'boolean + :version "29.1") (defcustom sh-leading-keywords '((bash sh-append sh @@ -866,7 +882,7 @@ See `sh-feature'.") "\\(?:\\(?:.*[^\\\n]\\)?\\(?:\\\\\\\\\\)*\\\\\n\\)*.*") (defconst sh-here-doc-open-re - (concat "[^<]<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\|[-/~._]\\)+\\)" + (concat "[^<]<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\|[-/~._@]\\)+\\)" sh-escaped-line-re "\\(\n\\)"))) (defun sh--inside-noncommand-expression (pos) @@ -1140,8 +1156,8 @@ Can be set to a number, or to nil which means leave it as is." "The default indentation increment. This value is used for the `+' and `-' symbols in an indentation variable." :type 'integer + :safe #'integerp :group 'sh-indentation) -(put 'sh-basic-offset 'safe-local-variable 'integerp) (defcustom sh-indent-comment t "How a comment line is to be indented. @@ -1409,7 +1425,7 @@ If FORCE is non-nil and no process found, create one." (defun sh-show-shell () "Pop the shell interaction buffer." (interactive) - (pop-to-buffer (process-buffer (sh-shell-process t)))) + (pop-to-buffer (process-buffer (sh-shell-process t)) display-comint-buffer-action)) (defun sh-send-text (text) "Send TEXT to `sh-shell-process'." @@ -1540,6 +1556,11 @@ with your script for an edit-interpret-debug cycle." (add-hook 'completion-at-point-functions #'sh-completion-at-point-function nil t) (setq-local outline-regexp "###") + (setq-local escaped-string-quote + (lambda (terminator) + (if (eq terminator ?') + "'\\'" + "\\"))) ;; Parse or insert magic number for exec, and set all variables depending ;; on the shell thus determined. (sh-set-shell @@ -1551,7 +1572,7 @@ with your script for an edit-interpret-debug cycle." ;; Checks that use `buffer-file-name' follow. ((string-match "\\.m?spec\\'" buffer-file-name) "rpm") ((string-match "[.]sh\\>" buffer-file-name) "sh") - ((string-match "[.]bash\\>" buffer-file-name) "bash") + ((string-match "[.]bash\\(rc\\)?\\>" buffer-file-name) "bash") ((string-match "[.]ksh\\>" buffer-file-name) "ksh") ((string-match "[.]mkshrc\\>" buffer-file-name) "mksh") ((string-match "[.]t?csh\\(rc\\)?\\>" buffer-file-name) "csh") @@ -1604,7 +1625,7 @@ This adds rules for comments and assignments." ;;; Completion -(defvar sh--completion-keywords '("if" "while" "until" "for")) +(defvar sh--completion-keywords '("if" "while" "until" "for" "then")) (defun sh--vars-before-point () (save-excursion @@ -1776,21 +1797,27 @@ Does not preserve point." (n (skip-syntax-backward "."))) (if (or (zerop n) (and (eq n -1) + ;; Skip past quoted white space. (let ((p (point))) (if (eq -1 (% (skip-syntax-backward "\\") 2)) t (goto-char p) nil)))) (while - (progn (skip-syntax-backward ".w_'") - (or (not (zerop (skip-syntax-backward "\\"))) - (when (eq ?\\ (char-before (1- (point)))) - (let ((p (point))) - (forward-char -1) - (if (eq -1 (% (skip-syntax-backward "\\") 2)) - t - (goto-char p) - nil)))))) + (progn + ;; Skip past words, but stop at semicolons. + (while (and (not (zerop (skip-syntax-backward "w_'"))) + (not (eq (char-before (point)) ?\;)) + (skip-syntax-backward "."))) + (or (not (zerop (skip-syntax-backward "\\"))) + ;; Skip past quoted white space. + (when (eq ?\\ (char-before (1- (point)))) + (let ((p (point))) + (forward-char -1) + (if (eq -1 (% (skip-syntax-backward "\\") 2)) + t + (goto-char p) + nil)))))) (goto-char (- (point) (% (skip-syntax-backward "\\") 2)))) (buffer-substring-no-properties (point) pos))) @@ -1899,9 +1926,9 @@ With t, you get the latter as long as that would indent the continuation line deeper than the initial line." :version "25.1" :type '(choice - (const nil :tag "Never") - (const t :tag "Only if needed to make it deeper") - (const always :tag "Always")) + (const :value nil :tag "Never") + (const :value t :tag "Only if needed to make it deeper") + (const :value always :tag "Always")) :group 'sh-indentation) (defun sh-smie--continuation-start-indent () @@ -1975,7 +2002,7 @@ May return nil if the line should not be treated as continued." (cons 'column (smie-indent-keyword ";")) (smie-rule-separator kind))) (`(:after . ,(or ";;" ";&" ";;&")) - (with-demoted-errors + (with-demoted-errors "SMIE rule error: %S" (smie-backward-sexp token) (cons 'column (if (or (smie-rule-bolp) @@ -1986,7 +2013,9 @@ May return nil if the line should not be treated as continued." (current-column) (smie-indent-calculate))))) (`(:before . ,(or "|" "&&" "||")) - (unless (smie-rule-parent-p token) + (when (and (not (smie-rule-parent-p token)) + (or (not (equal token "&&")) + sh-indent-statement-after-and)) (smie-backward-sexp token) `(column . ,(+ (funcall smie-rules-function :elem 'basic) (smie-indent-virtual))))) @@ -2381,6 +2410,8 @@ Lines containing only comments are considered empty." The working directory is that of the buffer, and only environment variables are already set which is why you can mark a header within the script. +The executed subshell is `sh-shell-file'. + With a positive prefix ARG, instead of sending region, define header from beginning of buffer to point. With a negative prefix ARG, instead of sending region, clear header." @@ -2388,17 +2419,18 @@ region, clear header." (if flag (setq sh-header-marker (if (> (prefix-numeric-value flag) 0) (point-marker))) - (if sh-header-marker - (save-excursion - (let (buffer-undo-list) - (goto-char sh-header-marker) - (append-to-buffer (current-buffer) start end) - (shell-command-on-region (point-min) - (setq end (+ sh-header-marker - (- end start))) - sh-shell-file) - (delete-region sh-header-marker end))) - (shell-command-on-region start end (concat sh-shell-file " -"))))) + (let ((shell-file-name sh-shell-file)) + (if sh-header-marker + (save-excursion + (let (buffer-undo-list) + (goto-char sh-header-marker) + (append-to-buffer (current-buffer) start end) + (shell-command-on-region (point-min) + (setq end (+ sh-header-marker + (- end start))) + sh-shell-file) + (delete-region sh-header-marker end))) + (shell-command-on-region start end (concat sh-shell-file " -")))))) (defun sh-remember-variable (var) |