;;; sh-script.el --- shell-script editing commands for Emacs
-;; Copyright (C) 1993, 94, 95, 96, 97, 1999, 2001, 2003
+;; Copyright (C) 1993, 1994, 1995, 1996, 1997, 1999, 2001, 2003, 2004, 2005
;; Free Software Foundation, Inc.
;; Author: Daniel Pfeiffer <occitan@esperanto.org>
:type '(repeat (cons (symbol :tag "Shell")
(choice (const :tag "No Arguments" nil)
(string :tag "Arguments")
- (cons :format "Evaluate: %v"
- (const :format "" eval)
- sexp))))
+ (sexp :format "Evaluate: %v"))))
:group 'sh-script)
(defcustom sh-imenu-generic-expression
(defvar sh-shell (sh-canonicalize-shell (file-name-nondirectory sh-shell-file))
"The shell being programmed. This is set by \\[sh-set-shell].")
+(defvar sh-mode-abbrev-table nil)
+
+(define-abbrev-table 'sh-mode-abbrev-table ())
+
+
;; I turned off this feature because it doesn't permit typing commands
;; in the usual way without help.
;;(defvar sh-abbrevs
-;; '((csh eval sh-abbrevs shell
+;; '((csh sh-abbrevs shell
;; "switch" 'sh-case
;; "getopts" 'sh-while-getopts)
-;; (es eval sh-abbrevs shell
+;; (es sh-abbrevs shell
;; "function" 'sh-function)
-;; (ksh88 eval sh-abbrevs sh
+;; (ksh88 sh-abbrevs sh
;; "select" 'sh-select)
-;; (rc eval sh-abbrevs shell
+;; (rc sh-abbrevs shell
;; "case" 'sh-case
;; "function" 'sh-function)
-;; (sh eval sh-abbrevs shell
+;; (sh sh-abbrevs shell
;; "case" 'sh-case
;; "function" 'sh-function
;; "until" 'sh-until
;; "tmpfile" sh-tmp-file
;; "while" sh-while)
-;; (zsh eval sh-abbrevs ksh88
+;; (zsh sh-abbrevs ksh88
;; "repeat" 'sh-repeat))
;; "Abbrev-table used in Shell-Script mode. See `sh-feature'.
;;;Due to the internal workings of abbrev tables, the shell name symbol is
(modify-syntax-entry (pop list) (pop list) table))
table)
+(defvar sh-mode-syntax-table nil
+ "The syntax table to use for Shell-Script mode.
+This is buffer-local in every such buffer.")
+
(defvar sh-mode-default-syntax-table
(sh-mode-syntax-table ()
?\# "<"
?\" "\"\""
?\' "\"'"
?\` "\"`"
+ ;; ?$ might also have a ". p" syntax. Both "'" and ". p" seem
+ ;; to work fine. This is needed so that dabbrev-expand
+ ;; $VARNAME works.
+ ?$ "'"
?! "_"
?% "_"
?: "_"
?^ "_"
?~ "_"
?, "_"
+ ?= "."
?< "."
?> ".")
"Default syntax table for shell mode.")
(define-key map "\C-c=" 'sh-set-indent)
(define-key map "\C-c<" 'sh-learn-line-indent)
(define-key map "\C-c>" 'sh-learn-buffer-indent)
+ (define-key map "\C-c\C-\\" 'sh-backslash-region)
(define-key map "=" 'sh-assignment)
(define-key map "\C-c+" 'sh-add)
(defcustom sh-require-final-newline
'((csh . t)
- (pdksh . t)
- (rc . require-final-newline)
- (sh . require-final-newline))
+ (pdksh . t))
"*Value of `require-final-newline' in Shell-Script mode buffers.
+\(SHELL . t) means use the value of `mode-require-final-newline' for SHELL.
See `sh-feature'."
:type '(repeat (cons (symbol :tag "Shell")
(choice (const :tag "require" t)
- (cons :format "Evaluate: %v"
- (const :format "" eval)
- sexp))))
+ (sexp :format "Evaluate: %v"))))
:group 'sh-script)
sign. See `sh-feature'."
:type '(repeat (cons (symbol :tag "Shell")
(choice regexp
- (cons :format "Evaluate: %v"
- (const :format "" eval)
- sexp))))
+ (sexp :format "Evaluate: %v"))))
:group 'sh-script)
-(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" "source"
- "suspend" "typeset" "unalias")
+ "jobs" "kill" "let" "local" "popd" "printf" "pushd" "shopt"
+ "source" "suspend" "typeset" "unalias")
;; The next entry is only used for defining the others
(bourne sh-append shell
implemented as aliases. See `sh-feature'."
:type '(repeat (cons (symbol :tag "Shell")
(choice (repeat string)
- (cons :format "Evaluate: %v"
- (const :format "" eval)
- sexp))))
+ (sexp :format "Evaluate: %v"))))
:group 'sh-script)
(rc "else")
- (sh "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
+ (sh "!" "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
"*List of keywords that may be immediately followed by a builtin or keyword.
Given some confusion between keywords and builtins depending on shell and
system, the distinction here has been based on whether they influence the
flow of control or syntax. See `sh-feature'."
:type '(repeat (cons (symbol :tag "Shell")
(choice (repeat string)
- (cons :format "Evaluate: %v"
- (const :format "" eval)
- sexp))))
+ (sexp :format "Evaluate: %v"))))
:group 'sh-script)
See `sh-feature'."
:type '(repeat (cons (symbol :tag "Shell")
(choice (repeat string)
- (cons :format "Evaluate: %v"
- (const :format "" eval)
- sexp))))
+ (sexp :format "Evaluate: %v"))))
:group 'sh-script)
\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
- '("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
- font-lock-variable-name-face))
+ ("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
+ font-lock-variable-name-face))
(es sh-append executable-font-lock-keywords
- '("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
- font-lock-variable-name-face))
+ ("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
+ font-lock-variable-name-face))
(rc sh-append es)
(sh sh-append shell
;; Variable names.
- '("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
+ ("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
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)))
+ ("^\\(\\sw+\\)[ \t]*(" 1 font-lock-function-name-face)
+ ("\\<\\(function\\)\\>[ \t]*\\(\\sw+\\)?"
+ (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)
- '("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
- '("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
+ ("\\(^\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\\\)$" 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))
(rpm sh-append rpm2
- '("%{?\\(\\sw+\\)" 1 font-lock-keyword-face))
+ ("%{?\\(\\sw+\\)" 1 font-lock-keyword-face))
(rpm2 sh-append shell
- '("^\\(\\sw+\\):" 1 font-lock-variable-name-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
(defconst sh-st-symbol (string-to-syntax "_"))
(defconst sh-here-doc-syntax (string-to-syntax "|")) ;; generic string
-(defconst sh-here-doc-open-re "<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\|\\s_\\)+\\).*\\(\n\\)")
+(defconst sh-here-doc-open-re "<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\)+\\).*\\(\n\\)")
(defvar sh-here-doc-markers nil)
(make-variable-buffer-local 'sh-here-doc-markers)
;; Skip through one pattern
(while
(or (/= 0 (skip-syntax-backward "w_"))
- (/= 0 (skip-chars-backward "?[]*/\\"))
+ (/= 0 (skip-chars-backward "?[]*@/\\"))
(and (sh-is-quoted-p (1- (point)))
(goto-char (- (point) 2)))
(when (memq (char-before) '(?\" ?\'))
: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 '+
- "*How much to indent a then relative to an if."
+(defcustom sh-indent-for-then 0
+ "*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)
+(defcustom sh-backslash-column 48
+ "*Column in which `sh-backslash-region' inserts backslashes."
+ :type 'integer
+ :group 'sh)
+
+(defcustom sh-backslash-align t
+ "*If non-nil, `sh-backslash-region' will align backslashes."
+ :type 'boolean
+ :group 'sh)
+
;; Internal use - not designed to be changed by the user:
(defun sh-mkword-regexpr (word)
(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)
(executable-set-magic shell (sh-feature sh-shell-arg)
no-query-flag insert-flag)))
(let ((tem (sh-feature sh-require-final-newline)))
- (unless (eq tem 'require-final-newline)
- (setq require-final-newline tem)))
+ (if (eq tem t)
+ (setq require-final-newline mode-require-final-newline)))
(setq
comment-start-skip "#+[\t ]*"
-;;; local-abbrev-table (sh-feature sh-abbrevs)
+ local-abbrev-table sh-mode-abbrev-table
mode-line-process (format "[%s]" sh-shell)
sh-shell-variables nil
sh-shell-variables-initialized nil
(unless elt
(setq elt (assq 'sh alist)))
(if (and (consp (setq val (cdr elt)))
- (eq (car val) 'sh-append))
+ (memq (car val) '(sh-append sh-modify)))
(setcdr elt
(setq val
- (apply 'sh-append
+ (apply (car val)
(let ((sh-shell (car (cdr val))))
- (sh-feature alist))
+ (if (assq sh-shell alist)
+ (sh-feature alist)
+ (eval sh-shell)))
(cddr val)))))
(if function
(nconc alist
(progn
(setq result (append result val))
(setq align-point (point))))
- (forward-char -1)
+ (or (bobp)
+ (forward-char -1))
(skip-chars-forward "[a-z0-9]*?")
)
((string-match "[])}]" x)
(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
(define-skeleton sh-for
"Insert a for loop. See `sh-feature'."
- (csh eval sh-modify sh
+ (csh sh-modify sh
1 ""
2 "foreach "
4 " ( "
6 " )"
15 '<
16 "end")
- (es eval sh-modify rc
+ (es sh-modify rc
4 " = ")
- (rc eval sh-modify sh
+ (rc sh-modify sh
2 "for( "
6 " ) {"
15 ?\} )
(define-skeleton sh-indexed-loop
"Insert an indexed loop from 1 to n. See `sh-feature'."
- (bash eval identity posix)
+ (bash sh-modify posix)
(csh "Index variable: "
"@ " str " = 1" \n
"while( $" str " <= " (read-string "upper limit: ") " )" \n
> _ ?$ str \n
"@ " str "++" \n
< "end" \n)
- (es eval sh-modify rc
+ (es sh-modify rc
4 " =")
(ksh88 "Index variable: "
> "integer " str "=0" \n
(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 . "}")
(define-skeleton sh-function
"Insert a function definition. See `sh-feature'."
- (bash eval sh-modify ksh88
+ (bash sh-modify ksh88
3 "() {")
(ksh88 "name: "
"function " str " {" \n
> _ \n
< "}" \n)
- (rc eval sh-modify ksh88
+ (rc sh-modify ksh88
1 "fn ")
(sh ()
"() {" \n
> "select " str " in " _ "; do" \n
> ?$ str \n
"done" > \n)
- (bash eval sh-append ksh88))
+ (bash sh-append ksh88))
;;;(put 'sh-select 'menu-enable '(sh-feature sh-select))
(define-skeleton sh-tmp-file
"Insert code to setup temporary file handling. See `sh-feature'."
- (bash eval identity ksh88)
+ (bash sh-append ksh88)
(csh (file-name-nondirectory (buffer-file-name))
"set tmp = /tmp/" str ".$$" \n
"onintr exit" \n _
_ \n
?\} > \n
?\} > \n)
- (ksh88 eval sh-modify sh
+ (ksh88 sh-modify sh
7 "EXIT")
(rc (file-name-nondirectory (buffer-file-name))
> "tmp = /tmp/" str ".$pid" \n
(define-skeleton sh-while
"Insert a while loop. See `sh-feature'."
- (csh eval sh-modify sh
+ (csh sh-modify sh
2 ""
3 "while( "
5 " )"
10 '<
11 "end")
- (es eval sh-modify sh
+ (es sh-modify sh
3 "while { "
5 " } {"
10 ?\} )
- (rc eval sh-modify sh
+ (rc sh-modify sh
3 "while( "
5 " ) {"
10 ?\} )
"Insert a while getopts loop. See `sh-feature'.
Prompts for an options string which consists of letters for each recognized
option followed by a colon `:' if the option accepts an argument."
- (bash eval sh-modify sh
+ (bash sh-modify sh
18 "${0##*/}")
(csh nil
"while( 1 )" \n
< < "endsw" \n
"shift" \n
< "end" \n)
- (ksh88 eval sh-modify sh
+ (ksh88 sh-modify sh
16 "print"
18 "${0##*/}"
37 "OPTIND-1")
- (posix eval sh-modify sh
+ (posix sh-modify sh
18 "$(basename $0)")
(sh "optstring: "
> "while getopts :" str " OPT; do" \n
"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
(if (re-search-forward sh-end-of-command nil t)
(goto-char (match-end 1))))
+;; Backslashification. Stolen from make-mode.el.
+
+(defun sh-backslash-region (from to delete-flag)
+ "Insert, align, or delete end-of-line backslashes on the lines in the region.
+With no argument, inserts backslashes and aligns existing backslashes.
+With an argument, deletes the backslashes.
+
+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
+shell command and conveniently use this command."
+ (interactive "r\nP")
+ (save-excursion
+ (goto-char from)
+ (let ((column sh-backslash-column)
+ (endmark (make-marker)))
+ (move-marker endmark to)
+ ;; Compute the smallest column number past the ends of all the lines.
+ (if sh-backslash-align
+ (progn
+ (if (not delete-flag)
+ (while (< (point) to)
+ (end-of-line)
+ (if (= (preceding-char) ?\\)
+ (progn (forward-char -1)
+ (skip-chars-backward " \t")))
+ (setq column (max column (1+ (current-column))))
+ (forward-line 1)))
+ ;; Adjust upward to a tab column, if that doesn't push
+ ;; past the margin.
+ (if (> (% column tab-width) 0)
+ (let ((adjusted (* (/ (+ column tab-width -1) tab-width)
+ tab-width)))
+ (if (< adjusted (window-width))
+ (setq column adjusted))))))
+ ;; Don't modify blank lines at start of region.
+ (goto-char from)
+ (while (and (< (point) endmark) (eolp))
+ (forward-line 1))
+ ;; Add or remove backslashes on all the lines.
+ (while (and (< (point) endmark)
+ ;; Don't backslashify the last line
+ ;; if the region ends right at the start of the next line.
+ (save-excursion
+ (forward-line 1)
+ (< (point) endmark)))
+ (if (not delete-flag)
+ (sh-append-backslash column)
+ (sh-delete-backslash))
+ (forward-line 1))
+ (move-marker endmark nil))))
+
+(defun sh-append-backslash (column)
+ (end-of-line)
+ ;; Note that "\\\\" is needed to get one backslash.
+ (if (= (preceding-char) ?\\)
+ (progn (forward-char -1)
+ (delete-horizontal-space)
+ (indent-to column (if sh-backslash-align nil 1)))
+ (indent-to column (if sh-backslash-align nil 1))
+ (insert "\\")))
+
+(defun sh-delete-backslash ()
+ (end-of-line)
+ (or (bolp)
+ (progn
+ (forward-char -1)
+ (if (looking-at "\\\\")
+ (delete-region (1+ (point))
+ (progn (skip-chars-backward " \t") (point)))))))
+
(provide 'sh-script)
-;;; arch-tag: eccd8b72-f337-4fc2-ae86-18155a69d937
+;; arch-tag: eccd8b72-f337-4fc2-ae86-18155a69d937
;;; sh-script.el ends here