]> code.delx.au - gnu-emacs/blob - lisp/progmodes/sh-script.el
Romain Francoise's and Ami Fischman's bugfixes.
[gnu-emacs] / lisp / progmodes / sh-script.el
1 ;;; sh-script.el --- shell-script editing commands for Emacs
2
3 ;; Copyright (C) 1993, 94, 95, 96, 97, 1999, 2001, 2003
4 ;; Free Software Foundation, Inc.
5
6 ;; Author: Daniel Pfeiffer <occitan@esperanto.org>
7 ;; Version: 2.0f
8 ;; Maintainer: FSF
9 ;; Keywords: languages, unix
10
11 ;; This file is part of GNU Emacs.
12
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; any later version.
17
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Commentary:
29
30 ;; Major mode for editing shell scripts. Bourne, C and rc shells as well
31 ;; as various derivatives are supported and easily derived from. Structured
32 ;; statements can be inserted with one command or abbrev. Completion is
33 ;; available for filenames, variables known from the script, the shell and
34 ;; the environment as well as commands.
35
36 ;;; Known Bugs:
37
38 ;; - In Bourne the keyword `in' is not anchored to case, for, select ...
39 ;; - Variables in `"' strings aren't fontified because there's no way of
40 ;; syntactically distinguishing those from `'' strings.
41
42 ;; Indentation
43 ;; ===========
44 ;; Indentation for rc and es modes is very limited, but for Bourne shells
45 ;; and its derivatives it is quite customizable.
46 ;;
47 ;; The following description applies to sh and derived shells (bash,
48 ;; zsh, ...).
49 ;;
50 ;; There are various customization variables which allow tailoring to
51 ;; a wide variety of styles. Most of these variables are named
52 ;; sh-indent-for-XXX and sh-indent-after-XXX. For example.
53 ;; sh-indent-after-if controls the indenting of a line following
54 ;; an if statement, and sh-indent-for-fi controls the indentation
55 ;; of the line containing the fi.
56 ;;
57 ;; You can set each to a numeric value, but it is often more convenient
58 ;; to a symbol such as `+' which uses the value of variable `sh-basic-offset'.
59 ;; By changing this one variable you can increase or decrease how much
60 ;; indentation there is. Valid symbols:
61 ;;
62 ;; + Indent right by sh-basic-offset
63 ;; - Indent left by sh-basic-offset
64 ;; ++ Indent right twice sh-basic-offset
65 ;; -- Indent left twice sh-basic-offset
66 ;; * Indent right half sh-basic-offset
67 ;; / Indent left half sh-basic-offset.
68 ;;
69 ;; There are 4 commands to help set the indentation variables:
70 ;;
71 ;; `sh-show-indent'
72 ;; This shows what variable controls the indentation of the current
73 ;; line and its value.
74 ;;
75 ;; `sh-set-indent'
76 ;; This allows you to set the value of the variable controlling the
77 ;; current line's indentation. You can enter a number or one of a
78 ;; number of special symbols to denote the value of sh-basic-offset,
79 ;; or its negative, or half it, or twice it, etc. If you've used
80 ;; cc-mode this should be familiar. If you forget which symbols are
81 ;; valid simply press C-h at the prompt.
82 ;;
83 ;; `sh-learn-line-indent'
84 ;; Simply make the line look the way you want it, then invoke this
85 ;; command. It will set the variable to the value that makes the line
86 ;; indent like that. If called with a prefix argument then it will set
87 ;; the value to one of the symbols if applicable.
88 ;;
89 ;; `sh-learn-buffer-indent'
90 ;; This is the deluxe function! It "learns" the whole buffer (use
91 ;; narrowing if you want it to process only part). It outputs to a
92 ;; buffer *indent* any conflicts it finds, and all the variables it has
93 ;; learned. This buffer is a sort of Occur mode buffer, allowing you to
94 ;; easily find where something was set. It is popped to automatically
95 ;; if there are any conflicts found or if `sh-popup-occur-buffer' is
96 ;; non-nil.
97 ;; `sh-indent-comment' will be set if all comments follow the same
98 ;; pattern; if they don't it will be set to nil.
99 ;; Whether `sh-basic-offset' is set is determined by variable
100 ;; `sh-learn-basic-offset'.
101 ;;
102 ;; Unfortunately, `sh-learn-buffer-indent' can take a long time to run
103 ;; (e.g. if there are large case statements). Perhaps it does not make
104 ;; sense to run it on large buffers: if lots of lines have different
105 ;; indentation styles it will produce a lot of diagnostics in the
106 ;; *indent* buffer; if there is a consistent style then running
107 ;; `sh-learn-buffer-indent' on a small region of the buffer should
108 ;; suffice.
109 ;;
110 ;; Saving indentation values
111 ;; -------------------------
112 ;; After you've learned the values in a buffer, how to you remember
113 ;; them? Originally I had hoped that `sh-learn-buffer-indent'
114 ;; would make this unnecessary; simply learn the values when you visit
115 ;; the buffer.
116 ;; You can do this automatically like this:
117 ;; (add-hook 'sh-set-shell-hook 'sh-learn-buffer-indent)
118 ;;
119 ;; However... `sh-learn-buffer-indent' is extremely slow,
120 ;; especially on large-ish buffer. Also, if there are conflicts the
121 ;; "last one wins" which may not produce the desired setting.
122 ;;
123 ;; So...There is a minimal way of being able to save indentation values and
124 ;; to reload them in another buffer or at another point in time.
125 ;;
126 ;; Use `sh-name-style' to give a name to the indentation settings of
127 ;; the current buffer.
128 ;; Use `sh-load-style' to load indentation settings for the current
129 ;; buffer from a specific style.
130 ;; Use `sh-save-styles-to-buffer' to write all the styles to a buffer
131 ;; in lisp code. You can then store it in a file and later use
132 ;; `load-file' to load it.
133 ;;
134 ;; Indentation variables - buffer local or global?
135 ;; ----------------------------------------------
136 ;; I think that often having them buffer-local makes sense,
137 ;; especially if one is using `sh-learn-buffer-indent'. However, if
138 ;; a user sets values using customization, these changes won't appear
139 ;; to work if the variables are already local!
140 ;;
141 ;; To get round this, there is a variable `sh-make-vars-local' and 2
142 ;; functions: `sh-make-vars-local' and `sh-reset-indent-vars-to-global-values'.
143 ;;
144 ;; If `sh-make-vars-local' is non-nil, then these variables become
145 ;; buffer local when the mode is established.
146 ;; If this is nil, then the variables are global. At any time you
147 ;; can make them local with the command `sh-make-vars-local'.
148 ;; Conversely, to update with the global values you can use the
149 ;; command `sh-reset-indent-vars-to-global-values'.
150 ;;
151 ;; This may be awkward, but the intent is to cover all cases.
152 ;;
153 ;; Awkward things, pitfalls
154 ;; ------------------------
155 ;; Indentation for a sh script is complicated for a number of reasons:
156 ;;
157 ;; 1. You can't format by simply looking at symbols, you need to look
158 ;; at keywords. [This is not the case for rc and es shells.]
159 ;; 2. The character ")" is used both as a matched pair "(" ... ")" and
160 ;; as a stand-alone symbol (in a case alternative). This makes
161 ;; things quite tricky!
162 ;; 3. Here-documents in a script should be treated "as is", and when
163 ;; they terminate we want to revert to the indentation of the line
164 ;; containing the "<<" symbol.
165 ;; 4. A line may be continued using the "\".
166 ;; 5. The character "#" (outside a string) normally starts a comment,
167 ;; but it doesn't in the sequence "$#"!
168 ;;
169 ;; To try and address points 2 3 and 5 I used a feature that cperl mode
170 ;; uses, that of a text's syntax property. This, however, has 2
171 ;; disadvantages:
172 ;; 1. We need to scan the buffer to find which ")" symbols belong to a
173 ;; case alternative, to find any here documents, and handle "$#".
174 ;; 2. Setting the text property makes the buffer modified. If the
175 ;; buffer is read-only buffer we have to cheat and bypass the read-only
176 ;; status. This is for cases where the buffer started read-only buffer
177 ;; but the user issued `toggle-read-only'.
178 ;;
179 ;; Bugs
180 ;; ----
181 ;; - Indenting many lines is slow. It currently does each line
182 ;; independently, rather than saving state information.
183 ;;
184 ;; - `sh-learn-buffer-indent' is extremely slow.
185 ;;
186 ;; Richard Sharman <rsharman@pobox.com> June 1999.
187
188 ;;; Code:
189
190 ;; page 1: variables and settings
191 ;; page 2: indentation stuff
192 ;; page 3: mode-command and utility functions
193 ;; page 4: statement syntax-commands for various shells
194 ;; page 5: various other commands
195
196 (eval-when-compile
197 (require 'skeleton)
198 (require 'cl)
199 (require 'comint))
200 (require 'executable)
201
202
203
204 (defgroup sh nil
205 "Shell programming utilities"
206 :group 'unix
207 :group 'languages)
208
209 (defgroup sh-script nil
210 "Shell script mode"
211 :group 'sh
212 :prefix "sh-")
213
214
215 (defcustom sh-ancestor-alist
216 '((ash . sh)
217 (bash . jsh)
218 (bash2 . jsh)
219 (dtksh . ksh)
220 (es . rc)
221 (itcsh . tcsh)
222 (jcsh . csh)
223 (jsh . sh)
224 (ksh . ksh88)
225 (ksh88 . jsh)
226 (oash . sh)
227 (pdksh . ksh88)
228 (posix . sh)
229 (tcsh . csh)
230 (wksh . ksh88)
231 (wsh . sh)
232 (zsh . ksh88)
233 (rpm . sh))
234 "*Alist showing the direct ancestor of various shells.
235 This is the basis for `sh-feature'. See also `sh-alias-alist'.
236 By default we have the following three hierarchies:
237
238 csh C Shell
239 jcsh C Shell with Job Control
240 tcsh Turbo C Shell
241 itcsh ? Turbo C Shell
242 rc Plan 9 Shell
243 es Extensible Shell
244 sh Bourne Shell
245 ash ? Shell
246 jsh Bourne Shell with Job Control
247 bash GNU Bourne Again Shell
248 ksh88 Korn Shell '88
249 ksh Korn Shell '93
250 dtksh CDE Desktop Korn Shell
251 pdksh Public Domain Korn Shell
252 wksh Window Korn Shell
253 zsh Z Shell
254 oash SCO OA (curses) Shell
255 posix IEEE 1003.2 Shell Standard
256 wsh ? Shell"
257 :type '(repeat (cons symbol symbol))
258 :group 'sh-script)
259
260
261 (defcustom sh-alias-alist
262 (append (if (eq system-type 'gnu/linux)
263 '((csh . tcsh)
264 (ksh . pdksh)))
265 ;; for the time being
266 '((ksh . ksh88)
267 (bash2 . bash)
268 (sh5 . sh)))
269 "*Alist for transforming shell names to what they really are.
270 Use this where the name of the executable doesn't correspond to the type of
271 shell it really is."
272 :type '(repeat (cons symbol symbol))
273 :group 'sh-script)
274
275
276 (defcustom sh-shell-file
277 (or
278 ;; On MSDOS and Windows, collapse $SHELL to lower-case and remove
279 ;; the executable extension, so comparisons with the list of
280 ;; known shells work.
281 (and (memq system-type '(ms-dos windows-nt))
282 (let* ((shell (getenv "SHELL"))
283 (shell-base
284 (and shell (file-name-nondirectory shell))))
285 ;; shell-script mode doesn't support DOS/Windows shells,
286 ;; so use the default instead.
287 (if (or (null shell)
288 (member (downcase shell-base)
289 '("command.com" "cmd.exe" "4dos.com" "ndos.com"
290 "cmdproxy.exe")))
291 "/bin/sh"
292 (file-name-sans-extension (downcase shell)))))
293 (getenv "SHELL")
294 "/bin/sh")
295 "*The executable file name for the shell being programmed."
296 :type 'string
297 :group 'sh-script)
298
299
300 (defcustom sh-shell-arg
301 ;; bash does not need any options when run in a shell script,
302 '((bash)
303 (csh . "-f")
304 (pdksh)
305 ;; Bill_Mann@praxisint.com says -p with ksh can do harm.
306 (ksh88)
307 ;; -p means don't initialize functions from the environment.
308 (rc . "-p")
309 ;; Someone proposed -motif, but we don't want to encourage
310 ;; use of a non-free widget set.
311 (wksh)
312 ;; -f means don't run .zshrc.
313 (zsh . "-f"))
314 "*Single argument string for the magic number. See `sh-feature'."
315 :type '(repeat (cons (symbol :tag "Shell")
316 (choice (const :tag "No Arguments" nil)
317 (string :tag "Arguments")
318 (cons :format "Evaluate: %v"
319 (const :format "" eval)
320 sexp))))
321 :group 'sh-script)
322
323 (defcustom sh-imenu-generic-expression
324 `((sh
325 . ((nil "^\\s-*\\(function\\s-+\\)?\\([A-Za-z_][A-Za-z_0-9]+\\)\\s-*()" 2))))
326 "*Alist of regular expressions for recognizing shell function definitions.
327 See `sh-feature' and `imenu-generic-expression'."
328 :type '(alist :key-type (symbol :tag "Shell")
329 :value-type (alist :key-type (choice :tag "Title"
330 string
331 (const :tag "None" nil))
332 :value-type
333 (repeat :tag "Regexp, index..." sexp)))
334 :group 'sh-script
335 :version "20.4")
336
337 (defvar sh-shell-variables nil
338 "Alist of shell variable names that should be included in completion.
339 These are used for completion in addition to all the variables named
340 in `process-environment'. Each element looks like (VAR . VAR), where
341 the car and cdr are the same symbol.")
342
343 (defvar sh-shell-variables-initialized nil
344 "Non-nil if `sh-shell-variables' is initialized.")
345
346 (defun sh-canonicalize-shell (shell)
347 "Convert a shell name SHELL to the one we should handle it as."
348 (if (string-match "\\.exe\\'" shell)
349 (setq shell (substring shell 0 (match-beginning 0))))
350 (or (symbolp shell)
351 (setq shell (intern shell)))
352 (or (cdr (assq shell sh-alias-alist))
353 shell))
354
355 (defvar sh-shell (sh-canonicalize-shell (file-name-nondirectory sh-shell-file))
356 "The shell being programmed. This is set by \\[sh-set-shell].")
357
358 ;; I turned off this feature because it doesn't permit typing commands
359 ;; in the usual way without help.
360 ;;(defvar sh-abbrevs
361 ;; '((csh sh-abbrevs shell
362 ;; "switch" 'sh-case
363 ;; "getopts" 'sh-while-getopts)
364
365 ;; (es sh-abbrevs shell
366 ;; "function" 'sh-function)
367
368 ;; (ksh88 sh-abbrevs sh
369 ;; "select" 'sh-select)
370
371 ;; (rc sh-abbrevs shell
372 ;; "case" 'sh-case
373 ;; "function" 'sh-function)
374
375 ;; (sh sh-abbrevs shell
376 ;; "case" 'sh-case
377 ;; "function" 'sh-function
378 ;; "until" 'sh-until
379 ;; "getopts" 'sh-while-getopts)
380
381 ;; ;; The next entry is only used for defining the others
382 ;; (shell "for" sh-for
383 ;; "loop" sh-indexed-loop
384 ;; "if" sh-if
385 ;; "tmpfile" sh-tmp-file
386 ;; "while" sh-while)
387
388 ;; (zsh sh-abbrevs ksh88
389 ;; "repeat" 'sh-repeat))
390 ;; "Abbrev-table used in Shell-Script mode. See `sh-feature'.
391 ;;;Due to the internal workings of abbrev tables, the shell name symbol is
392 ;;;actually defined as the table for the like of \\[edit-abbrevs].")
393
394
395
396 (defun sh-mode-syntax-table (table &rest list)
397 "Copy TABLE and set syntax for successive CHARs according to strings S."
398 (setq table (copy-syntax-table table))
399 (while list
400 (modify-syntax-entry (pop list) (pop list) table))
401 table)
402
403 (defvar sh-mode-syntax-table nil
404 "The syntax table to use for Shell-Script mode.
405 This is buffer-local in every such buffer.")
406
407 (defvar sh-mode-default-syntax-table
408 (sh-mode-syntax-table ()
409 ?\# "<"
410 ?\n ">#"
411 ?\" "\"\""
412 ?\' "\"'"
413 ?\` "\"`"
414 ?! "_"
415 ?% "_"
416 ?: "_"
417 ?. "_"
418 ?^ "_"
419 ?~ "_"
420 ?, "_"
421 ?< "."
422 ?> ".")
423 "Default syntax table for shell mode.")
424
425 (defvar sh-mode-syntax-table-input
426 '((sh . nil))
427 "Syntax-table used in Shell-Script mode. See `sh-feature'.")
428
429 (defvar sh-mode-map
430 (let ((map (make-sparse-keymap))
431 (menu-map (make-sparse-keymap "Insert")))
432 (define-key map "\C-c(" 'sh-function)
433 (define-key map "\C-c\C-w" 'sh-while)
434 (define-key map "\C-c\C-u" 'sh-until)
435 (define-key map "\C-c\C-t" 'sh-tmp-file)
436 (define-key map "\C-c\C-s" 'sh-select)
437 (define-key map "\C-c\C-r" 'sh-repeat)
438 (define-key map "\C-c\C-o" 'sh-while-getopts)
439 (define-key map "\C-c\C-l" 'sh-indexed-loop)
440 (define-key map "\C-c\C-i" 'sh-if)
441 (define-key map "\C-c\C-f" 'sh-for)
442 (define-key map "\C-c\C-c" 'sh-case)
443 (define-key map "\C-c?" 'sh-show-indent)
444 (define-key map "\C-c=" 'sh-set-indent)
445 (define-key map "\C-c<" 'sh-learn-line-indent)
446 (define-key map "\C-c>" 'sh-learn-buffer-indent)
447
448 (define-key map "=" 'sh-assignment)
449 (define-key map "\C-c+" 'sh-add)
450 (define-key map "\C-\M-x" 'sh-execute-region)
451 (define-key map "\C-c\C-x" 'executable-interpret)
452 (define-key map "<" 'sh-maybe-here-document)
453 (define-key map "(" 'skeleton-pair-insert-maybe)
454 (define-key map "{" 'skeleton-pair-insert-maybe)
455 (define-key map "[" 'skeleton-pair-insert-maybe)
456 (define-key map "'" 'skeleton-pair-insert-maybe)
457 (define-key map "`" 'skeleton-pair-insert-maybe)
458 (define-key map "\"" 'skeleton-pair-insert-maybe)
459
460 (define-key map [remap complete-tag] 'comint-dynamic-complete)
461 (define-key map [remap newline-and-indent] 'sh-newline-and-indent)
462 (define-key map [remap delete-backward-char]
463 'backward-delete-char-untabify)
464 (define-key map "\C-c:" 'sh-set-shell)
465 (define-key map [remap backward-sentence] 'sh-beginning-of-command)
466 (define-key map [remap forward-sentence] 'sh-end-of-command)
467 (define-key map [menu-bar insert] (cons "Insert" menu-map))
468 (define-key menu-map [sh-while] '("While Loop" . sh-while))
469 (define-key menu-map [sh-until] '("Until Loop" . sh-until))
470 (define-key menu-map [sh-tmp-file] '("Temporary File" . sh-tmp-file))
471 (define-key menu-map [sh-select] '("Select Statement" . sh-select))
472 (define-key menu-map [sh-repeat] '("Repeat Loop" . sh-repeat))
473 (define-key menu-map [sh-getopts] '("Options Loop" . sh-while-getopts))
474 (define-key menu-map [sh-indexed-loop] '("Indexed Loop" . sh-indexed-loop))
475 (define-key menu-map [sh-if] '("If Statement" . sh-if))
476 (define-key menu-map [sh-for] '("For Loop" . sh-for))
477 (define-key menu-map [sh-case] '("Case Statement" . sh-case))
478 map)
479 "Keymap used in Shell-Script mode.")
480
481
482
483 (defcustom sh-dynamic-complete-functions
484 '(shell-dynamic-complete-environment-variable
485 shell-dynamic-complete-command
486 comint-dynamic-complete-filename)
487 "*Functions for doing TAB dynamic completion."
488 :type '(repeat function)
489 :group 'sh-script)
490
491
492 (defcustom sh-require-final-newline
493 '((csh . t)
494 (pdksh . t)
495 (rc . require-final-newline)
496 (sh . require-final-newline))
497 "*Value of `require-final-newline' in Shell-Script mode buffers.
498 See `sh-feature'."
499 :type '(repeat (cons (symbol :tag "Shell")
500 (choice (const :tag "require" t)
501 (cons :format "Evaluate: %v"
502 (const :format "" eval)
503 sexp))))
504 :group 'sh-script)
505
506
507 (defcustom sh-assignment-regexp
508 '((csh . "\\<\\([a-zA-Z0-9_]+\\)\\(\\[.+\\]\\)?[ \t]*[-+*/%^]?=")
509 ;; actually spaces are only supported in let/(( ... ))
510 (ksh88 . "\\<\\([a-zA-Z0-9_]+\\)\\(\\[.+\\]\\)?[ \t]*\\([-+*/%&|~^]\\|<<\\|>>\\)?=")
511 (rc . "\\<\\([a-zA-Z0-9_*]+\\)[ \t]*=")
512 (sh . "\\<\\([a-zA-Z0-9_]+\\)="))
513 "*Regexp for the variable name and what may follow in an assignment.
514 First grouping matches the variable name. This is upto and including the `='
515 sign. See `sh-feature'."
516 :type '(repeat (cons (symbol :tag "Shell")
517 (choice regexp
518 (cons :format "Evaluate: %v"
519 (const :format "" eval)
520 sexp))))
521 :group 'sh-script)
522
523
524 (defcustom sh-indentation 4
525 "The width for further indentation in Shell-Script mode."
526 :type 'integer
527 :group 'sh-script)
528
529
530 (defcustom sh-remember-variable-min 3
531 "*Don't remember variables less than this length for completing reads."
532 :type 'integer
533 :group 'sh-script)
534
535
536 (defvar sh-header-marker nil
537 "When non-nil is the end of header for prepending by \\[sh-execute-region].
538 That command is also used for setting this variable.")
539
540
541 (defcustom sh-beginning-of-command
542 "\\([;({`|&]\\|\\`\\|[^\\]\n\\)[ \t]*\\([/~a-zA-Z0-9:]\\)"
543 "*Regexp to determine the beginning of a shell command.
544 The actual command starts at the beginning of the second \\(grouping\\)."
545 :type 'regexp
546 :group 'sh-script)
547
548
549 (defcustom sh-end-of-command
550 "\\([/~a-zA-Z0-9:]\\)[ \t]*\\([;#)}`|&]\\|$\\)"
551 "*Regexp to determine the end of a shell command.
552 The actual command ends at the end of the first \\(grouping\\)."
553 :type 'regexp
554 :group 'sh-script)
555
556
557
558 (defvar sh-here-document-word "EOF"
559 "Word to delimit here documents.
560 If the first character of this string is \"-\", this character will
561 be removed from the string when it is used to close the here document.
562 This convention is used by the Bash shell, for example, to indicate
563 that leading tabs inside the here document should be ignored.
564 Note that Emacs currently has no support for indenting inside here
565 documents - you must insert literal tabs by hand.")
566
567 (defvar sh-test
568 '((sh "[ ]" . 3)
569 (ksh88 "[[ ]]" . 4))
570 "Initial input in Bourne if, while and until skeletons. See `sh-feature'.")
571
572
573 ;; customized this out of sheer bravado. not for the faint of heart.
574 ;; but it *did* have an asterisk in the docstring!
575 (defcustom sh-builtins
576 '((bash sh-append posix
577 "." "alias" "bg" "bind" "builtin" "compgen" "complete"
578 "declare" "dirs" "disown" "enable" "fc" "fg" "help" "history"
579 "jobs" "kill" "let" "local" "popd" "printf" "pushd" "shopt"
580 "source" "suspend" "typeset" "unalias")
581
582 ;; The next entry is only used for defining the others
583 (bourne sh-append shell
584 "eval" "export" "getopts" "newgrp" "pwd" "read" "readonly"
585 "times" "ulimit")
586
587 (csh sh-append shell
588 "alias" "chdir" "glob" "history" "limit" "nice" "nohup" "rehash"
589 "setenv" "source" "time" "unalias" "unhash")
590
591 (dtksh sh-append wksh)
592
593 (es "access" "apids" "cd" "echo" "eval" "false" "let" "limit" "local"
594 "newpgrp" "result" "time" "umask" "var" "vars" "wait" "whatis")
595
596 (jsh sh-append sh
597 "bg" "fg" "jobs" "kill" "stop" "suspend")
598
599 (jcsh sh-append csh
600 "bg" "fg" "jobs" "kill" "notify" "stop" "suspend")
601
602 (ksh88 sh-append bourne
603 "alias" "bg" "false" "fc" "fg" "jobs" "kill" "let" "print" "time"
604 "typeset" "unalias" "whence")
605
606 (oash sh-append sh
607 "checkwin" "dateline" "error" "form" "menu" "newwin" "oadeinit"
608 "oaed" "oahelp" "oainit" "pp" "ppfile" "scan" "scrollok" "wattr"
609 "wclear" "werase" "win" "wmclose" "wmmessage" "wmopen" "wmove"
610 "wmtitle" "wrefresh")
611
612 (pdksh sh-append ksh88
613 "bind")
614
615 (posix sh-append sh
616 "command")
617
618 (rc "builtin" "cd" "echo" "eval" "limit" "newpgrp" "shift" "umask" "wait"
619 "whatis")
620
621 (sh sh-append bourne
622 "hash" "test" "type")
623
624 ;; The next entry is only used for defining the others
625 (shell "cd" "echo" "eval" "set" "shift" "umask" "unset" "wait")
626
627 (wksh sh-append ksh88
628 "Xt[A-Z][A-Za-z]*")
629
630 (zsh sh-append ksh88
631 "autoload" "bindkey" "builtin" "chdir" "compctl" "declare" "dirs"
632 "disable" "disown" "echotc" "enable" "functions" "getln" "hash"
633 "history" "integer" "limit" "local" "log" "popd" "pushd" "r"
634 "readonly" "rehash" "sched" "setopt" "source" "suspend" "true"
635 "ttyctl" "type" "unfunction" "unhash" "unlimit" "unsetopt" "vared"
636 "which"))
637 "*List of all shell builtins for completing read and fontification.
638 Note that on some systems not all builtins are available or some are
639 implemented as aliases. See `sh-feature'."
640 :type '(repeat (cons (symbol :tag "Shell")
641 (choice (repeat string)
642 (cons :format "Evaluate: %v"
643 (const :format "" eval)
644 sexp))))
645 :group 'sh-script)
646
647
648
649 (defcustom sh-leading-keywords
650 '((bash sh-append sh
651 "time")
652
653 (csh "else")
654
655 (es "true" "unwind-protect" "whatis")
656
657 (rc "else")
658
659 (sh "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
660 "*List of keywords that may be immediately followed by a builtin or keyword.
661 Given some confusion between keywords and builtins depending on shell and
662 system, the distinction here has been based on whether they influence the
663 flow of control or syntax. See `sh-feature'."
664 :type '(repeat (cons (symbol :tag "Shell")
665 (choice (repeat string)
666 (cons :format "Evaluate: %v"
667 (const :format "" eval)
668 sexp))))
669 :group 'sh-script)
670
671
672 (defcustom sh-other-keywords
673 '((bash sh-append bourne
674 "bye" "logout" "select")
675
676 ;; The next entry is only used for defining the others
677 (bourne sh-append sh
678 "function")
679
680 (csh sh-append shell
681 "breaksw" "default" "end" "endif" "endsw" "foreach" "goto"
682 "if" "logout" "onintr" "repeat" "switch" "then" "while")
683
684 (es "break" "catch" "exec" "exit" "fn" "for" "forever" "fork" "if"
685 "return" "throw" "while")
686
687 (ksh88 sh-append bourne
688 "select")
689
690 (rc "break" "case" "exec" "exit" "fn" "for" "if" "in" "return" "switch"
691 "while")
692
693 (sh sh-append shell
694 "done" "esac" "fi" "for" "in" "return")
695
696 ;; The next entry is only used for defining the others
697 (shell "break" "case" "continue" "exec" "exit")
698
699 (zsh sh-append bash
700 "select"))
701 "*List of keywords not in `sh-leading-keywords'.
702 See `sh-feature'."
703 :type '(repeat (cons (symbol :tag "Shell")
704 (choice (repeat string)
705 (cons :format "Evaluate: %v"
706 (const :format "" eval)
707 sexp))))
708 :group 'sh-script)
709
710
711
712 (defvar sh-variables
713 '((bash sh-append sh
714 "allow_null_glob_expansion" "auto_resume" "BASH" "BASH_ENV"
715 "BASH_VERSINFO" "BASH_VERSION" "cdable_vars" "COMP_CWORD"
716 "COMP_LINE" "COMP_POINT" "COMP_WORDS" "COMPREPLY" "DIRSTACK"
717 "ENV" "EUID" "FCEDIT" "FIGNORE" "FUNCNAME"
718 "glob_dot_filenames" "GLOBIGNORE" "GROUPS" "histchars"
719 "HISTCMD" "HISTCONTROL" "HISTFILE" "HISTFILESIZE"
720 "HISTIGNORE" "history_control" "HISTSIZE"
721 "hostname_completion_file" "HOSTFILE" "HOSTTYPE" "IGNOREEOF"
722 "ignoreeof" "INPUTRC" "LINENO" "MACHTYPE" "MAIL_WARNING"
723 "noclobber" "nolinks" "notify" "no_exit_on_failed_exec"
724 "NO_PROMPT_VARS" "OLDPWD" "OPTERR" "OSTYPE" "PIPESTATUS"
725 "PPID" "POSIXLY_CORRECT" "PROMPT_COMMAND" "PS3" "PS4"
726 "pushd_silent" "PWD" "RANDOM" "REPLY" "SECONDS" "SHELLOPTS"
727 "SHLVL" "TIMEFORMAT" "TMOUT" "UID")
728
729 (csh sh-append shell
730 "argv" "cdpath" "child" "echo" "histchars" "history" "home"
731 "ignoreeof" "mail" "noclobber" "noglob" "nonomatch" "path" "prompt"
732 "shell" "status" "time" "verbose")
733
734 (es sh-append shell
735 "apid" "cdpath" "CDPATH" "history" "home" "ifs" "noexport" "path"
736 "pid" "prompt" "signals")
737
738 (jcsh sh-append csh
739 "notify")
740
741 (ksh88 sh-append sh
742 "ENV" "ERRNO" "FCEDIT" "FPATH" "HISTFILE" "HISTSIZE" "LINENO"
743 "OLDPWD" "PPID" "PS3" "PS4" "PWD" "RANDOM" "REPLY" "SECONDS"
744 "TMOUT")
745
746 (oash sh-append sh
747 "FIELD" "FIELD_MAX" "LAST_KEY" "OALIB" "PP_ITEM" "PP_NUM")
748
749 (rc sh-append shell
750 "apid" "apids" "cdpath" "CDPATH" "history" "home" "ifs" "path" "pid"
751 "prompt" "status")
752
753 (sh sh-append shell
754 "CDPATH" "IFS" "OPTARG" "OPTIND" "PS1" "PS2")
755
756 ;; The next entry is only used for defining the others
757 (shell "COLUMNS" "EDITOR" "HOME" "HUSHLOGIN" "LANG" "LC_COLLATE"
758 "LC_CTYPE" "LC_MESSAGES" "LC_MONETARY" "LC_NUMERIC" "LC_TIME"
759 "LINES" "LOGNAME" "MAIL" "MAILCHECK" "MAILPATH" "PAGER" "PATH"
760 "SHELL" "TERM" "TERMCAP" "TERMINFO" "VISUAL")
761
762 (tcsh sh-append csh
763 "addsuffix" "ampm" "autocorrect" "autoexpand" "autolist"
764 "autologout" "chase_symlinks" "correct" "dextract" "edit" "el"
765 "fignore" "gid" "histlit" "HOST" "HOSTTYPE" "HPATH"
766 "ignore_symlinks" "listjobs" "listlinks" "listmax" "matchbeep"
767 "nobeep" "NOREBIND" "oid" "printexitvalue" "prompt2" "prompt3"
768 "pushdsilent" "pushdtohome" "recexact" "recognize_only_executables"
769 "rmstar" "savehist" "SHLVL" "showdots" "sl" "SYSTYPE" "tcsh" "term"
770 "tperiod" "tty" "uid" "version" "visiblebell" "watch" "who"
771 "wordchars")
772
773 (zsh sh-append ksh88
774 "BAUD" "bindcmds" "cdpath" "DIRSTACKSIZE" "fignore" "FIGNORE" "fpath"
775 "HISTCHARS" "hostcmds" "hosts" "HOSTS" "LISTMAX" "LITHISTSIZE"
776 "LOGCHECK" "mailpath" "manpath" "NULLCMD" "optcmds" "path" "POSTEDIT"
777 "prompt" "PROMPT" "PROMPT2" "PROMPT3" "PROMPT4" "psvar" "PSVAR"
778 "READNULLCMD" "REPORTTIME" "RPROMPT" "RPS1" "SAVEHIST" "SPROMPT"
779 "STTY" "TIMEFMT" "TMOUT" "TMPPREFIX" "varcmds" "watch" "WATCH"
780 "WATCHFMT" "WORDCHARS" "ZDOTDIR"))
781 "List of all shell variables available for completing read.
782 See `sh-feature'.")
783
784 \f
785 ;; Font-Lock support
786
787 (defface sh-heredoc-face
788 '((((class color)
789 (background dark))
790 (:foreground "yellow" :weight bold))
791 (((class color)
792 (background light))
793 (:foreground "tan" ))
794 (t
795 (:weight bold)))
796 "Face to show a here-document"
797 :group 'sh-indentation)
798 (defvar sh-heredoc-face 'sh-heredoc-face)
799
800
801 (defvar sh-font-lock-keywords
802 '((csh sh-append shell
803 ("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
804 font-lock-variable-name-face))
805
806 (es sh-append executable-font-lock-keywords
807 ("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
808 font-lock-variable-name-face))
809
810 (rc sh-append es)
811
812 (sh sh-append shell
813 ;; Variable names.
814 ("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
815 font-lock-variable-name-face)
816 ;; Function names.
817 ("^\\(\\sw+\\)[ \t]*(" 1 font-lock-function-name-face)
818 ("\\<\\(function\\)\\>[ \t]*\\(\\sw+\\)?"
819 (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)))
820
821 ;; The next entry is only used for defining the others
822 (shell sh-append executable-font-lock-keywords
823 ;; Using font-lock-string-face here confuses sh-get-indent-info.
824 ("\\\\$" 0 font-lock-warning-face)
825 ("\\\\[^A-Za-z0-9]" 0 font-lock-string-face)
826 ("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
827 font-lock-variable-name-face))
828 (rpm sh-append rpm2
829 ("%{?\\(\\sw+\\)" 1 font-lock-keyword-face))
830 (rpm2 sh-append shell
831 ("^\\(\\sw+\\):" 1 font-lock-variable-name-face)))
832 "Default expressions to highlight in Shell Script modes. See `sh-feature'.")
833
834 (defvar sh-font-lock-keywords-1
835 '((sh "[ \t]in\\>"))
836 "Subdued level highlighting for Shell Script modes.")
837
838 (defvar sh-font-lock-keywords-2 ()
839 "Gaudy level highlighting for Shell Script modes.")
840
841 ;; These are used for the syntax table stuff (derived from cperl-mode).
842 ;; Note: parse-sexp-lookup-properties must be set to t for it to work.
843 (defconst sh-st-punc (string-to-syntax "."))
844 (defconst sh-st-symbol (string-to-syntax "_"))
845 (defconst sh-here-doc-syntax (string-to-syntax "|")) ;; generic string
846
847 (defconst sh-here-doc-open-re "<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\|\\s_\\)+\\).*\\(\n\\)")
848
849 (defvar sh-here-doc-markers nil)
850 (make-variable-buffer-local 'sh-here-doc-markers)
851 (defvar sh-here-doc-re sh-here-doc-open-re)
852 (make-variable-buffer-local 'sh-here-doc-re)
853
854 (defun sh-font-lock-close-heredoc (bol eof indented)
855 "Determine the syntax of the \\n after an EOF.
856 If non-nil INDENTED indicates that the EOF was indented."
857 (let* ((eof-re (if eof (regexp-quote eof) ""))
858 ;; A rough regexp that should find the opening <<EOF back.
859 (sre (concat "<<\\(-?\\)\\s-*['\"\\]?"
860 ;; Use \s| to cheaply check it's an open-heredoc.
861 eof-re "['\"]?\\([ \t|;&)<>].*\\)?\\s|"))
862 ;; A regexp that will find other EOFs.
863 (ere (concat "^" (if indented "[ \t]*") eof-re "\n"))
864 (start (save-excursion
865 (goto-char bol)
866 (re-search-backward (concat sre "\\|" ere) nil t))))
867 ;; If subgroup 1 matched, we found an open-heredoc, otherwise we first
868 ;; found a close-heredoc which makes the current close-heredoc inoperant.
869 (cond
870 ((when (and start (match-end 1)
871 (not (and indented (= (match-beginning 1) (match-end 1))))
872 (not (sh-in-comment-or-string (match-beginning 0))))
873 ;; Make sure our `<<' is not the EOF1 of a `cat <<EOF1 <<EOF2'.
874 (save-excursion
875 (goto-char start)
876 (setq start (line-beginning-position 2))
877 (while
878 (progn
879 (re-search-forward "<<") ; Skip ourselves.
880 (and (re-search-forward sh-here-doc-open-re start 'move)
881 (goto-char (match-beginning 0))
882 (sh-in-comment-or-string (point)))))
883 ;; No <<EOF2 found after our <<.
884 (= (point) start)))
885 sh-here-doc-syntax)
886 ((not (or start (save-excursion (re-search-forward sre nil t))))
887 ;; There's no <<EOF either before or after us,
888 ;; so we should remove ourselves from font-lock's keywords.
889 (setq sh-here-doc-markers (delete eof sh-here-doc-markers))
890 (setq sh-here-doc-re
891 (concat sh-here-doc-open-re "\\|^\\([ \t]*\\)"
892 (regexp-opt sh-here-doc-markers t) "\\(\n\\)"))
893 nil))))
894
895 (defun sh-font-lock-open-heredoc (start string)
896 "Determine the syntax of the \\n after a <<EOF.
897 START is the position of <<.
898 STRING is the actual word used as delimiter (f.ex. \"EOF\").
899 INDENTED is non-nil if the here document's content (and the EOF mark) can
900 be indented (i.e. a <<- was used rather than just <<)."
901 (unless (or (memq (char-before start) '(?< ?>))
902 (sh-in-comment-or-string start))
903 ;; We're looking at <<STRING, so we add "^STRING$" to the syntactic
904 ;; font-lock keywords to detect the end of this here document.
905 (let ((str (replace-regexp-in-string "['\"]" "" string)))
906 (unless (member str sh-here-doc-markers)
907 (push str sh-here-doc-markers)
908 (setq sh-here-doc-re
909 (concat sh-here-doc-open-re "\\|^\\([ \t]*\\)"
910 (regexp-opt sh-here-doc-markers t) "\\(\n\\)"))))
911 sh-here-doc-syntax))
912
913 (defun sh-font-lock-here-doc (limit)
914 "Search for a heredoc marker."
915 ;; This looks silly, but it's because `sh-here-doc-re' keeps changing.
916 (re-search-forward sh-here-doc-re limit t))
917
918 (defun sh-is-quoted-p (pos)
919 (and (eq (char-before pos) ?\\)
920 (not (sh-is-quoted-p (1- pos)))))
921
922 (defun sh-font-lock-paren (start)
923 (save-excursion
924 (goto-char start)
925 ;; Skip through all patterns
926 (while
927 (progn
928 (forward-comment (- (point-max)))
929 ;; Skip through one pattern
930 (while
931 (or (/= 0 (skip-syntax-backward "w_"))
932 (/= 0 (skip-chars-backward "?[]*/\\"))
933 (and (sh-is-quoted-p (1- (point)))
934 (goto-char (- (point) 2)))
935 (when (memq (char-before) '(?\" ?\'))
936 (condition-case nil (progn (backward-sexp 1) t)
937 (error nil)))))
938 (forward-comment (- (point-max)))
939 (when (eq (char-before) ?|)
940 (backward-char 1) t)))
941 (when (save-excursion (backward-char 2) (looking-at ";;\\|in"))
942 sh-st-punc)))
943
944 (defconst sh-font-lock-syntactic-keywords
945 ;; A `#' begins a comment when it is unquoted and at the beginning of a
946 ;; word. In the shell, words are separated by metacharacters.
947 ;; The list of special chars is taken from the single-unix spec
948 ;; of the shell command language (under `quoting') but with `$' removed.
949 `(("[^|&;<>()`\\\"' \t\n]\\(#+\\)" 1 ,sh-st-symbol)
950 ;; Find HEREDOC starters and add a corresponding rule for the ender.
951 (sh-font-lock-here-doc
952 (2 (sh-font-lock-open-heredoc
953 (match-beginning 0) (match-string 1)) nil t)
954 (5 (sh-font-lock-close-heredoc
955 (match-beginning 0) (match-string 4)
956 (and (match-beginning 3) (/= (match-beginning 3) (match-end 3))))
957 nil t))
958 ;; Distinguish the special close-paren in `case'.
959 (")" 0 (sh-font-lock-paren (match-beginning 0)))))
960
961 (defun sh-font-lock-syntactic-face-function (state)
962 (if (nth 3 state)
963 (if (char-valid-p (nth 3 state))
964 font-lock-string-face
965 sh-heredoc-face)
966 font-lock-comment-face))
967
968 (defgroup sh-indentation nil
969 "Variables controlling indentation in shell scripts.
970
971 Note: customizing these variables will not affect existing buffers if
972 `sh-make-vars-local' is no-nil. See the documentation for
973 variable `sh-make-vars-local', command `sh-make-vars-local'
974 and command `sh-reset-indent-vars-to-global-values'."
975 :group 'sh-script)
976
977
978 (defcustom sh-set-shell-hook nil
979 "*Hook run by `sh-set-shell'."
980 :type 'hook
981 :group 'sh-script)
982
983 (defcustom sh-mode-hook nil
984 "*Hook run by `sh-mode'."
985 :type 'hook
986 :group 'sh-script)
987
988 (defcustom sh-learn-basic-offset nil
989 "*When `sh-guess-basic-offset' should learn `sh-basic-offset'.
990
991 nil mean: never.
992 t means: only if there seems to be an obvious value.
993 Anything else means: whenever we have a \"good guess\" as to the value."
994 :type '(choice
995 (const :tag "Never" nil)
996 (const :tag "Only if sure" t)
997 (const :tag "If have a good guess" usually))
998 :group 'sh-indentation)
999
1000 (defcustom sh-popup-occur-buffer nil
1001 "*Controls when `sh-learn-buffer-indent' pops the *indent* buffer.
1002 If t it is always shown. If nil, it is shown only when there
1003 are conflicts."
1004 :type '(choice
1005 (const :tag "Only when there are conflicts." nil)
1006 (const :tag "Always" t))
1007 :group 'sh-indentation)
1008
1009 (defcustom sh-blink t
1010 "*If non-nil, `sh-show-indent' shows the line indentation is relative to.
1011 The position on the line is not necessarily meaningful.
1012 In some cases the line will be the matching keyword, but this is not
1013 always the case."
1014 :type 'boolean
1015 :group 'sh-indentation)
1016
1017 (defcustom sh-first-lines-indent 0
1018 "*The indentation of the first non-blank non-comment line.
1019 Usually 0 meaning first column.
1020 Can be set to a number, or to nil which means leave it as is."
1021 :type '(choice
1022 (const :tag "Leave as is" nil)
1023 (integer :tag "Column number"
1024 :menu-tag "Indent to this col (0 means first col)" ))
1025 :group 'sh-indentation)
1026
1027
1028 (defcustom sh-basic-offset 4
1029 "*The default indentation increment.
1030 This value is used for the + and - symbols in an indentation variable."
1031 :type 'integer
1032 :group 'sh-indentation)
1033
1034 (defcustom sh-indent-comment nil
1035 "*How a comment line is to be indented.
1036 nil means leave it as it is;
1037 t means indent it as a normal line, aligning it to previous non-blank
1038 non-comment line;
1039 a number means align to that column, e.g. 0 means fist column."
1040 :type '(choice
1041 (const :tag "Leave as is." nil)
1042 (const :tag "Indent as a normal line." t)
1043 (integer :menu-tag "Indent to this col (0 means first col)."
1044 :tag "Indent to column number.") )
1045 :group 'sh-indentation)
1046
1047
1048 (defvar sh-debug nil
1049 "Enable lots of debug messages - if function `sh-debug' is enabled.")
1050
1051
1052 ;; Uncomment this defun and comment the defmacro for debugging.
1053 ;; (defun sh-debug (&rest args)
1054 ;; "For debugging: display message ARGS if variable SH-DEBUG is non-nil."
1055 ;; (if sh-debug
1056 ;; (apply 'message args)))
1057 (defmacro sh-debug (&rest args))
1058
1059 (defconst sh-symbol-list
1060 '((const :tag "+ " :value +
1061 :menu-tag "+ Indent right by sh-basic-offset")
1062 (const :tag "- " :value -
1063 :menu-tag "- Indent left by sh-basic-offset")
1064 (const :tag "++" :value ++
1065 :menu-tag "++ Indent right twice sh-basic-offset")
1066 (const :tag "--" :value --
1067 :menu-tag "-- Indent left twice sh-basic-offset")
1068 (const :tag "* " :value *
1069 :menu-tag "* Indent right half sh-basic-offset")
1070 (const :tag "/ " :value /
1071 :menu-tag "/ Indent left half sh-basic-offset")))
1072
1073 (defcustom sh-indent-for-else 0
1074 "*How much to indent an else relative to an if. Usually 0."
1075 :type `(choice
1076 (integer :menu-tag "A number (positive=>indent right)"
1077 :tag "A number")
1078 (const :tag "--") ;; separator!
1079 ,@ sh-symbol-list
1080 )
1081 :group 'sh-indentation)
1082
1083 (defconst sh-number-or-symbol-list
1084 (append '((integer :menu-tag "A number (positive=>indent right)"
1085 :tag "A number")
1086 (const :tag "--")) ; separator
1087 sh-symbol-list))
1088
1089 (defcustom sh-indent-for-fi 0
1090 "*How much to indent a fi relative to an if. Usually 0."
1091 :type `(choice ,@ sh-number-or-symbol-list )
1092 :group 'sh-indentation)
1093
1094 (defcustom sh-indent-for-done '0
1095 "*How much to indent a done relative to its matching stmt. Usually 0."
1096 :type `(choice ,@ sh-number-or-symbol-list )
1097 :group 'sh-indentation)
1098
1099 (defcustom sh-indent-after-else '+
1100 "*How much to indent a statement after an else statement."
1101 :type `(choice ,@ sh-number-or-symbol-list )
1102 :group 'sh-indentation)
1103
1104 (defcustom sh-indent-after-if '+
1105 "*How much to indent a statement after an if statement.
1106 This includes lines after else and elif statements, too, but
1107 does not affect then else elif or fi statements themselves."
1108 :type `(choice ,@ sh-number-or-symbol-list )
1109 :group 'sh-indentation)
1110
1111 (defcustom sh-indent-for-then '+
1112 "*How much to indent a then relative to an if."
1113 :type `(choice ,@ sh-number-or-symbol-list )
1114 :group 'sh-indentation)
1115
1116 (defcustom sh-indent-for-do '*
1117 "*How much to indent a do statement.
1118 This is relative to the statement before the do, i.e. the
1119 while until or for statement."
1120 :type `(choice ,@ sh-number-or-symbol-list)
1121 :group 'sh-indentation)
1122
1123 (defcustom sh-indent-after-do '*
1124 "*How much to indent a line after a do statement.
1125 This is used when the do is the first word of the line.
1126 This is relative to the statement before the do, e.g. a
1127 while for repeat or select statement."
1128 :type `(choice ,@ sh-number-or-symbol-list)
1129 :group 'sh-indentation)
1130
1131 (defcustom sh-indent-after-loop-construct '+
1132 "*How much to indent a statement after a loop construct.
1133
1134 This variable is used when the keyword \"do\" is on the same line as the
1135 loop statement (e.g. \"until\", \"while\" or \"for\").
1136 If the do is on a line by itself, then `sh-indent-after-do' is used instead."
1137 :type `(choice ,@ sh-number-or-symbol-list)
1138 :group 'sh-indentation)
1139
1140
1141 (defcustom sh-indent-after-done 0
1142 "*How much to indent a statement after a \"done\" keyword.
1143 Normally this is 0, which aligns the \"done\" to the matching
1144 looping construct line.
1145 Setting it non-zero allows you to have the \"do\" statement on a line
1146 by itself and align the done under to do."
1147 :type `(choice ,@ sh-number-or-symbol-list)
1148 :group 'sh-indentation)
1149
1150 (defcustom sh-indent-for-case-label '+
1151 "*How much to indent a case label statement.
1152 This is relative to the line containing the case statement."
1153 :type `(choice ,@ sh-number-or-symbol-list)
1154 :group 'sh-indentation)
1155
1156 (defcustom sh-indent-for-case-alt '++
1157 "*How much to indent statements after the case label.
1158 This is relative to the line containing the case statement."
1159 :type `(choice ,@ sh-number-or-symbol-list)
1160 :group 'sh-indentation)
1161
1162
1163 (defcustom sh-indent-for-continuation '+
1164 "*How much to indent for a continuation statement."
1165 :type `(choice ,@ sh-number-or-symbol-list)
1166 :group 'sh-indentation)
1167
1168 (defcustom sh-indent-after-open '+
1169 "*How much to indent after a line with an opening parenthesis or brace.
1170 For an open paren after a function `sh-indent-after-function' is used."
1171 :type `(choice ,@ sh-number-or-symbol-list)
1172 :group 'sh-indentation)
1173
1174 (defcustom sh-indent-after-function '+
1175 "*How much to indent after a function line."
1176 :type `(choice ,@ sh-number-or-symbol-list)
1177 :group 'sh-indentation)
1178
1179 ;; These 2 are for the rc shell:
1180
1181 (defcustom sh-indent-after-switch '+
1182 "*How much to indent a case statement relative to the switch statement.
1183 This is for the rc shell."
1184 :type `(choice ,@ sh-number-or-symbol-list)
1185 :group 'sh-indentation)
1186
1187 (defcustom sh-indent-after-case '+
1188 "*How much to indent a statement relative to the case statement.
1189 This is for the rc shell."
1190 :type `(choice ,@ sh-number-or-symbol-list)
1191 :group 'sh-indentation)
1192
1193 ;; Internal use - not designed to be changed by the user:
1194
1195 (defun sh-mkword-regexpr (word)
1196 "Make a regexp which matches WORD as a word.
1197 This specifically excludes an occurrence of WORD followed by
1198 punctuation characters like '-'."
1199 (concat word "\\([^-a-z0-9_]\\|$\\)"))
1200
1201 (defconst sh-re-done (sh-mkword-regexpr "done"))
1202
1203
1204 (defconst sh-kws-for-done
1205 '((sh . ( "while" "until" "for" ) )
1206 (bash . ( "while" "until" "for" "select" ) )
1207 (ksh88 . ( "while" "until" "for" "select" ) )
1208 (zsh . ( "while" "until" "for" "repeat" "select" ) ) )
1209 "Which keywords can match the word `done' in this shell.")
1210
1211
1212 (defconst sh-indent-supported
1213 '((sh . t)
1214 (csh . nil)
1215 (rc . t))
1216 "Shell types that shell indenting can do something with.")
1217
1218 (defvar sh-indent-supported-here nil
1219 "Non-nil if we support indentation for the current buffer's shell type.")
1220
1221 (defconst sh-var-list
1222 '(
1223 sh-basic-offset sh-first-lines-indent sh-indent-after-case
1224 sh-indent-after-do sh-indent-after-done
1225 sh-indent-after-else
1226 sh-indent-after-if
1227 sh-indent-after-loop-construct
1228 sh-indent-after-open
1229 sh-indent-comment
1230 sh-indent-for-case-alt
1231 sh-indent-for-case-label
1232 sh-indent-for-continuation
1233 sh-indent-for-do
1234 sh-indent-for-done
1235 sh-indent-for-else
1236 sh-indent-for-fi
1237 sh-indent-for-then
1238 )
1239 "A list of variables used by script mode to control indentation.
1240 This list is used when switching between buffer-local and global
1241 values of variables, and for the commands using indentation styles.")
1242
1243 (defvar sh-make-vars-local t
1244 "*Controls whether indentation variables are local to the buffer.
1245 If non-nil, indentation variables are made local initially.
1246 If nil, you can later make the variables local by invoking
1247 command `sh-make-vars-local'.
1248 The default is t because I assume that in one Emacs session one is
1249 frequently editing existing scripts with different styles.")
1250
1251 \f
1252 ;; mode-command and utility functions
1253
1254 ;;;###autoload
1255 (defun sh-mode ()
1256 "Major mode for editing shell scripts.
1257 This mode works for many shells, since they all have roughly the same syntax,
1258 as far as commands, arguments, variables, pipes, comments etc. are concerned.
1259 Unless the file's magic number indicates the shell, your usual shell is
1260 assumed. Since filenames rarely give a clue, they are not further analyzed.
1261
1262 This mode adapts to the variations between shells (see `sh-set-shell') by
1263 means of an inheritance based feature lookup (see `sh-feature'). This
1264 mechanism applies to all variables (including skeletons) that pertain to
1265 shell-specific features.
1266
1267 The default style of this mode is that of Rosenblatt's Korn shell book.
1268 The syntax of the statements varies with the shell being used. The
1269 following commands are available, based on the current shell's syntax:
1270
1271 \\[sh-case] case statement
1272 \\[sh-for] for loop
1273 \\[sh-function] function definition
1274 \\[sh-if] if statement
1275 \\[sh-indexed-loop] indexed loop from 1 to n
1276 \\[sh-while-getopts] while getopts loop
1277 \\[sh-repeat] repeat loop
1278 \\[sh-select] select loop
1279 \\[sh-until] until loop
1280 \\[sh-while] while loop
1281
1282 For sh and rc shells indentation commands are:
1283 \\[sh-show-indent] Show the variable controlling this line's indentation.
1284 \\[sh-set-indent] Set then variable controlling this line's indentation.
1285 \\[sh-learn-line-indent] Change the indentation variable so this line
1286 would indent to the way it currently is.
1287 \\[sh-learn-buffer-indent] Set the indentation variables so the
1288 buffer indents as it currently is indented.
1289
1290
1291 \\[backward-delete-char-untabify] Delete backward one position, even if it was a tab.
1292 \\[sh-newline-and-indent] Delete unquoted space and indent new line same as this one.
1293 \\[sh-end-of-command] Go to end of successive commands.
1294 \\[sh-beginning-of-command] Go to beginning of successive commands.
1295 \\[sh-set-shell] Set this buffer's shell, and maybe its magic number.
1296 \\[sh-execute-region] Have optional header and region be executed in a subshell.
1297
1298 \\[sh-maybe-here-document] Without prefix, following an unquoted < inserts here document.
1299 \{, (, [, ', \", `
1300 Unless quoted with \\, insert the pairs {}, (), [], or '', \"\", ``.
1301
1302 If you generally program a shell different from your login shell you can
1303 set `sh-shell-file' accordingly. If your shell's file name doesn't correctly
1304 indicate what shell it is use `sh-alias-alist' to translate.
1305
1306 If your shell gives error messages with line numbers, you can use \\[executable-interpret]
1307 with your script for an edit-interpret-debug cycle."
1308 (interactive)
1309 (kill-all-local-variables)
1310 (setq major-mode 'sh-mode
1311 mode-name "Shell-script")
1312 (use-local-map sh-mode-map)
1313 (make-local-variable 'skeleton-end-hook)
1314 (make-local-variable 'paragraph-start)
1315 (make-local-variable 'paragraph-separate)
1316 (make-local-variable 'comment-start)
1317 (make-local-variable 'comment-start-skip)
1318 (make-local-variable 'require-final-newline)
1319 (make-local-variable 'sh-header-marker)
1320 (make-local-variable 'sh-shell-file)
1321 (make-local-variable 'sh-shell)
1322 (make-local-variable 'skeleton-pair-alist)
1323 (make-local-variable 'skeleton-pair-filter)
1324 (make-local-variable 'comint-dynamic-complete-functions)
1325 (make-local-variable 'comint-prompt-regexp)
1326 (make-local-variable 'font-lock-defaults)
1327 (make-local-variable 'skeleton-filter)
1328 (make-local-variable 'skeleton-newline-indent-rigidly)
1329 (make-local-variable 'sh-shell-variables)
1330 (make-local-variable 'sh-shell-variables-initialized)
1331 (make-local-variable 'imenu-generic-expression)
1332 (make-local-variable 'sh-indent-supported-here)
1333 (setq skeleton-end-hook (lambda ()
1334 (or (eolp) (newline) (indent-relative)))
1335 paragraph-start (concat page-delimiter "\\|$")
1336 paragraph-separate paragraph-start
1337 comment-start "# "
1338 comint-dynamic-complete-functions sh-dynamic-complete-functions
1339 ;; we can't look if previous line ended with `\'
1340 comint-prompt-regexp "^[ \t]*"
1341 font-lock-defaults
1342 `((sh-font-lock-keywords
1343 sh-font-lock-keywords-1 sh-font-lock-keywords-2)
1344 nil nil
1345 ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")) nil
1346 (font-lock-syntactic-keywords . sh-font-lock-syntactic-keywords)
1347 (font-lock-syntactic-face-function
1348 . sh-font-lock-syntactic-face-function))
1349 skeleton-pair-alist '((?` _ ?`))
1350 skeleton-pair-filter 'sh-quoted-p
1351 skeleton-further-elements '((< '(- (min sh-indentation
1352 (current-column)))))
1353 skeleton-filter 'sh-feature
1354 skeleton-newline-indent-rigidly t
1355 sh-indent-supported-here nil)
1356 (set (make-local-variable 'parse-sexp-ignore-comments) t)
1357 ;; Parse or insert magic number for exec, and set all variables depending
1358 ;; on the shell thus determined.
1359 (let ((interpreter
1360 (save-excursion
1361 (goto-char (point-min))
1362 (cond ((looking-at "#![ \t]?\\([^ \t\n]*/bin/env[ \t]\\)?\\([^ \t\n]+\\)")
1363 (match-string 2))
1364 ((and buffer-file-name
1365 (string-match "\\.m?spec$" buffer-file-name))
1366 "rpm")))))
1367 (sh-set-shell (or interpreter sh-shell-file) nil nil))
1368 (run-hooks 'sh-mode-hook))
1369
1370 ;;;###autoload
1371 (defalias 'shell-script-mode 'sh-mode)
1372
1373
1374 (defun sh-font-lock-keywords (&optional keywords)
1375 "Function to get simple fontification based on `sh-font-lock-keywords'.
1376 This adds rules for comments and assignments."
1377 (sh-feature sh-font-lock-keywords
1378 (when (stringp (sh-feature sh-assignment-regexp))
1379 (lambda (list)
1380 `((,(sh-feature sh-assignment-regexp)
1381 1 font-lock-variable-name-face)
1382 ,@keywords
1383 ,@list)))))
1384
1385 (defun sh-font-lock-keywords-1 (&optional builtins)
1386 "Function to get better fontification including keywords."
1387 (let ((keywords (concat "\\([;(){}`|&]\\|^\\)[ \t]*\\(\\("
1388 (regexp-opt (sh-feature sh-leading-keywords) t)
1389 "[ \t]+\\)?"
1390 (regexp-opt (append (sh-feature sh-leading-keywords)
1391 (sh-feature sh-other-keywords))
1392 t))))
1393 (sh-font-lock-keywords
1394 `(,@(if builtins
1395 `((,(concat keywords "[ \t]+\\)?"
1396 (regexp-opt (sh-feature sh-builtins) t)
1397 "\\>")
1398 (2 font-lock-keyword-face nil t)
1399 (6 font-lock-builtin-face))
1400 ,@(sh-feature sh-font-lock-keywords-2)))
1401 (,(concat keywords "\\)\\>")
1402 2 font-lock-keyword-face)
1403 ,@(sh-feature sh-font-lock-keywords-1)))))
1404
1405 (defun sh-font-lock-keywords-2 ()
1406 "Function to get better fontification including keywords and builtins."
1407 (sh-font-lock-keywords-1 t))
1408
1409
1410 (defvar sh-regexp-for-done nil
1411 "A buffer-local regexp to match opening keyword for done.")
1412
1413 (defvar sh-kw-alist nil
1414 "A buffer-local, since it is shell-type dependent, list of keywords.")
1415
1416 ;; ( key-word first-on-this on-prev-line )
1417 ;; This is used to set `sh-kw-alist' which is a list of sublists each
1418 ;; having 3 elements:
1419 ;; a keyword
1420 ;; a rule to check when the keyword appears on "this" line
1421 ;; a rule to check when the keyword appears on "the previous" line
1422 ;; The keyword is usually a string and is the first word on a line.
1423 ;; If this keyword appears on the line whose indentation is to be
1424 ;; calculated, the rule in element 2 is called. If this returns
1425 ;; non-zero, the resulting point (which may be changed by the rule)
1426 ;; is used as the default indentation.
1427 ;; If it returned false or the keyword was not found in the table,
1428 ;; then the keyword from the previous line is looked up and the rule
1429 ;; in element 3 is called. In this case, however,
1430 ;; `sh-get-indent-info' does not stop but may keep going and test
1431 ;; other keywords against rules in element 3. This is because the
1432 ;; preceding line could have, for example, an opening "if" and an
1433 ;; opening "while" keyword and we need to add the indentation offsets
1434 ;; for both.
1435 ;;
1436 (defconst sh-kw
1437 '((sh
1438 ("if" nil sh-handle-prev-if)
1439 ("elif" sh-handle-this-else sh-handle-prev-else)
1440 ("else" sh-handle-this-else sh-handle-prev-else)
1441 ("fi" sh-handle-this-fi sh-handle-prev-fi)
1442 ("then" sh-handle-this-then sh-handle-prev-then)
1443 ("(" nil sh-handle-prev-open)
1444 ("{" nil sh-handle-prev-open)
1445 ("[" nil sh-handle-prev-open)
1446 ("}" sh-handle-this-close nil)
1447 (")" sh-handle-this-close nil)
1448 ("]" sh-handle-this-close nil)
1449 ("case" nil sh-handle-prev-case)
1450 ("esac" sh-handle-this-esac sh-handle-prev-esac)
1451 (case-label nil sh-handle-after-case-label) ;; ???
1452 (";;" nil sh-handle-prev-case-alt-end) ;; ???
1453 ("done" sh-handle-this-done sh-handle-prev-done)
1454 ("do" sh-handle-this-do sh-handle-prev-do))
1455
1456 ;; Note: we don't need specific stuff for bash and zsh shells;
1457 ;; the regexp `sh-regexp-for-done' handles the extra keywords
1458 ;; these shells use.
1459 (rc
1460 ("{" nil sh-handle-prev-open)
1461 ("}" sh-handle-this-close nil)
1462 ("case" sh-handle-this-rc-case sh-handle-prev-rc-case))))
1463
1464
1465 (defun sh-set-shell (shell &optional no-query-flag insert-flag)
1466 "Set this buffer's shell to SHELL (a string).
1467 When used interactively, insert the proper starting #!-line,
1468 and make the visited file executable via `executable-set-magic',
1469 perhaps querying depending on the value of `executable-query'.
1470
1471 When this function is called noninteractively, INSERT-FLAG (the third
1472 argument) controls whether to insert a #!-line and think about making
1473 the visited file executable, and NO-QUERY-FLAG (the second argument)
1474 controls whether to query about making the visited file executable.
1475
1476 Calls the value of `sh-set-shell-hook' if set."
1477 (interactive (list (completing-read (format "Shell \(default %s\): "
1478 sh-shell-file)
1479 interpreter-mode-alist
1480 (lambda (x) (eq (cdr x) 'sh-mode))
1481 nil nil nil sh-shell-file)
1482 (eq executable-query 'function)
1483 t))
1484 (if (string-match "\\.exe\\'" shell)
1485 (setq shell (substring shell 0 (match-beginning 0))))
1486 (setq sh-shell (intern (file-name-nondirectory shell))
1487 sh-shell (or (cdr (assq sh-shell sh-alias-alist))
1488 sh-shell))
1489 (if insert-flag
1490 (setq sh-shell-file
1491 (executable-set-magic shell (sh-feature sh-shell-arg)
1492 no-query-flag insert-flag)))
1493 (let ((tem (sh-feature sh-require-final-newline)))
1494 (unless (eq tem 'require-final-newline)
1495 (setq require-final-newline tem)))
1496 (setq
1497 comment-start-skip "#+[\t ]*"
1498 ;;; local-abbrev-table (sh-feature sh-abbrevs)
1499 mode-line-process (format "[%s]" sh-shell)
1500 sh-shell-variables nil
1501 sh-shell-variables-initialized nil
1502 imenu-generic-expression (sh-feature sh-imenu-generic-expression)
1503 imenu-case-fold-search nil)
1504 (make-local-variable 'sh-mode-syntax-table)
1505 (let ((tem (sh-feature sh-mode-syntax-table-input)))
1506 (setq sh-mode-syntax-table
1507 (if tem (apply 'sh-mode-syntax-table tem)
1508 sh-mode-default-syntax-table)))
1509 (set-syntax-table sh-mode-syntax-table)
1510 (dolist (var (sh-feature sh-variables))
1511 (sh-remember-variable var))
1512 (make-local-variable 'indent-line-function)
1513 (if (setq sh-indent-supported-here (sh-feature sh-indent-supported))
1514 (progn
1515 (message "Setting up indent for shell type %s" sh-shell)
1516 (set (make-local-variable 'parse-sexp-lookup-properties) t)
1517 (set (make-local-variable 'sh-kw-alist) (sh-feature sh-kw))
1518 (let ((regexp (sh-feature sh-kws-for-done)))
1519 (if regexp
1520 (set (make-local-variable 'sh-regexp-for-done)
1521 (sh-mkword-regexpr (regexp-opt regexp t)))))
1522 (message "setting up indent stuff")
1523 ;; sh-mode has already made indent-line-function local
1524 ;; but do it in case this is called before that.
1525 (setq indent-line-function 'sh-indent-line)
1526 (if sh-make-vars-local
1527 (sh-make-vars-local))
1528 (message "Indentation setup for shell type %s" sh-shell))
1529 (message "No indentation for this shell type.")
1530 (setq indent-line-function 'sh-basic-indent-line))
1531 (run-hooks 'sh-set-shell-hook))
1532
1533
1534
1535 (defun sh-feature (alist &optional function)
1536 "Index ALIST by the current shell.
1537 If ALIST isn't a list where every element is a cons, it is returned as is.
1538 Else indexing follows an inheritance logic which works in two ways:
1539
1540 - Fall back on successive ancestors (see `sh-ancestor-alist') as long as
1541 the alist contains no value for the current shell.
1542 The ultimate default is always `sh'.
1543
1544 - If the value thus looked up is a list starting with `sh-append',
1545 we call the function `sh-append' with the rest of the list as
1546 arguments, and use the value. However, the next element of the
1547 list is not used as-is; instead, we look it up recursively
1548 in ALIST to allow the function called to define the value for
1549 one shell to be derived from another shell.
1550 The value thus determined is physically replaced into the alist.
1551
1552 Optional FUNCTION is applied to the determined value and the result is cached
1553 in ALIST."
1554 (or (if (consp alist)
1555 (let ((l alist))
1556 (while (and l (consp (car l)))
1557 (setq l (cdr l)))
1558 (if l alist)))
1559 (if function
1560 (cdr (assoc (setq function (cons sh-shell function)) alist)))
1561 (let ((sh-shell sh-shell)
1562 elt val)
1563 (while (and sh-shell
1564 (not (setq elt (assq sh-shell alist))))
1565 (setq sh-shell (cdr (assq sh-shell sh-ancestor-alist))))
1566 ;; If the shell is not known, treat it as sh.
1567 (unless elt
1568 (setq elt (assq 'sh alist)))
1569 (if (and (consp (setq val (cdr elt)))
1570 (memq (car val) '(sh-append sh-modify)))
1571 (setcdr elt
1572 (setq val
1573 (apply (car val)
1574 (let ((sh-shell (car (cdr val))))
1575 (if (assq sh-shell alist)
1576 (sh-feature alist)
1577 (eval sh-shell)))
1578 (cddr val)))))
1579 (if function
1580 (nconc alist
1581 (list (cons function
1582 (setq sh-shell (car function)
1583 val (funcall (cdr function) val))))))
1584 val)))
1585
1586
1587
1588 ;; I commented this out because nobody calls it -- rms.
1589 ;;(defun sh-abbrevs (ancestor &rest list)
1590 ;; "Iff it isn't, define the current shell as abbrev table and fill that.
1591 ;;Abbrev table will inherit all abbrevs from ANCESTOR, which is either an abbrev
1592 ;;table or a list of (NAME1 EXPANSION1 ...). In addition it will define abbrevs
1593 ;;according to the remaining arguments NAMEi EXPANSIONi ...
1594 ;;EXPANSION may be either a string or a skeleton command."
1595 ;; (or (if (boundp sh-shell)
1596 ;; (symbol-value sh-shell))
1597 ;; (progn
1598 ;; (if (listp ancestor)
1599 ;; (nconc list ancestor))
1600 ;; (define-abbrev-table sh-shell ())
1601 ;; (if (vectorp ancestor)
1602 ;; (mapatoms (lambda (atom)
1603 ;; (or (eq atom 0)
1604 ;; (define-abbrev (symbol-value sh-shell)
1605 ;; (symbol-name atom)
1606 ;; (symbol-value atom)
1607 ;; (symbol-function atom))))
1608 ;; ancestor))
1609 ;; (while list
1610 ;; (define-abbrev (symbol-value sh-shell)
1611 ;; (car list)
1612 ;; (if (stringp (car (cdr list)))
1613 ;; (car (cdr list))
1614 ;; "")
1615 ;; (if (symbolp (car (cdr list)))
1616 ;; (car (cdr list))))
1617 ;; (setq list (cdr (cdr list)))))
1618 ;; (symbol-value sh-shell)))
1619
1620
1621 (defun sh-append (ancestor &rest list)
1622 "Return list composed of first argument (a list) physically appended to rest."
1623 (nconc list ancestor))
1624
1625
1626 (defun sh-modify (skeleton &rest list)
1627 "Modify a copy of SKELETON by replacing I1 with REPL1, I2 with REPL2 ..."
1628 (setq skeleton (copy-sequence skeleton))
1629 (while list
1630 (setcar (or (nthcdr (car list) skeleton)
1631 (error "Index %d out of bounds" (car list)))
1632 (car (cdr list)))
1633 (setq list (nthcdr 2 list)))
1634 skeleton)
1635
1636
1637 (defun sh-basic-indent-line ()
1638 "Indent a line for Sh mode (shell script mode).
1639 Indent as far as preceding non-empty line, then by steps of `sh-indentation'.
1640 Lines containing only comments are considered empty."
1641 (interactive)
1642 (let ((previous (save-excursion
1643 (while (and (progn (beginning-of-line)
1644 (not (bobp)))
1645 (progn
1646 (forward-line -1)
1647 (back-to-indentation)
1648 (or (eolp)
1649 (eq (following-char) ?#)))))
1650 (current-column)))
1651 current)
1652 (save-excursion
1653 (indent-to (if (eq this-command 'newline-and-indent)
1654 previous
1655 (if (< (current-column)
1656 (setq current (progn (back-to-indentation)
1657 (current-column))))
1658 (if (eolp) previous 0)
1659 (delete-region (point)
1660 (progn (beginning-of-line) (point)))
1661 (if (eolp)
1662 (max previous (* (1+ (/ current sh-indentation))
1663 sh-indentation))
1664 (* (1+ (/ current sh-indentation)) sh-indentation))))))
1665 (if (< (current-column) (current-indentation))
1666 (skip-chars-forward " \t"))))
1667
1668
1669 (defun sh-execute-region (start end &optional flag)
1670 "Pass optional header and region to a subshell for noninteractive execution.
1671 The working directory is that of the buffer, and only environment variables
1672 are already set which is why you can mark a header within the script.
1673
1674 With a positive prefix ARG, instead of sending region, define header from
1675 beginning of buffer to point. With a negative prefix ARG, instead of sending
1676 region, clear header."
1677 (interactive "r\nP")
1678 (if flag
1679 (setq sh-header-marker (if (> (prefix-numeric-value flag) 0)
1680 (point-marker)))
1681 (if sh-header-marker
1682 (save-excursion
1683 (let (buffer-undo-list)
1684 (goto-char sh-header-marker)
1685 (append-to-buffer (current-buffer) start end)
1686 (shell-command-on-region (point-min)
1687 (setq end (+ sh-header-marker
1688 (- end start)))
1689 sh-shell-file)
1690 (delete-region sh-header-marker end)))
1691 (shell-command-on-region start end (concat sh-shell-file " -")))))
1692
1693
1694 (defun sh-remember-variable (var)
1695 "Make VARIABLE available for future completing reads in this buffer."
1696 (or (< (length var) sh-remember-variable-min)
1697 (getenv var)
1698 (assoc var sh-shell-variables)
1699 (push (cons var var) sh-shell-variables))
1700 var)
1701
1702
1703
1704 (defun sh-quoted-p ()
1705 "Is point preceded by an odd number of backslashes?"
1706 (eq -1 (% (save-excursion (skip-chars-backward "\\\\")) 2)))
1707 \f
1708 ;; Indentation stuff.
1709 (defun sh-must-support-indent ()
1710 "*Signal an error if the shell type for this buffer is not supported.
1711 Also, the buffer must be in Shell-script mode."
1712 (unless sh-indent-supported-here
1713 (error "This buffer's shell does not support indentation through Emacs")))
1714
1715 (defun sh-make-vars-local ()
1716 "Make the indentation variables local to this buffer.
1717 Normally they already are local. This command is provided in case
1718 variable `sh-make-vars-local' has been set to nil.
1719
1720 To revert all these variables to the global values, use
1721 command `sh-reset-indent-vars-to-global-values'."
1722 (interactive)
1723 (mapcar 'make-local-variable sh-var-list)
1724 (message "Indentation variable are now local."))
1725
1726 (defun sh-reset-indent-vars-to-global-values ()
1727 "Reset local indentation variables to the global values.
1728 Then, if variable `sh-make-vars-local' is non-nil, make them local."
1729 (interactive)
1730 (mapcar 'kill-local-variable sh-var-list)
1731 (if sh-make-vars-local
1732 (mapcar 'make-local-variable sh-var-list)))
1733
1734
1735 ;; Theoretically these are only needed in shell and derived modes.
1736 ;; However, the routines which use them are only called in those modes.
1737 (defconst sh-special-keywords "then\\|do")
1738
1739 (defun sh-help-string-for-variable (var)
1740 "Construct a string for `sh-read-variable' when changing variable VAR ."
1741 (let ((msg (documentation-property var 'variable-documentation))
1742 (msg2 ""))
1743 (unless (memq var '(sh-first-lines-indent sh-indent-comment))
1744 (setq msg2
1745 (format "\n
1746 You can enter a number (positive to increase indentation,
1747 negative to decrease indentation, zero for no change to indentation).
1748
1749 Or, you can enter one of the following symbols which are relative to
1750 the value of variable `sh-basic-offset'
1751 which in this buffer is currently %s.
1752
1753 \t%s."
1754 sh-basic-offset
1755 (mapconcat (lambda (x)
1756 (nth (1- (length x)) x))
1757 sh-symbol-list "\n\t"))))
1758 (concat
1759 ;; The following shows the global not the local value!
1760 ;; (format "Current value of %s is %s\n\n" var (symbol-value var))
1761 msg msg2)))
1762
1763 (defun sh-read-variable (var)
1764 "Read a new value for indentation variable VAR."
1765 (interactive "*variable? ") ;; to test
1766 (let ((minibuffer-help-form `(sh-help-string-for-variable
1767 (quote ,var)))
1768 val)
1769 (setq val (read-from-minibuffer
1770 (format "New value for %s (press %s for help): "
1771 var (single-key-description help-char))
1772 (format "%s" (symbol-value var))
1773 nil t))
1774 val))
1775
1776
1777
1778 (defun sh-in-comment-or-string (start)
1779 "Return non-nil if START is in a comment or string."
1780 (save-excursion
1781 (let ((state (syntax-ppss start)))
1782 (or (nth 3 state) (nth 4 state)))))
1783
1784 (defun sh-goto-matching-if ()
1785 "Go to the matching if for a fi.
1786 This handles nested if..fi pairs."
1787 (let ((found (sh-find-prev-matching "\\bif\\b" "\\bfi\\b" 1)))
1788 (if found
1789 (goto-char found))))
1790
1791
1792 ;; Functions named sh-handle-this-XXX are called when the keyword on the
1793 ;; line whose indentation is being handled contain XXX;
1794 ;; those named sh-handle-prev-XXX are when XXX appears on the previous line.
1795
1796 (defun sh-handle-prev-if ()
1797 (list '(+ sh-indent-after-if)))
1798
1799 (defun sh-handle-this-else ()
1800 (if (sh-goto-matching-if)
1801 ;; (list "aligned to if")
1802 (list "aligned to if" '(+ sh-indent-for-else))
1803 nil
1804 ))
1805
1806 (defun sh-handle-prev-else ()
1807 (if (sh-goto-matching-if)
1808 (list '(+ sh-indent-after-if))
1809 ))
1810
1811 (defun sh-handle-this-fi ()
1812 (if (sh-goto-matching-if)
1813 (list "aligned to if" '(+ sh-indent-for-fi))
1814 nil
1815 ))
1816
1817 (defun sh-handle-prev-fi ()
1818 ;; Why do we have this rule? Because we must go back to the if
1819 ;; to get its indent. We may continue back from there.
1820 ;; We return nil because we don't have anything to add to result,
1821 ;; the side affect of setting align-point is all that matters.
1822 ;; we could return a comment (a string) but I can't think of a good one...
1823 (sh-goto-matching-if)
1824 nil)
1825
1826 (defun sh-handle-this-then ()
1827 (let ((p (sh-goto-matching-if)))
1828 (if p
1829 (list '(+ sh-indent-for-then))
1830 )))
1831
1832 (defun sh-handle-prev-then ()
1833 (let ((p (sh-goto-matching-if)))
1834 (if p
1835 (list '(+ sh-indent-after-if))
1836 )))
1837
1838 (defun sh-handle-prev-open ()
1839 (save-excursion
1840 (let ((x (sh-prev-stmt)))
1841 (if (and x
1842 (progn
1843 (goto-char x)
1844 (or
1845 (looking-at "function\\b")
1846 (looking-at "\\s-*\\S-+\\s-*()")
1847 )))
1848 (list '(+ sh-indent-after-function))
1849 (list '(+ sh-indent-after-open)))
1850 )))
1851
1852 (defun sh-handle-this-close ()
1853 (forward-char 1) ;; move over ")"
1854 (if (sh-safe-forward-sexp -1)
1855 (list "aligned to opening paren")))
1856
1857 (defun sh-goto-matching-case ()
1858 (let ((found (sh-find-prev-matching "\\bcase\\b" "\\besac\\b" 1)))
1859 (if found (goto-char found))))
1860
1861 (defun sh-handle-prev-case ()
1862 ;; This is typically called when point is on same line as a case
1863 ;; we shouldn't -- and can't find prev-case
1864 (if (looking-at ".*\\<case\\>")
1865 (list '(+ sh-indent-for-case-label))
1866 (error "We don't seem to be on a line with a case"))) ;; debug
1867
1868 (defun sh-handle-this-esac ()
1869 (if (sh-goto-matching-case)
1870 (list "aligned to matching case")))
1871
1872 (defun sh-handle-prev-esac ()
1873 (if (sh-goto-matching-case)
1874 (list "matching case")))
1875
1876 (defun sh-handle-after-case-label ()
1877 (if (sh-goto-matching-case)
1878 (list '(+ sh-indent-for-case-alt))))
1879
1880 (defun sh-handle-prev-case-alt-end ()
1881 (if (sh-goto-matching-case)
1882 (list '(+ sh-indent-for-case-label))))
1883
1884 (defun sh-safe-forward-sexp (&optional arg)
1885 "Try and do a `forward-sexp', but do not error.
1886 Return new point if successful, nil if an error occurred."
1887 (condition-case nil
1888 (progn
1889 (forward-sexp (or arg 1))
1890 (point)) ;; return point if successful
1891 (error
1892 (sh-debug "oops!(1) %d" (point))
1893 nil))) ;; return nil if fail
1894
1895 (defun sh-goto-match-for-done ()
1896 (let ((found (sh-find-prev-matching sh-regexp-for-done sh-re-done 1)))
1897 (if found
1898 (goto-char found))))
1899
1900 (defun sh-handle-this-done ()
1901 (if (sh-goto-match-for-done)
1902 (list "aligned to do stmt" '(+ sh-indent-for-done))))
1903
1904 (defun sh-handle-prev-done ()
1905 (if (sh-goto-match-for-done)
1906 (list "previous done")))
1907
1908 (defun sh-handle-this-do ()
1909 (if (sh-goto-match-for-done)
1910 (list '(+ sh-indent-for-do))))
1911
1912 (defun sh-handle-prev-do ()
1913 (cond
1914 ((save-restriction
1915 (narrow-to-region
1916 (point)
1917 (save-excursion
1918 (beginning-of-line)
1919 (point)))
1920 (sh-goto-match-for-done))
1921 (sh-debug "match for done found on THIS line")
1922 (list '(+ sh-indent-after-loop-construct)))
1923 ((sh-goto-match-for-done)
1924 (sh-debug "match for done found on PREV line")
1925 (list '(+ sh-indent-after-do)))
1926 (t
1927 (message "match for done NOT found")
1928 nil)))
1929
1930 ;; for rc:
1931 (defun sh-find-prev-switch ()
1932 "Find the line for the switch keyword matching this line's case keyword."
1933 (re-search-backward "\\<switch\\>" nil t))
1934
1935 (defun sh-handle-this-rc-case ()
1936 (if (sh-find-prev-switch)
1937 (list '(+ sh-indent-after-switch))
1938 ;; (list '(+ sh-indent-for-case-label))
1939 nil))
1940
1941 (defun sh-handle-prev-rc-case ()
1942 (list '(+ sh-indent-after-case)))
1943
1944 (defun sh-check-rule (n thing)
1945 (let ((rule (nth n (assoc thing sh-kw-alist)))
1946 (val nil))
1947 (if rule
1948 (progn
1949 (setq val (funcall rule))
1950 (sh-debug "rule (%d) for %s at %d is %s\n-> returned %s"
1951 n thing (point) rule val)))
1952 val))
1953
1954
1955 (defun sh-get-indent-info ()
1956 "Return indent-info for this line.
1957 This is a list. nil means the line is to be left as is.
1958 Otherwise it contains one or more of the following sublists:
1959 \(t NUMBER\) NUMBER is the base location in the buffer that indentation is
1960 relative to. If present, this is always the first of the
1961 sublists. The indentation of the line in question is
1962 derived from the indentation of this point, possibly
1963 modified by subsequent sublists.
1964 \(+ VAR\)
1965 \(- VAR\) Get the value of variable VAR and add to or subtract from
1966 the indentation calculated so far.
1967 \(= VAR\) Get the value of variable VAR and *replace* the
1968 indentation with its value. This only occurs for
1969 special variables such as `sh-indent-comment'.
1970 STRING This is ignored for the purposes of calculating
1971 indentation, it is printed in certain cases to help show
1972 what the indentation is based on."
1973 ;; See comments before `sh-kw'.
1974 (save-excursion
1975 (let ((have-result nil)
1976 this-kw
1977 start
1978 val
1979 (result nil)
1980 (align-point nil)
1981 prev-line-end x)
1982 (beginning-of-line)
1983 ;; Note: setting result to t means we are done and will return nil.
1984 ;;(This function never returns just t.)
1985 (cond
1986 ((or (and (boundp 'font-lock-string-face) (not (bobp))
1987 (eq (get-text-property (1- (point)) 'face)
1988 font-lock-string-face))
1989 (eq (get-text-property (point) 'face) sh-heredoc-face))
1990 (setq result t)
1991 (setq have-result t))
1992 ((looking-at "\\s-*#") ; was (equal this-kw "#")
1993 (if (bobp)
1994 (setq result t) ;; return nil if 1st line!
1995 (setq result (list '(= sh-indent-comment)))
1996 ;; we still need to get previous line in case
1997 ;; sh-indent-comment is t (indent as normal)
1998 (setq align-point (sh-prev-line nil))
1999 (setq have-result nil)
2000 ))
2001 ) ;; cond
2002
2003 (unless have-result
2004 ;; Continuation lines are handled specially
2005 (if (sh-this-is-a-continuation)
2006 (progn
2007 ;; We assume the line being continued is already
2008 ;; properly indented...
2009 ;; (setq prev-line-end (sh-prev-line))
2010 (setq align-point (sh-prev-line nil))
2011 (setq result (list '(+ sh-indent-for-continuation)))
2012 (setq have-result t))
2013 (beginning-of-line)
2014 (skip-chars-forward " \t")
2015 (setq this-kw (sh-get-kw)))
2016
2017 ;; Handle "this" keyword: first word on the line we're
2018 ;; calculating indentation info for.
2019 (if this-kw
2020 (if (setq val (sh-check-rule 1 this-kw))
2021 (progn
2022 (setq align-point (point))
2023 (sh-debug
2024 "this - setting align-point to %d" align-point)
2025 (setq result (append result val))
2026 (setq have-result t)
2027 ;; set prev-line to continue processing remainder
2028 ;; of this line as a previous line
2029 (setq prev-line-end (point))
2030 ))))
2031
2032 (unless have-result
2033 (setq prev-line-end (sh-prev-line 'end)))
2034
2035 (if prev-line-end
2036 (save-excursion
2037 ;; We start off at beginning of this line.
2038 ;; Scan previous statements while this is <=
2039 ;; start of previous line.
2040 (setq start (point)) ;; for debug only
2041 (goto-char prev-line-end)
2042 (setq x t)
2043 (while (and x (setq x (sh-prev-thing)))
2044 (sh-debug "at %d x is: %s result is: %s" (point) x result)
2045 (cond
2046 ((and (equal x ")")
2047 (equal (get-text-property (1- (point)) 'syntax-table)
2048 sh-st-punc))
2049 (sh-debug "Case label) here")
2050 (setq x 'case-label)
2051 (if (setq val (sh-check-rule 2 x))
2052 (progn
2053 (setq result (append result val))
2054 (setq align-point (point))))
2055 (or (bobp)
2056 (forward-char -1))
2057 (skip-chars-forward "[a-z0-9]*?")
2058 )
2059 ((string-match "[])}]" x)
2060 (setq x (sh-safe-forward-sexp -1))
2061 (if x
2062 (progn
2063 (setq align-point (point))
2064 (setq result (append result
2065 (list "aligned to opening paren")))
2066 )))
2067 ((string-match "[[({]" x)
2068 (sh-debug "Checking special thing: %s" x)
2069 (if (setq val (sh-check-rule 2 x))
2070 (setq result (append result val)))
2071 (forward-char -1)
2072 (setq align-point (point)))
2073 ((string-match "[\"'`]" x)
2074 (sh-debug "Skipping back for %s" x)
2075 ;; this was oops-2
2076 (setq x (sh-safe-forward-sexp -1)))
2077 ((stringp x)
2078 (sh-debug "Checking string %s at %s" x (point))
2079 (if (setq val (sh-check-rule 2 x))
2080 ;; (or (eq t (car val))
2081 ;; (eq t (car (car val))))
2082 (setq result (append result val)))
2083 ;; not sure about this test Wed Jan 27 23:48:35 1999
2084 (setq align-point (point))
2085 (unless (bolp)
2086 (forward-char -1)))
2087 (t
2088 (error "Don't know what to do with %s" x))
2089 )
2090 ) ;; while
2091 (sh-debug "result is %s" result)
2092 )
2093 (sh-debug "No prev line!")
2094 (sh-debug "result: %s align-point: %s" result align-point)
2095 )
2096
2097 (if align-point
2098 ;; was: (setq result (append result (list (list t align-point))))
2099 (setq result (append (list (list t align-point)) result))
2100 )
2101 (sh-debug "result is now: %s" result)
2102
2103 (or result
2104 (if prev-line-end
2105 (setq result (list (list t prev-line-end)))
2106 (setq result (list (list '= 'sh-first-lines-indent)))
2107 ))
2108
2109 (if (eq result t)
2110 (setq result nil))
2111 (sh-debug "result is: %s" result)
2112 result
2113 ) ;; let
2114 ))
2115
2116
2117 (defun sh-get-indent-var-for-line (&optional info)
2118 "Return the variable controlling indentation for this line.
2119 If there is not [just] one such variable, return a string
2120 indicating the problem.
2121 If INFO is supplied it is used, else it is calculated."
2122 (let ((var nil)
2123 (result nil)
2124 (reason nil)
2125 sym elt)
2126 (or info
2127 (setq info (sh-get-indent-info)))
2128 (if (null info)
2129 (setq result "this line to be left as is")
2130 (while (and info (null result))
2131 (setq elt (car info))
2132 (cond
2133 ((stringp elt)
2134 (setq reason elt)
2135 )
2136 ((not (listp elt))
2137 (error "sh-get-indent-var-for-line invalid elt: %s" elt))
2138 ;; so it is a list
2139 ((eq t (car elt))
2140 ) ;; nothing
2141 ((symbolp (setq sym (nth 1 elt)))
2142 ;; A bit of a kludge - when we see the sh-indent-comment
2143 ;; ignore other variables. Otherwise it is tricky to
2144 ;; "learn" the comment indentation.
2145 (if (eq var 'sh-indent-comment)
2146 (setq result var)
2147 (if var
2148 (setq result
2149 "this line is controlled by more than 1 variable.")
2150 (setq var sym))))
2151 (t
2152 (error "sh-get-indent-var-for-line invalid list elt: %s" elt)))
2153 (setq info (cdr info))
2154 ))
2155 (or result
2156 (setq result var))
2157 (or result
2158 (setq result reason))
2159 (if (null result)
2160 ;; e.g. just had (t POS)
2161 (setq result "line has default indentation"))
2162 result))
2163
2164
2165
2166 ;; Finding the previous line isn't trivial.
2167 ;; We must *always* go back one more and see if that is a continuation
2168 ;; line -- it is the PREVIOUS line which is continued, not the one
2169 ;; we are going to!
2170 ;; Also, we want to treat a whole "here document" as one big line,
2171 ;; because we may want to a align to the beginning of it.
2172 ;;
2173 ;; What we do:
2174 ;; - go back to previous non-empty line
2175 ;; - if this is in a here-document, go to the beginning of it
2176 ;; - while previous line is continued, go back one line
2177 (defun sh-prev-line (&optional end)
2178 "Back to end of previous non-comment non-empty line.
2179 Go to beginning of logical line unless END is non-nil, in which case
2180 we go to the end of the previous line and do not check for continuations."
2181 (save-excursion
2182 (beginning-of-line)
2183 (forward-comment (- (point-max)))
2184 (unless end (beginning-of-line))
2185 (when (and (not (bobp))
2186 (equal (get-text-property (1- (point)) 'face)
2187 sh-heredoc-face))
2188 (let ((p1 (previous-single-property-change (1- (point)) 'face)))
2189 (when p1
2190 (goto-char p1)
2191 (if end
2192 (end-of-line)
2193 (beginning-of-line)))))
2194 (unless end
2195 ;; we must check previous lines to see if they are continuation lines
2196 ;; if so, we must return position of first of them
2197 (while (and (sh-this-is-a-continuation)
2198 (>= 0 (forward-line -1))))
2199 (beginning-of-line)
2200 (skip-chars-forward " \t"))
2201 (point)))
2202
2203
2204 (defun sh-prev-stmt ()
2205 "Return the address of the previous stmt or nil."
2206 ;; This is used when we are trying to find a matching keyword.
2207 ;; Searching backward for the keyword would certainly be quicker, but
2208 ;; it is hard to remove "false matches" -- such as if the keyword
2209 ;; appears in a string or quote. This way is slower, but (I think) safer.
2210 (interactive)
2211 (save-excursion
2212 (let ((going t)
2213 (start (point))
2214 (found nil)
2215 (prev nil))
2216 (skip-chars-backward " \t;|&({[")
2217 (while (and (not found)
2218 (not (bobp))
2219 going)
2220 ;; Do a backward-sexp if possible, else backup bit by bit...
2221 (if (sh-safe-forward-sexp -1)
2222 (progn
2223 (if (looking-at sh-special-keywords)
2224 (progn
2225 (setq found prev))
2226 (setq prev (point))
2227 ))
2228 ;; backward-sexp failed
2229 (if (zerop (skip-chars-backward " \t()[\]{};`'"))
2230 (forward-char -1))
2231 (if (bolp)
2232 (let ((back (sh-prev-line nil)))
2233 (if back
2234 (goto-char back)
2235 (setq going nil)))))
2236 (unless found
2237 (skip-chars-backward " \t")
2238 (if (or (and (bolp) (not (sh-this-is-a-continuation)))
2239 (eq (char-before) ?\;)
2240 (looking-at "\\s-*[|&]"))
2241 (setq found (point)))))
2242 (if found
2243 (goto-char found))
2244 (if found
2245 (progn
2246 (skip-chars-forward " \t|&({[")
2247 (setq found (point))))
2248 (if (>= (point) start)
2249 (progn
2250 (debug "We didn't move!")
2251 (setq found nil))
2252 (or found
2253 (sh-debug "Did not find prev stmt.")))
2254 found)))
2255
2256
2257 (defun sh-get-word ()
2258 "Get a shell word skipping whitespace from point."
2259 (interactive)
2260 (skip-chars-forward "\t ")
2261 (let ((start (point)))
2262 (while
2263 (if (looking-at "[\"'`]")
2264 (sh-safe-forward-sexp)
2265 ;; (> (skip-chars-forward "^ \t\n\"'`") 0)
2266 (> (skip-chars-forward "-_a-zA-Z\$0-9") 0)
2267 ))
2268 (buffer-substring start (point))
2269 ))
2270
2271 (defun sh-prev-thing ()
2272 "Return the previous thing this logical line."
2273 ;; This is called when `sh-get-indent-info' is working backwards on
2274 ;; the previous line(s) finding what keywords may be relevant for
2275 ;; indenting. It moves over sexps if possible, and will stop
2276 ;; on a ; and at the beginning of a line if it is not a continuation
2277 ;; line.
2278 ;;
2279 ;; Added a kludge for ";;"
2280 ;; Possible return values:
2281 ;; nil - nothing
2282 ;; a string - possibly a keyword
2283 ;;
2284 (if (bolp)
2285 nil
2286 (let (c min-point
2287 (start (point)))
2288 (save-restriction
2289 (narrow-to-region
2290 (if (sh-this-is-a-continuation)
2291 (setq min-point (sh-prev-line nil))
2292 (save-excursion
2293 (beginning-of-line)
2294 (setq min-point (point))))
2295 (point))
2296 (skip-chars-backward " \t;")
2297 (unless (looking-at "\\s-*;;")
2298 (skip-chars-backward "^)}];\"'`({[")
2299 (setq c (char-before))))
2300 (sh-debug "stopping at %d c is %s start=%d min-point=%d"
2301 (point) c start min-point)
2302 (if (< (point) min-point)
2303 (error "point %d < min-point %d" (point) min-point))
2304 (cond
2305 ((looking-at "\\s-*;;")
2306 ;; (message "Found ;; !")
2307 ";;")
2308 ((or (eq c ?\n)
2309 (eq c nil)
2310 (eq c ?\;))
2311 (save-excursion
2312 ;; skip forward over white space newline and \ at eol
2313 (skip-chars-forward " \t\n\\\\")
2314 (sh-debug "Now at %d start=%d" (point) start)
2315 (if (>= (point) start)
2316 (progn
2317 (sh-debug "point: %d >= start: %d" (point) start)
2318 nil)
2319 (sh-get-word))
2320 ))
2321 (t
2322 ;; c -- return a string
2323 (char-to-string c)
2324 ))
2325 )))
2326
2327
2328 (defun sh-this-is-a-continuation ()
2329 "Return non-nil if current line is a continuation of previous line."
2330 (save-excursion
2331 (and (zerop (forward-line -1))
2332 (looking-at ".*\\\\$")
2333 (not (nth 4 (parse-partial-sexp (match-beginning 0) (match-end 0)
2334 nil nil nil t))))))
2335
2336 (defun sh-get-kw (&optional where and-move)
2337 "Return first word of line from WHERE.
2338 If AND-MOVE is non-nil then move to end of word."
2339 (let ((start (point)))
2340 (if where
2341 (goto-char where))
2342 (prog1
2343 (buffer-substring (point)
2344 (progn (skip-chars-forward "^ \t\n;")(point)))
2345 (unless and-move
2346 (goto-char start)))))
2347
2348 (defun sh-find-prev-matching (open close &optional depth)
2349 "Find a matching token for a set of opening and closing keywords.
2350 This takes into account that there may be nested open..close pairings.
2351 OPEN and CLOSE are regexps denoting the tokens to be matched.
2352 Optional parameter DEPTH (usually 1) says how many to look for."
2353 (let ((parse-sexp-ignore-comments t)
2354 prev)
2355 (setq depth (or depth 1))
2356 (save-excursion
2357 (condition-case nil
2358 (while (and
2359 (/= 0 depth)
2360 (not (bobp))
2361 (setq prev (sh-prev-stmt)))
2362 (goto-char prev)
2363 (save-excursion
2364 (if (looking-at "\\\\\n")
2365 (progn
2366 (forward-char 2)
2367 (skip-chars-forward " \t")))
2368 (cond
2369 ((looking-at open)
2370 (setq depth (1- depth))
2371 (sh-debug "found open at %d - depth = %d" (point) depth))
2372 ((looking-at close)
2373 (setq depth (1+ depth))
2374 (sh-debug "found close - depth = %d" depth))
2375 (t
2376 ))))
2377 (error nil))
2378 (if (eq depth 0)
2379 prev ;; (point)
2380 nil)
2381 )))
2382
2383
2384 (defun sh-var-value (var &optional ignore-error)
2385 "Return the value of variable VAR, interpreting symbols.
2386 It can also return t or nil.
2387 If an illegal value is found, throw an error unless Optional argument
2388 IGNORE-ERROR is non-nil."
2389 (let ((val (symbol-value var)))
2390 (cond
2391 ((numberp val)
2392 val)
2393 ((eq val t)
2394 val)
2395 ((null val)
2396 val)
2397 ((eq val '+)
2398 sh-basic-offset)
2399 ((eq val '-)
2400 (- sh-basic-offset))
2401 ((eq val '++)
2402 (* 2 sh-basic-offset))
2403 ((eq val '--)
2404 (* 2 (- sh-basic-offset)))
2405 ((eq val '*)
2406 (/ sh-basic-offset 2))
2407 ((eq val '/)
2408 (/ (- sh-basic-offset) 2))
2409 (t
2410 (if ignore-error
2411 (progn
2412 (message "Don't know how to handle %s's value of %s" var val)
2413 0)
2414 (error "Don't know how to handle %s's value of %s" var val))
2415 ))))
2416
2417 (defun sh-set-var-value (var value &optional no-symbol)
2418 "Set variable VAR to VALUE.
2419 Unless optional argument NO-SYMBOL is non-nil, then if VALUE is
2420 can be represented by a symbol then do so."
2421 (cond
2422 (no-symbol
2423 (set var value))
2424 ((= value sh-basic-offset)
2425 (set var '+))
2426 ((= value (- sh-basic-offset))
2427 (set var '-))
2428 ((eq value (* 2 sh-basic-offset))
2429 (set var '++))
2430 ((eq value (* 2 (- sh-basic-offset)))
2431 (set var '--))
2432 ((eq value (/ sh-basic-offset 2))
2433 (set var '*))
2434 ((eq value (/ (- sh-basic-offset) 2))
2435 (set var '/))
2436 (t
2437 (set var value)))
2438 )
2439
2440
2441 (defun sh-calculate-indent (&optional info)
2442 "Return the indentation for the current line.
2443 If INFO is supplied it is used, else it is calculated from current line."
2444 (let ((ofs 0)
2445 (base-value 0)
2446 elt a b var val)
2447 (or info
2448 (setq info (sh-get-indent-info)))
2449 (when info
2450 (while info
2451 (sh-debug "info: %s ofs=%s" info ofs)
2452 (setq elt (car info))
2453 (cond
2454 ((stringp elt)) ;; do nothing?
2455 ((listp elt)
2456 (setq a (car (car info)))
2457 (setq b (nth 1 (car info)))
2458 (cond
2459 ((eq a t)
2460 (save-excursion
2461 (goto-char b)
2462 (setq val (current-indentation)))
2463 (setq base-value val))
2464 ((symbolp b)
2465 (setq val (sh-var-value b))
2466 (cond
2467 ((eq a '=)
2468 (cond
2469 ((null val)
2470 ;; no indentation
2471 ;; set info to nil so we stop immediately
2472 (setq base-value nil ofs nil info nil))
2473 ((eq val t) (setq ofs 0)) ;; indent as normal line
2474 (t
2475 ;; The following assume the (t POS) come first!
2476 (setq ofs val base-value 0)
2477 (setq info nil)))) ;; ? stop now
2478 ((eq a '+) (setq ofs (+ ofs val)))
2479 ((eq a '-) (setq ofs (- ofs val)))
2480 (t
2481 (error "sh-calculate-indent invalid a a=%s b=%s" a b))))
2482 (t
2483 (error "sh-calculate-indent invalid elt: a=%s b=%s" a b))))
2484 (t
2485 (error "sh-calculate-indent invalid elt %s" elt)))
2486 (sh-debug "a=%s b=%s val=%s base-value=%s ofs=%s"
2487 a b val base-value ofs)
2488 (setq info (cdr info)))
2489 ;; return value:
2490 (sh-debug "at end: base-value: %s ofs: %s" base-value ofs)
2491
2492 (cond
2493 ((or (null base-value)(null ofs))
2494 nil)
2495 ((and (numberp base-value)(numberp ofs))
2496 (sh-debug "base (%d) + ofs (%d) = %d"
2497 base-value ofs (+ base-value ofs))
2498 (+ base-value ofs)) ;; return value
2499 (t
2500 (error "sh-calculate-indent: Help. base-value=%s ofs=%s"
2501 base-value ofs)
2502 nil)))))
2503
2504
2505 (defun sh-indent-line ()
2506 "Indent the current line."
2507 (interactive)
2508 (let ((indent (sh-calculate-indent))
2509 (pos (- (point-max) (point))))
2510 (when indent
2511 (beginning-of-line)
2512 (skip-chars-forward " \t")
2513 (indent-line-to indent)
2514 ;; If initial point was within line's indentation,
2515 ;; position after the indentation. Else stay at same point in text.
2516 (if (> (- (point-max) pos) (point))
2517 (goto-char (- (point-max) pos))))))
2518
2519
2520 (defun sh-blink (blinkpos &optional msg)
2521 "Move cursor momentarily to BLINKPOS and display MSG."
2522 ;; We can get here without it being a number on first line
2523 (if (numberp blinkpos)
2524 (save-excursion
2525 (goto-char blinkpos)
2526 (message msg)
2527 (sit-for blink-matching-delay))
2528 (message msg)))
2529
2530 (defun sh-show-indent (arg)
2531 "Show the how the currently line would be indented.
2532 This tells you which variable, if any, controls the indentation of
2533 this line.
2534 If optional arg ARG is non-null (called interactively with a prefix),
2535 a pop up window describes this variable.
2536 If variable `sh-blink' is non-nil then momentarily go to the line
2537 we are indenting relative to, if applicable."
2538 (interactive "P")
2539 (sh-must-support-indent)
2540 (let* ((info (sh-get-indent-info))
2541 (var (sh-get-indent-var-for-line info))
2542 (curr-indent (current-indentation))
2543 val msg)
2544 (if (stringp var)
2545 (message (setq msg var))
2546 (setq val (sh-calculate-indent info))
2547
2548 (if (eq curr-indent val)
2549 (setq msg (format "%s is %s" var (symbol-value var)))
2550 (setq msg
2551 (if val
2552 (format "%s (%s) would change indent from %d to: %d"
2553 var (symbol-value var) curr-indent val)
2554 (format "%s (%s) would leave line as is"
2555 var (symbol-value var)))
2556 ))
2557 (if (and arg var)
2558 (describe-variable var)))
2559 (if sh-blink
2560 (let ((info (sh-get-indent-info)))
2561 (if (and info (listp (car info))
2562 (eq (car (car info)) t))
2563 (sh-blink (nth 1 (car info)) msg)
2564 (message msg)))
2565 (message msg))
2566 ))
2567
2568 (defun sh-set-indent ()
2569 "Set the indentation for the current line.
2570 If the current line is controlled by an indentation variable, prompt
2571 for a new value for it."
2572 (interactive)
2573 (sh-must-support-indent)
2574 (let* ((info (sh-get-indent-info))
2575 (var (sh-get-indent-var-for-line info))
2576 val old-val indent-val)
2577 (if (stringp var)
2578 (message (format "Cannot set indent - %s" var))
2579 (setq old-val (symbol-value var))
2580 (setq val (sh-read-variable var))
2581 (condition-case nil
2582 (progn
2583 (set var val)
2584 (setq indent-val (sh-calculate-indent info))
2585 (if indent-val
2586 (message "Variable: %s Value: %s would indent to: %d"
2587 var (symbol-value var) indent-val)
2588 (message "Variable: %s Value: %s would leave line as is."
2589 var (symbol-value var)))
2590 ;; I'm not sure about this, indenting it now?
2591 ;; No. Because it would give the impression that an undo would
2592 ;; restore thing, but the value has been altered.
2593 ;; (sh-indent-line)
2594 )
2595 (error
2596 (set var old-val)
2597 (message "Bad value for %s, restoring to previous value %s"
2598 var old-val)
2599 (sit-for 1)
2600 nil))
2601 )))
2602
2603
2604 (defun sh-learn-line-indent (arg)
2605 "Learn how to indent a line as it currently is indented.
2606
2607 If there is an indentation variable which controls this line's indentation,
2608 then set it to a value which would indent the line the way it
2609 presently is.
2610
2611 If the value can be represented by one of the symbols then do so
2612 unless optional argument ARG (the prefix when interactive) is non-nil."
2613 (interactive "*P")
2614 (sh-must-support-indent)
2615 ;; I'm not sure if we show allow learning on an empty line.
2616 ;; Though it might occasionally be useful I think it usually
2617 ;; would just be confusing.
2618 (if (save-excursion
2619 (beginning-of-line)
2620 (looking-at "\\s-*$"))
2621 (message "sh-learn-line-indent ignores empty lines.")
2622 (let* ((info (sh-get-indent-info))
2623 (var (sh-get-indent-var-for-line info))
2624 ival sval diff new-val
2625 (no-symbol arg)
2626 (curr-indent (current-indentation)))
2627 (cond
2628 ((stringp var)
2629 (message (format "Cannot learn line - %s" var)))
2630 ((eq var 'sh-indent-comment)
2631 ;; This is arbitrary...
2632 ;; - if curr-indent is 0, set to curr-indent
2633 ;; - else if it has the indentation of a "normal" line,
2634 ;; then set to t
2635 ;; - else set to curr-indent.
2636 (setq sh-indent-comment
2637 (if (= curr-indent 0)
2638 0
2639 (let* ((sh-indent-comment t)
2640 (val2 (sh-calculate-indent info)))
2641 (if (= val2 curr-indent)
2642 t
2643 curr-indent))))
2644 (message "%s set to %s" var (symbol-value var))
2645 )
2646 ((numberp (setq sval (sh-var-value var)))
2647 (setq ival (sh-calculate-indent info))
2648 (setq diff (- curr-indent ival))
2649
2650 (sh-debug "curr-indent: %d ival: %d diff: %d var:%s sval %s"
2651 curr-indent ival diff var sval)
2652 (setq new-val (+ sval diff))
2653 ;;; I commented out this because someone might want to replace
2654 ;;; a value of `+' with the current value of sh-basic-offset
2655 ;;; or vice-versa.
2656 ;;; (if (= 0 diff)
2657 ;;; (message "No change needed!")
2658 (sh-set-var-value var new-val no-symbol)
2659 (message "%s set to %s" var (symbol-value var))
2660 )
2661 (t
2662 (debug)
2663 (message "Cannot change %s" var))))))
2664
2665
2666
2667 (defun sh-mark-init (buffer)
2668 "Initialize a BUFFER to be used by `sh-mark-line'."
2669 (save-excursion
2670 (set-buffer (get-buffer-create buffer))
2671 (erase-buffer)
2672 (occur-mode)
2673 ))
2674
2675
2676 (defun sh-mark-line (message point buffer &optional add-linenum occur-point)
2677 "Insert MESSAGE referring to location POINT in current buffer into BUFFER.
2678 Buffer BUFFER is in `occur-mode'.
2679 If ADD-LINENUM is non-nil the message is preceded by the line number.
2680 If OCCUR-POINT is non-nil then the line is marked as a new occurrence
2681 so that `occur-next' and `occur-prev' will work."
2682 (let ((m1 (make-marker))
2683 start
2684 (line ""))
2685 (when point
2686 (set-marker m1 point (current-buffer))
2687 (if add-linenum
2688 (setq line (format "%d: " (1+ (count-lines 1 point))))))
2689 (save-excursion
2690 (if (get-buffer buffer)
2691 (set-buffer (get-buffer buffer))
2692 (set-buffer (get-buffer-create buffer))
2693 (occur-mode)
2694 )
2695 (goto-char (point-max))
2696 (setq start (point))
2697 (insert line)
2698 (if occur-point
2699 (setq occur-point (point)))
2700 (insert message)
2701 (if point
2702 (add-text-properties
2703 start (point)
2704 '(mouse-face highlight
2705 help-echo "mouse-2: go to the line where I learned this")))
2706 (insert "\n")
2707 (if point
2708 (progn
2709 (put-text-property start (point) 'occur-target m1)
2710 (if occur-point
2711 (put-text-property start occur-point
2712 'occur-match t))
2713 ))
2714 )))
2715
2716
2717
2718 ;; Is this really worth having?
2719 (defvar sh-learned-buffer-hook nil
2720 "*An abnormal hook, called with an alist of learned variables.")
2721 ;; Example of how to use sh-learned-buffer-hook
2722 ;;
2723 ;; (defun what-i-learned (list)
2724 ;; (let ((p list))
2725 ;; (save-excursion
2726 ;; (set-buffer "*scratch*")
2727 ;; (goto-char (point-max))
2728 ;; (insert "(setq\n")
2729 ;; (while p
2730 ;; (insert (format " %s %s \n"
2731 ;; (nth 0 (car p)) (nth 1 (car p))))
2732 ;; (setq p (cdr p)))
2733 ;; (insert ")\n")
2734 ;; )))
2735 ;;
2736 ;; (add-hook 'sh-learned-buffer-hook 'what-i-learned)
2737
2738
2739 ;; Originally this was sh-learn-region-indent (beg end)
2740 ;; However, in practice this was awkward so I changed it to
2741 ;; use the whole buffer. Use narrowing if needbe.
2742 (defun sh-learn-buffer-indent (&optional arg)
2743 "Learn how to indent the buffer the way it currently is.
2744
2745 Output in buffer \"*indent*\" shows any lines which have conflicting
2746 values of a variable, and the final value of all variables learned.
2747 This buffer is popped to automatically if there are any discrepancies.
2748
2749 If no prefix ARG is given, then variables are set to numbers.
2750 If a prefix arg is given, then variables are set to symbols when
2751 applicable -- e.g. to symbol `+' if the value is that of the
2752 basic indent.
2753 If a positive numerical prefix is given, then `sh-basic-offset'
2754 is set to the prefix's numerical value.
2755 Otherwise, sh-basic-offset may or may not be changed, according
2756 to the value of variable `sh-learn-basic-offset'.
2757
2758 Abnormal hook `sh-learned-buffer-hook' if non-nil is called when the
2759 function completes. The function is abnormal because it is called
2760 with an alist of variables learned. This feature may be changed or
2761 removed in the future.
2762
2763 This command can often take a long time to run."
2764 (interactive "P")
2765 (sh-must-support-indent)
2766 (save-excursion
2767 (goto-char (point-min))
2768 (let ((learned-var-list nil)
2769 (out-buffer "*indent*")
2770 (num-diffs 0)
2771 previous-set-info
2772 (max 17)
2773 vec
2774 msg
2775 (comment-col nil) ;; number if all same, t if seen diff values
2776 (comments-always-default t) ;; nil if we see one not default
2777 initial-msg
2778 (specified-basic-offset (and arg (numberp arg)
2779 (> arg 0)))
2780 (linenum 0)
2781 suggested)
2782 (setq vec (make-vector max 0))
2783 (sh-mark-init out-buffer)
2784
2785 (if specified-basic-offset
2786 (progn
2787 (setq sh-basic-offset arg)
2788 (setq initial-msg
2789 (format "Using specified sh-basic-offset of %d"
2790 sh-basic-offset)))
2791 (setq initial-msg
2792 (format "Initial value of sh-basic-offset: %s"
2793 sh-basic-offset)))
2794
2795 (while (< (point) (point-max))
2796 (setq linenum (1+ linenum))
2797 ;; (if (zerop (% linenum 10))
2798 (message "line %d" linenum)
2799 ;; )
2800 (unless (looking-at "\\s-*$") ;; ignore empty lines!
2801 (let* ((sh-indent-comment t) ;; info must return default indent
2802 (info (sh-get-indent-info))
2803 (var (sh-get-indent-var-for-line info))
2804 sval ival diff new-val
2805 (curr-indent (current-indentation)))
2806 (cond
2807 ((null var)
2808 nil)
2809 ((stringp var)
2810 nil)
2811 ((numberp (setq sval (sh-var-value var 'no-error)))
2812 ;; the numberp excludes comments since sval will be t.
2813 (setq ival (sh-calculate-indent))
2814 (setq diff (- curr-indent ival))
2815 (setq new-val (+ sval diff))
2816 (sh-set-var-value var new-val 'no-symbol)
2817 (unless (looking-at "\\s-*#") ;; don't learn from comments
2818 (if (setq previous-set-info (assoc var learned-var-list))
2819 (progn
2820 ;; it was already there, is it same value ?
2821 (unless (eq (symbol-value var)
2822 (nth 1 previous-set-info))
2823 (sh-mark-line
2824 (format "Variable %s was set to %s"
2825 var (symbol-value var))
2826 (point) out-buffer t t)
2827 (sh-mark-line
2828 (format " but was previously set to %s"
2829 (nth 1 previous-set-info))
2830 (nth 2 previous-set-info) out-buffer t)
2831 (setq num-diffs (1+ num-diffs))
2832 ;; (delete previous-set-info learned-var-list)
2833 (setcdr previous-set-info
2834 (list (symbol-value var) (point)))
2835 )
2836 )
2837 (setq learned-var-list
2838 (append (list (list var (symbol-value var)
2839 (point)))
2840 learned-var-list)))
2841 (if (numberp new-val)
2842 (progn
2843 (sh-debug
2844 "This line's indent value: %d" new-val)
2845 (if (< new-val 0)
2846 (setq new-val (- new-val)))
2847 (if (< new-val max)
2848 (aset vec new-val (1+ (aref vec new-val))))))
2849 ))
2850 ((eq var 'sh-indent-comment)
2851 (unless (= curr-indent (sh-calculate-indent info))
2852 ;; this is not the default indentation
2853 (setq comments-always-default nil)
2854 (if comment-col ;; then we have see one before
2855 (or (eq comment-col curr-indent)
2856 (setq comment-col t)) ;; seen a different one
2857 (setq comment-col curr-indent))
2858 ))
2859 (t
2860 (sh-debug "Cannot learn this line!!!")
2861 ))
2862 (sh-debug
2863 "at %s learned-var-list is %s" (point) learned-var-list)
2864 ))
2865 (forward-line 1)
2866 ) ;; while
2867 (if sh-debug
2868 (progn
2869 (setq msg (format
2870 "comment-col = %s comments-always-default = %s"
2871 comment-col comments-always-default))
2872 ;; (message msg)
2873 (sh-mark-line msg nil out-buffer)))
2874 (cond
2875 ((eq comment-col 0)
2876 (setq msg "\nComments are all in 1st column.\n"))
2877 (comments-always-default
2878 (setq msg "\nComments follow default indentation.\n")
2879 (setq comment-col t))
2880 ((numberp comment-col)
2881 (setq msg (format "\nComments are in col %d." comment-col)))
2882 (t
2883 (setq msg "\nComments seem to be mixed, leaving them as is.\n")
2884 (setq comment-col nil)
2885 ))
2886 (sh-debug msg)
2887 (sh-mark-line msg nil out-buffer)
2888
2889 (sh-mark-line initial-msg nil out-buffer t t)
2890
2891 (setq suggested (sh-guess-basic-offset vec))
2892
2893 (if (and suggested (not specified-basic-offset))
2894 (let ((new-value
2895 (cond
2896 ;; t => set it if we have a single value as a number
2897 ((and (eq sh-learn-basic-offset t) (numberp suggested))
2898 suggested)
2899 ;; other non-nil => set it if only one value was found
2900 (sh-learn-basic-offset
2901 (if (numberp suggested)
2902 suggested
2903 (if (= (length suggested) 1)
2904 (car suggested))))
2905 (t
2906 nil))))
2907 (if new-value
2908 (progn
2909 (setq learned-var-list
2910 (append (list (list 'sh-basic-offset
2911 (setq sh-basic-offset new-value)
2912 (point-max)))
2913 learned-var-list))
2914 ;; Not sure if we need to put this line in, since
2915 ;; it will appear in the "Learned variable settings".
2916 (sh-mark-line
2917 (format "Changed sh-basic-offset to: %d" sh-basic-offset)
2918 nil out-buffer))
2919 (sh-mark-line
2920 (if (listp suggested)
2921 (format "Possible value(s) for sh-basic-offset: %s"
2922 (mapconcat 'int-to-string suggested " "))
2923 (format "Suggested sh-basic-offset: %d" suggested))
2924 nil out-buffer))))
2925
2926
2927 (setq learned-var-list
2928 (append (list (list 'sh-indent-comment comment-col (point-max)))
2929 learned-var-list))
2930 (setq sh-indent-comment comment-col)
2931 (let ((name (buffer-name)))
2932 (sh-mark-line "\nLearned variable settings:" nil out-buffer)
2933 (if arg
2934 ;; Set learned variables to symbolic rather than numeric
2935 ;; values where possible.
2936 (dolist (learned-var (reverse learned-var-list))
2937 (let ((var (car learned-var))
2938 (val (nth 1 learned-var)))
2939 (when (and (not (eq var 'sh-basic-offset))
2940 (numberp val))
2941 (sh-set-var-value var val)))))
2942 (dolist (learned-var (reverse learned-var-list))
2943 (let ((var (car learned-var)))
2944 (sh-mark-line (format " %s %s" var (symbol-value var))
2945 (nth 2 learned-var) out-buffer)))
2946 (save-excursion
2947 (set-buffer out-buffer)
2948 (goto-char (point-min))
2949 (insert
2950 (format "Indentation values for buffer %s.\n" name)
2951 (format "%d indentation variable%s different values%s\n\n"
2952 num-diffs
2953 (if (= num-diffs 1)
2954 " has" "s have")
2955 (if (zerop num-diffs)
2956 "." ":"))
2957 )))
2958 ;; Are abnormal hooks considered bad form?
2959 (run-hook-with-args 'sh-learned-buffer-hook learned-var-list)
2960 (if (or sh-popup-occur-buffer (> num-diffs 0))
2961 (pop-to-buffer out-buffer))
2962 )))
2963
2964 (defun sh-guess-basic-offset (vec)
2965 "See if we can determine a reasonable value for `sh-basic-offset'.
2966 This is experimental, heuristic and arbitrary!
2967 Argument VEC is a vector of information collected by
2968 `sh-learn-buffer-indent'.
2969 Return values:
2970 number - there appears to be a good single value
2971 list of numbers - no obvious one, here is a list of one or more
2972 reasonable choices
2973 nil - we couldn't find a reasonable one."
2974 (let* ((max (1- (length vec)))
2975 (i 1)
2976 (totals (make-vector max 0)))
2977 (while (< i max)
2978 (aset totals i (+ (aref totals i) (* 4 (aref vec i))))
2979 (if (zerop (% i 2))
2980 (aset totals i (+ (aref totals i) (aref vec (/ i 2)))))
2981 (if (< (* i 2) max)
2982 (aset totals i (+ (aref totals i) (aref vec (* i 2)))))
2983 (setq i (1+ i)))
2984
2985 (let ((x nil)
2986 (result nil)
2987 tot sum p)
2988 (setq i 1)
2989 (while (< i max)
2990 (if (/= (aref totals i) 0)
2991 (setq x (append x (list (cons i (aref totals i))))))
2992 (setq i (1+ i)))
2993
2994 (setq x (sort x (lambda (a b) (> (cdr a) (cdr b)))))
2995 (setq tot (apply '+ (append totals nil)))
2996 (sh-debug (format "vec: %s\ntotals: %s\ntot: %d"
2997 vec totals tot))
2998 (cond
2999 ((zerop (length x))
3000 (message "no values!")) ;; we return nil
3001 ((= (length x) 1)
3002 (message "only value is %d" (car (car x)))
3003 (setq result (car (car x)))) ;; return single value
3004 ((> (cdr (car x)) (/ tot 2))
3005 ;; 1st is > 50%
3006 (message "basic-offset is probably %d" (car (car x)))
3007 (setq result (car (car x)))) ;; again, return a single value
3008 ((>= (cdr (car x)) (* 2 (cdr (car (cdr x)))))
3009 ;; 1st is >= 2 * 2nd
3010 (message "basic-offset could be %d" (car (car x)))
3011 (setq result (car (car x))))
3012 ((>= (+ (cdr (car x))(cdr (car (cdr x)))) (/ tot 2))
3013 ;; 1st & 2nd together >= 50% - return a list
3014 (setq p x sum 0 result nil)
3015 (while (and p
3016 (<= (setq sum (+ sum (cdr (car p)))) (/ tot 2)))
3017 (setq result (append result (list (car (car p)))))
3018 (setq p (cdr p)))
3019 (message "Possible choices for sh-basic-offset: %s"
3020 (mapconcat 'int-to-string result " ")))
3021 (t
3022 (message "No obvious value for sh-basic-offset. Perhaps %d"
3023 (car (car x)))
3024 ;; result is nil here
3025 ))
3026 result)))
3027
3028 ;; ========================================================================
3029
3030 ;; Styles -- a quick and dirty way of saving the indentation settings.
3031
3032 (defvar sh-styles-alist nil
3033 "A list of all known shell indentation styles.")
3034
3035 (defun sh-name-style (name &optional confirm-overwrite)
3036 "Name the current indentation settings as a style called NAME.
3037 If this name exists, the command will prompt whether it should be
3038 overwritten if
3039 - - it was called interactively with a prefix argument, or
3040 - - called non-interactively with optional CONFIRM-OVERWRITE non-nil."
3041 ;; (interactive "sName for this style: ")
3042 (interactive
3043 (list
3044 (read-from-minibuffer "Name for this style? " )
3045 (not current-prefix-arg)))
3046 (let ((slist (cons name
3047 (mapcar (lambda (var) (cons var (symbol-value var)))
3048 sh-var-list)))
3049 (style (assoc name sh-styles-alist)))
3050 (if style
3051 (if (and confirm-overwrite
3052 (not (y-or-n-p "This style exists. Overwrite it? ")))
3053 (message "Not changing style %s" name)
3054 (message "Updating style %s" name)
3055 (setcdr style (cdr slist)))
3056 (message "Creating new style %s" name)
3057 (push slist sh-styles-alist))))
3058
3059 (defun sh-load-style (name)
3060 "Set shell indentation values for this buffer from those in style NAME."
3061 (interactive (list (completing-read
3062 "Which style to use for this buffer? "
3063 sh-styles-alist nil t)))
3064 (let ((sl (assoc name sh-styles-alist)))
3065 (if (null sl)
3066 (error "sh-load-style - style %s not known" name)
3067 (dolist (var (cdr sl))
3068 (set (car var) (cdr var))))))
3069
3070 (defun sh-save-styles-to-buffer (buff)
3071 "Save all current styles in elisp to buffer BUFF.
3072 This is always added to the end of the buffer."
3073 (interactive (list
3074 (read-from-minibuffer "Buffer to save styles in? " "*scratch*")))
3075 (with-current-buffer (get-buffer-create buff)
3076 (goto-char (point-max))
3077 (insert "\n")
3078 (pp `(setq sh-styles-alist ',sh-styles-alist) (current-buffer))))
3079
3080
3081 \f
3082 ;; statement syntax-commands for various shells
3083
3084 ;; You are welcome to add the syntax or even completely new statements as
3085 ;; appropriate for your favorite shell.
3086
3087 (defconst sh-non-closing-paren
3088 ;; If we leave it rear-sticky, calling `newline' ends up inserting a \n
3089 ;; that inherits this property, which then confuses the indentation.
3090 (propertize ")" 'syntax-table sh-st-punc 'rear-nonsticky t))
3091
3092 (define-skeleton sh-case
3093 "Insert a case/switch statement. See `sh-feature'."
3094 (csh "expression: "
3095 "switch( " str " )" \n
3096 > "case " (read-string "pattern: ") ?: \n
3097 > _ \n
3098 "breaksw" \n
3099 ( "other pattern, %s: "
3100 < "case " str ?: \n
3101 > _ \n
3102 "breaksw" \n)
3103 < "default:" \n
3104 > _ \n
3105 resume:
3106 < < "endsw" \n)
3107 (es)
3108 (rc "expression: "
3109 > "switch( " str " ) {" \n
3110 > "case " (read-string "pattern: ") \n
3111 > _ \n
3112 ( "other pattern, %s: "
3113 "case " str > \n
3114 > _ \n)
3115 "case *" > \n
3116 > _ \n
3117 resume:
3118 ?\} > \n)
3119 (sh "expression: "
3120 > "case " str " in" \n
3121 ( "pattern, %s: "
3122 > str sh-non-closing-paren \n
3123 > _ \n
3124 ";;" \n)
3125 > "*" sh-non-closing-paren \n
3126 > _ \n
3127 resume:
3128 "esac" > \n))
3129
3130 (define-skeleton sh-for
3131 "Insert a for loop. See `sh-feature'."
3132 (csh sh-modify sh
3133 1 ""
3134 2 "foreach "
3135 4 " ( "
3136 6 " )"
3137 15 '<
3138 16 "end")
3139 (es sh-modify rc
3140 4 " = ")
3141 (rc sh-modify sh
3142 2 "for( "
3143 6 " ) {"
3144 15 ?\} )
3145 (sh "Index variable: "
3146 > "for " str " in " _ "; do" \n
3147 > _ | ?$ & (sh-remember-variable str) \n
3148 "done" > \n))
3149
3150
3151
3152 (define-skeleton sh-indexed-loop
3153 "Insert an indexed loop from 1 to n. See `sh-feature'."
3154 (bash sh-modify posix)
3155 (csh "Index variable: "
3156 "@ " str " = 1" \n
3157 "while( $" str " <= " (read-string "upper limit: ") " )" \n
3158 > _ ?$ str \n
3159 "@ " str "++" \n
3160 < "end" \n)
3161 (es sh-modify rc
3162 4 " =")
3163 (ksh88 "Index variable: "
3164 > "integer " str "=0" \n
3165 > "while (( ( " str " += 1 ) <= "
3166 (read-string "upper limit: ")
3167 " )); do" \n
3168 > _ ?$ (sh-remember-variable str) > \n
3169 "done" > \n)
3170 (posix "Index variable: "
3171 > str "=1" \n
3172 "while [ $" str " -le "
3173 (read-string "upper limit: ")
3174 " ]; do" \n
3175 > _ ?$ str \n
3176 str ?= (sh-add (sh-remember-variable str) 1) \n
3177 "done" > \n)
3178 (rc "Index variable: "
3179 > "for( " str " in" " `{awk 'BEGIN { for( i=1; i<="
3180 (read-string "upper limit: ")
3181 "; i++ ) print i }'`}) {" \n
3182 > _ ?$ (sh-remember-variable str) \n
3183 ?\} > \n)
3184 (sh "Index variable: "
3185 > "for " str " in `awk 'BEGIN { for( i=1; i<="
3186 (read-string "upper limit: ")
3187 "; i++ ) print i }'`; do" \n
3188 > _ ?$ (sh-remember-variable str) \n
3189 "done" > \n))
3190
3191
3192 (defun sh-shell-initialize-variables ()
3193 "Scan the buffer for variable assignments.
3194 Add these variables to `sh-shell-variables'."
3195 (message "Scanning buffer `%s' for variable assignments..." (buffer-name))
3196 (save-excursion
3197 (goto-char (point-min))
3198 (setq sh-shell-variables-initialized t)
3199 (while (search-forward "=" nil t)
3200 (sh-assignment 0)))
3201 (message "Scanning buffer `%s' for variable assignments...done"
3202 (buffer-name)))
3203
3204 (defvar sh-add-buffer)
3205
3206 (defun sh-add-completer (string predicate code)
3207 "Do completion using `sh-shell-variables', but initialize it first.
3208 This function is designed for use as the \"completion table\",
3209 so it takes three arguments:
3210 STRING, the current buffer contents;
3211 PREDICATE, the predicate for filtering possible matches;
3212 CODE, which says what kind of things to do.
3213 CODE can be nil, t or `lambda'.
3214 nil means to return the best completion of STRING, or nil if there is none.
3215 t means to return a list of all possible completions of STRING.
3216 `lambda' means to return t if STRING is a valid completion as it stands."
3217 (let ((sh-shell-variables
3218 (save-excursion
3219 (set-buffer sh-add-buffer)
3220 (or sh-shell-variables-initialized
3221 (sh-shell-initialize-variables))
3222 (nconc (mapcar (lambda (var)
3223 (let ((name
3224 (substring var 0 (string-match "=" var))))
3225 (cons name name)))
3226 process-environment)
3227 sh-shell-variables))))
3228 (case code
3229 ((nil) (try-completion string sh-shell-variables predicate))
3230 (lambda (test-completion string sh-shell-variables predicate))
3231 (t (all-completions string sh-shell-variables predicate)))))
3232
3233 (defun sh-add (var delta)
3234 "Insert an addition of VAR and prefix DELTA for Bourne (type) shell."
3235 (interactive
3236 (let ((sh-add-buffer (current-buffer)))
3237 (list (completing-read "Variable: " 'sh-add-completer)
3238 (prefix-numeric-value current-prefix-arg))))
3239 (insert (sh-feature '((bash . "$[ ")
3240 (ksh88 . "$(( ")
3241 (posix . "$(( ")
3242 (rc . "`{expr $")
3243 (sh . "`expr $")
3244 (zsh . "$[ ")))
3245 (sh-remember-variable var)
3246 (if (< delta 0) " - " " + ")
3247 (number-to-string (abs delta))
3248 (sh-feature '((bash . " ]")
3249 (ksh88 . " ))")
3250 (posix . " ))")
3251 (rc . "}")
3252 (sh . "`")
3253 (zsh . " ]")))))
3254
3255
3256
3257 (define-skeleton sh-function
3258 "Insert a function definition. See `sh-feature'."
3259 (bash sh-modify ksh88
3260 3 "() {")
3261 (ksh88 "name: "
3262 "function " str " {" \n
3263 > _ \n
3264 < "}" \n)
3265 (rc sh-modify ksh88
3266 1 "fn ")
3267 (sh ()
3268 "() {" \n
3269 > _ \n
3270 < "}" \n))
3271
3272
3273
3274 (define-skeleton sh-if
3275 "Insert an if statement. See `sh-feature'."
3276 (csh "condition: "
3277 "if( " str " ) then" \n
3278 > _ \n
3279 ( "other condition, %s: "
3280 < "else if( " str " ) then" \n
3281 > _ \n)
3282 < "else" \n
3283 > _ \n
3284 resume:
3285 < "endif" \n)
3286 (es "condition: "
3287 > "if { " str " } {" \n
3288 > _ \n
3289 ( "other condition, %s: "
3290 "} { " str " } {" > \n
3291 > _ \n)
3292 "} {" > \n
3293 > _ \n
3294 resume:
3295 ?\} > \n)
3296 (rc "condition: "
3297 > "if( " str " ) {" \n
3298 > _ \n
3299 ( "other condition, %s: "
3300 "} else if( " str " ) {" > \n
3301 > _ \n)
3302 "} else {" > \n
3303 > _ \n
3304 resume:
3305 ?\} > \n)
3306 (sh "condition: "
3307 '(setq input (sh-feature sh-test))
3308 > "if " str "; then" \n
3309 > _ \n
3310 ( "other condition, %s: "
3311 > "elif " str "; then" > \n
3312 > \n)
3313 "else" > \n
3314 > \n
3315 resume:
3316 "fi" > \n))
3317
3318
3319
3320 (define-skeleton sh-repeat
3321 "Insert a repeat loop definition. See `sh-feature'."
3322 (es nil
3323 > "forever {" \n
3324 > _ \n
3325 ?\} > \n)
3326 (zsh "factor: "
3327 > "repeat " str "; do" > \n
3328 > \n
3329 "done" > \n))
3330
3331 ;;;(put 'sh-repeat 'menu-enable '(sh-feature sh-repeat))
3332
3333
3334
3335 (define-skeleton sh-select
3336 "Insert a select statement. See `sh-feature'."
3337 (ksh88 "Index variable: "
3338 > "select " str " in " _ "; do" \n
3339 > ?$ str \n
3340 "done" > \n)
3341 (bash sh-append ksh88))
3342 ;;;(put 'sh-select 'menu-enable '(sh-feature sh-select))
3343
3344
3345
3346 (define-skeleton sh-tmp-file
3347 "Insert code to setup temporary file handling. See `sh-feature'."
3348 (bash sh-append ksh88)
3349 (csh (file-name-nondirectory (buffer-file-name))
3350 "set tmp = /tmp/" str ".$$" \n
3351 "onintr exit" \n _
3352 (and (goto-char (point-max))
3353 (not (bolp))
3354 ?\n)
3355 "exit:\n"
3356 "rm $tmp* >&/dev/null" > \n)
3357 (es (file-name-nondirectory (buffer-file-name))
3358 > "local( signals = $signals sighup sigint; tmp = /tmp/" str
3359 ".$pid ) {" \n
3360 > "catch @ e {" \n
3361 > "rm $tmp^* >[2]/dev/null" \n
3362 "throw $e" \n
3363 "} {" > \n
3364 _ \n
3365 ?\} > \n
3366 ?\} > \n)
3367 (ksh88 sh-modify sh
3368 7 "EXIT")
3369 (rc (file-name-nondirectory (buffer-file-name))
3370 > "tmp = /tmp/" str ".$pid" \n
3371 "fn sigexit { rm $tmp^* >[2]/dev/null }" \n)
3372 (sh (file-name-nondirectory (buffer-file-name))
3373 > "TMP=${TMPDIR:-/tmp}/" str ".$$" \n
3374 "trap \"rm $TMP* 2>/dev/null\" " ?0 \n))
3375
3376
3377
3378 (define-skeleton sh-until
3379 "Insert an until loop. See `sh-feature'."
3380 (sh "condition: "
3381 '(setq input (sh-feature sh-test))
3382 > "until " str "; do" \n
3383 > _ \n
3384 "done" > \n))
3385 ;;;(put 'sh-until 'menu-enable '(sh-feature sh-until))
3386
3387
3388
3389 (define-skeleton sh-while
3390 "Insert a while loop. See `sh-feature'."
3391 (csh sh-modify sh
3392 2 ""
3393 3 "while( "
3394 5 " )"
3395 10 '<
3396 11 "end")
3397 (es sh-modify sh
3398 3 "while { "
3399 5 " } {"
3400 10 ?\} )
3401 (rc sh-modify sh
3402 3 "while( "
3403 5 " ) {"
3404 10 ?\} )
3405 (sh "condition: "
3406 '(setq input (sh-feature sh-test))
3407 > "while " str "; do" \n
3408 > _ \n
3409 "done" > \n))
3410
3411
3412
3413 (define-skeleton sh-while-getopts
3414 "Insert a while getopts loop. See `sh-feature'.
3415 Prompts for an options string which consists of letters for each recognized
3416 option followed by a colon `:' if the option accepts an argument."
3417 (bash sh-modify sh
3418 18 "${0##*/}")
3419 (csh nil
3420 "while( 1 )" \n
3421 > "switch( \"$1\" )" \n
3422 '(setq input '("- x" . 2))
3423 > >
3424 ( "option, %s: "
3425 < "case " '(eval str)
3426 '(if (string-match " +" str)
3427 (setq v1 (substring str (match-end 0))
3428 str (substring str 0 (match-beginning 0)))
3429 (setq v1 nil))
3430 str ?: \n
3431 > "set " v1 & " = $2" | -4 & _ \n
3432 (if v1 "shift") & \n
3433 "breaksw" \n)
3434 < "case --:" \n
3435 > "shift" \n
3436 < "default:" \n
3437 > "break" \n
3438 resume:
3439 < < "endsw" \n
3440 "shift" \n
3441 < "end" \n)
3442 (ksh88 sh-modify sh
3443 16 "print"
3444 18 "${0##*/}"
3445 37 "OPTIND-1")
3446 (posix sh-modify sh
3447 18 "$(basename $0)")
3448 (sh "optstring: "
3449 > "while getopts :" str " OPT; do" \n
3450 > "case $OPT in" \n
3451 '(setq v1 (append (vconcat str) nil))
3452 ( (prog1 (if v1 (char-to-string (car v1)))
3453 (if (eq (nth 1 v1) ?:)
3454 (setq v1 (nthcdr 2 v1)
3455 v2 "\"$OPTARG\"")
3456 (setq v1 (cdr v1)
3457 v2 nil)))
3458 > str "|+" str sh-non-closing-paren \n
3459 > _ v2 \n
3460 > ";;" \n)
3461 > "*" sh-non-closing-paren \n
3462 > "echo" " \"usage: " "`basename $0`"
3463 " [+-" '(setq v1 (point)) str
3464 '(save-excursion
3465 (while (search-backward ":" v1 t)
3466 (replace-match " ARG] [+-" t t)))
3467 (if (eq (preceding-char) ?-) -5)
3468 (if (and (sequencep v1) (length v1)) "] " "} ")
3469 "[--] ARGS...\"" \n
3470 "exit 2" > \n
3471 "esac" >
3472 \n "done"
3473 > \n
3474 "shift " (sh-add "OPTIND" -1) \n))
3475
3476
3477
3478 (defun sh-assignment (arg)
3479 "Remember preceding identifier for future completion and do self-insert."
3480 (interactive "p")
3481 (self-insert-command arg)
3482 (if (<= arg 1)
3483 (sh-remember-variable
3484 (save-excursion
3485 (if (re-search-forward (sh-feature sh-assignment-regexp)
3486 (prog1 (point)
3487 (beginning-of-line 1))
3488 t)
3489 (match-string 1))))))
3490
3491
3492
3493 (defun sh-maybe-here-document (arg)
3494 "Insert self. Without prefix, following unquoted `<' inserts here document.
3495 The document is bounded by `sh-here-document-word'."
3496 (interactive "*P")
3497 (self-insert-command (prefix-numeric-value arg))
3498 (or arg
3499 (not (eq (char-after (- (point) 2)) last-command-char))
3500 (save-excursion
3501 (backward-char 2)
3502 (sh-quoted-p))
3503 (progn
3504 (insert sh-here-document-word)
3505 (or (eolp) (looking-at "[ \t]") (insert ? ))
3506 (end-of-line 1)
3507 (while
3508 (sh-quoted-p)
3509 (end-of-line 2))
3510 (newline)
3511 (save-excursion
3512 (insert ?\n (substring
3513 sh-here-document-word
3514 (if (string-match "^-" sh-here-document-word) 1 0)))))))
3515
3516 \f
3517 ;; various other commands
3518
3519 (autoload 'comint-dynamic-complete "comint"
3520 "Dynamically perform completion at point." t)
3521
3522 (autoload 'shell-dynamic-complete-command "shell"
3523 "Dynamically complete the command at point." t)
3524
3525 (autoload 'comint-dynamic-complete-filename "comint"
3526 "Dynamically complete the filename at point." t)
3527
3528 (autoload 'shell-dynamic-complete-environment-variable "shell"
3529 "Dynamically complete the environment variable at point." t)
3530
3531
3532
3533 (defun sh-newline-and-indent ()
3534 "Strip unquoted whitespace, insert newline, and indent like current line."
3535 (interactive "*")
3536 (indent-to (prog1 (current-indentation)
3537 (delete-region (point)
3538 (progn
3539 (or (zerop (skip-chars-backward " \t"))
3540 (if (sh-quoted-p)
3541 (forward-char)))
3542 (point)))
3543 (newline))))
3544
3545 (defun sh-beginning-of-command ()
3546 "Move point to successive beginnings of commands."
3547 (interactive)
3548 (if (re-search-backward sh-beginning-of-command nil t)
3549 (goto-char (match-beginning 2))))
3550
3551 (defun sh-end-of-command ()
3552 "Move point to successive ends of commands."
3553 (interactive)
3554 (if (re-search-forward sh-end-of-command nil t)
3555 (goto-char (match-end 1))))
3556
3557 (provide 'sh-script)
3558
3559 ;;; arch-tag: eccd8b72-f337-4fc2-ae86-18155a69d937
3560 ;;; sh-script.el ends here