1 ;; ksh-mode.el --- sh (ksh, bash) script editing mode for GNU Emacs.
3 ;; Copyright (C) 1992-1999 Gary Ellison.
5 ;; This file is compatible with GNU Emacs but is not part of the official
8 ;; This program is free software; you can redistribute it and/or modify
11 ;; Gary Ellison makes no representations about the suitability
12 ;; of this software for any purpose. It is provided "as is" without
13 ;; express or implied warranty.
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 ;; $Source: /usr/CVSroot/XEmacs/packages/xemacs-packages/prog-modes/ksh-mode.el,v $ --
22 ;; ksh-mode|Gary Ellison|gfe@interhack.net
23 ;; |Mode for editing sh/ksh/bash scripts
24 ;; |$Date: 2000/10/06 09:12:14 $|$Revision: 1.4 $|~/modes/ksh-mode.el.Z|
26 ;; Author: Gary Ellison <gfe@interhack.net>
29 ;; Maintainer: Gary Ellison <gfe@interhack.net>
30 ;; Created: Fri Jun 19
32 ;; Keywords: shell, korn, bourne, sh, ksh, bash
34 ;; Delta On $Date: 2000/10/06 09:12:14 $
35 ;; Last Modified By: Gary Ellison
36 ;; Status : Highly Functional
43 ;; sh, ksh, and bash script editing commands for emacs.
46 ;; Put ksh-mode.el in some directory in your load-path.
47 ;; Refer to the installation section of ksh-mode's function definition.
50 ;; This major mode assists shell script writers with indentation
51 ;; control and control structure construct matching in much the same
52 ;; fashion as other programming language modes. Invoke describe-mode
53 ;; for more information.
56 ;; When the ksh-align-to-keyword is non-nil and the nester
57 ;; is a multi-command expression with a compound command
58 ;; the lines following the compound end will align incorrectly
59 ;; to the compound command instead of it's current indentation.
60 ;; The fix will probably require the detection of syntax elements
61 ;; in the nesting line.
63 ;; Function ending brace "}" must be on a separate line for indent-line
64 ;; to do the right thing.
66 ;; Explicit function definition matching will proclaim in the minibuffer
67 ;; "No matching compound command" followed by "Matched ... "
69 ;; indent-for-comment fails to recognize a comment starting in column 0,
70 ;; hence it moves the comment-start in comment-column.
74 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
77 ;; 13-May-99 Gary Ellison
78 ;; Removed some testing baggage which leaked into previous release.
80 ;; 29-Apr-99 Gary Ellison
81 ;; I moved and forgot to submit a forwarding address to the codeoffice.
82 ;; Minor mucks with font-lock.
84 ;; 28-Feb-97 Kevin Blake <kblake@nortel.ca>
85 ;; font-lock enhancements
87 ;; 4-Feb-97 Jim Berry <jberry@FRB.GOV>
88 ;; Converted from use of font-lock-keywords to font-lock-defaults.
90 ;; 6-Apr-96 Gary Ellison <gary.f.ellison@att.com>
91 ;; Depreciated font-lock-doc-string-face.
92 ;; Narly keywords inside strings bug fixed.
94 ;; 8-Aug-95 Jack Repenning <jackr@sgi.com>
95 ;; Fix documentation of `ksh-align-to-keyword' to conform to the 23
96 ;; Feb default change. Search for keywords obeying case, since the
99 ;; 23-Feb-1995 Gary Ellison
100 ;; Merged Jonathan Stigelman <Stig@hackvan.com> into 2.5 souce.
102 ;; 23 Feb 1995 Jonathan Stigelman <Stig@hackvan.com>
103 ;; Reshuffled documentation to make the format more consistant with other
104 ;; elisp. Added autoload and removed autoloading instructions from the
105 ;; ksh-mode docstring. Changed default value for `ksh-align-to-keyword'
106 ;; to nil because it doesn't work properly.
108 ;; 2-Aug-1994 Gary Ellison
109 ;; Last Modified: Mon Jun 13 16:52:55 1994 #29 (Gary Ellison)
110 ;; - Syntax table modifications to better support sexp navigation and
112 ;; - Fixed keyword regexps. Keywords were not being recoginized on the
113 ;; same line as " ' `.
115 ;; 13-Jun-1994 Gary Ellison
116 ;; Last Modified: Wed Mar 30 14:12:26 1994 #28 (Gary Ellison)
117 ;; - Minor excursion problem fixed in ksh-indent-command.
119 ;; 30-Mar-1994 Gary Ellison
120 ;; Last Modified: Fri Mar 25 15:42:29 1994 #25 (Gary Ellison)
121 ;; - Implement user customizable ksh-comment-regexp.
122 ;; - Make the keyword vs line indentation alignment customizable
123 ;; by calling ksh-align-to-keyword based on variable of same
124 ;; name. (If the code is obfuscated or convoluted I can attribute
125 ;; this to a severe head cold and not malice :)
127 ;; 25-Mar-1994 Gary Ellison
128 ;; Last Modified: Fri Feb 4 13:06:30 1994 #23 (Gary Ellison)
129 ;; - Nest relative to the line indentation not the keywords
132 ;; 4-Feb-1994 Gary Ellison
133 ;; Last Modified: Wed Nov 10 10:03:01 1993 #18 (Gary Ellison)
134 ;; - Add direct support for font-lock-mode. Thanks Espen Skoglund
135 ;; for the regular expressions.
137 ;; 10-Nov-1993 Gary Ellison
138 ;; Last Modified: Tue Oct 12 15:23:06 1993 #17 (Gary Ellison)
139 ;; Fix message on ksh-match-and-tell to not get invalid format
140 ;; when a % appears in the string.
142 ;; 12-Oct-1993 Espen Skoglund <espensk@stud.cs.uit.no>.
143 ;; Last Modified: Tue Oct 12 15:03:01 1993 #16 (Gary Ellison)
144 ;; Apply Line continuation patch supplied by Espen Skoglund
146 ;; 1-Sep-1993 Gary Ellison
147 ;; Last Modified: Tue Aug 17 17:18:18 1993 #14 (Gary Ellison)
148 ;; Get rid of this-line hack in ksh-get-nester-column.
150 ;; 17-Aug-1993 Gary Ellison
151 ;; Last Modified: Mon Jun 21 14:00:43 1993 #13 (Gary Ellison)
152 ;; Code uses builtin current-indentation instead of lisp defun
153 ;; ksh-indentation-on-this-line (thanks to Tom Tromey).
154 ;; More and better doc strings.
156 ;; 5-Aug-1993 Tom Tromey <tromey@cns.caltech.edu>
157 ;; Last Modified: Thu Aug 5 11:09:12 1993 #12 (Tom Tromey)
158 ;; ksh-indent-region skips blank lines. Uses let binding instead
159 ;; of setq. No longer marks buffer modified if indentation
162 ;; 21-Jun-1993 Gary Ellison
163 ;; Last Modified: Mon Mar 29 15:05:34 1993 #11 (Gary Ellison)
164 ;; Use make-local-variables instead of make-variables-buffer-local
165 ;; ksh-indent now supports nil (keyword aligned) or number (offset)
166 ;; Support ksh-tab-always-indent feature
167 ;; Variables offsetting indentation renamed to better reflect their
169 ;; Integrate keyword completion feature supplied by
170 ;; Haavard Rue <hrue@imf.unit.no>.
172 ;; 29-Mar-1993 Gary Ellison
173 ;; Last Modified: Tue Sep 29 16:14:02 1992 #10 (Gary Ellison)
174 ;; Integrate line continuation patch supplied by
175 ;; Haavard Rue <hrue@imf.unit.no>
176 ;; Name back to ksh-mode to avoid confusion with sh-mode
177 ;; by Thomas W. Strong, Jr. <strong+@cmu.edu>.
179 ;; 29-Sep-1992 Gary Ellison
180 ;; Last Modified: Wed Sep 2 08:51:40 1992 #9 (Gary Ellison)
181 ;; Full support of ksh88 case items.
182 ;; Align statements under "do" and "then" keywords one position
185 ;; 2-Sep-1992 Gary Ellison
186 ;; Last Modified: Tue Aug 4 14:34:35 1992 #8 (Gary Ellison)
187 ;; Use make-variable-buffer-local instead of make-local-variable
188 ;; Get rid of superflous ksh-default variables.
189 ;; Use end of word match \b for "then", "do", "else", "elif"
190 ;; Support process substitution lists and exclude ksh 88 case items
191 ;; Use default-tab-width for indentation defaults.
192 ;; Moved installation instructions to the mode level documentation
194 ;; Fixed auto-mode-alist documentation.
196 ;; 24-Jul-1992 Gary Ellison
197 ;; Last Modified: Fri Jul 24 09:45:11 1992 #7 (Gary Ellison)
198 ;; Modified ksh-indent-region to use marker versus fixed end point.
199 ;; comment-start-skip regexp no longer fooled by parameter substitution.
200 ;; Added constant ksh-mode-version.
202 ;; 21-Jul-1992 Gary Ellison
203 ;; Last Modified: Tue Jul 21 15:53:57 1992 #6 (Gary Ellison)
204 ;; Indent with tabs instead of spaces.
205 ;; Can handle just about all styles.
206 ;; Anti-newline in REs.
207 ;; Word delim "\b" in REs
208 ;; More syntax entries.
209 ;; Variables with regexp suffix abbreviated to re
211 ;; Implemented minimal indent-region-function
212 ;; Mode documentation corrected.
213 ;; Minor lisp source format changes.
215 ;; 29-Jun-1992 Gary Ellison
216 ;; Last Modified: Mon Jun 29 15:39:35 1992 #5 (Gary Ellison)
217 ;; Optimize line-to-string
218 ;; Implicit/Explicit functions aok
219 ;; More indentation variables
220 ;; Superfluous defun killed.
221 ;; renamed to sh-mode
223 ;; 22-Jun-1992 Gary Ellison
224 ;; Last Modified: Mon Jun 22 15:01:14 1992 #4 (Gary Ellison)
225 ;; Cleanup pre att.emacs posting
227 ;; 19-Jun-1992 Gary Ellison
228 ;; Last Modified: Fri Jun 19 17:19:14 1992 #3 (Gary Ellison)
229 ;; Minimal case indent handling
231 ;; 19-Jun-1992 Gary Ellison
232 ;; Last Modified: Fri Jun 19 16:23:26 1992 #2 (Gary Ellison)
233 ;; Nesting handled except for case statement
235 ;; 19-Jun-1992 Gary Ellison
236 ;; Last Modified: Fri Jun 19 10:03:07 1992 #1 (Gary Ellison)
237 ;; Conception of this mode.
238 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
240 (defconst ksh-mode-version "$Revision: 1.4 $"
241 "*Version numbers of this version of ksh-mode")
244 ;; Variables controlling indentation style
248 ;; perhaps c-basic-offset would be okay to use as a default, but using
249 ;; default-tab-width as the default is ridiculous --Stig
250 "*Indentation of ksh statements with respect to containing block. A value
251 of nil indicates compound list keyword \(\"do\" and \"then\"\) alignment.")
252 (defvar ksh-case-item-offset ksh-indent
253 "*Additional indentation for case items within a case statement.")
254 (defvar ksh-case-indent nil
255 "*Additional indentation for statements under case items.")
256 (defvar ksh-group-offset (- ksh-indent)
257 "*Additional indentation for keywords \"do\" and \"then\".")
258 (defvar ksh-brace-offset 0
259 "*Additional indentation of \"{\" under functions or brace groupings.")
260 (defvar ksh-multiline-offset 1
261 "*Additional indentation of line that is preceded of a line ending with a
262 \\ to make it continue on next line.")
263 (defvar ksh-match-and-tell t
264 "*If non-nil echo in the minibuffer the matching compound command
265 for the \"done\", \"}\", \"fi\", or \"esac\". ")
266 (defvar ksh-tab-always-indent t
267 "*Controls the operation of the TAB key. If t (the default), always
268 reindent the current line. If nil, indent the current line only if
269 point is at the left margin or in the line's indentation; otherwise
272 (defvar ksh-align-to-keyword nil
273 ;; #### - this is broken, so it should be disabled by default --Stig
274 "*Controls whether nested constructs align from the keyword or
275 the current indentation. If non-nil, indentation will be relative to
276 the column the keyword starts. If nil, indentation will be relative to
277 the current indentation of the line the keyword is on.
278 The default value is non-nil. The non-nil case doesn't work very well.")
280 (defvar ksh-comment-regexp "^\\s *#"
281 "*Regular expression used to recognize comments. Customize to support
282 ksh-like languages.")
284 (defun ksh-current-indentation ()
287 (fset 'ksh-current-indentation 'current-column)
289 ;; Variables controlling completion
290 (defvar ksh-completion-list '())
291 (make-variable-buffer-local 'ksh-completion-list)
292 (set-default 'ksh-completion-list '())
295 ;; -type- : type number, 0:misc, 1:variable, 2:function
296 ;; -regexp-: regexp used to parse the script
297 ;; -match- : used by match-beginning/end to pickup target
299 (defvar ksh-completion-type-misc 0)
300 (defvar ksh-completion-regexp-var "\\([A-Za-z_0-9]+\\)=")
301 (defvar ksh-completion-type-var 1)
302 (defvar ksh-completion-match-var 1)
303 (defvar ksh-completion-regexp-var2 "\\$\\({\\|{#\\)?\\([A-Za-z_0-9]+\\)[#%:}]?")
304 (defvar ksh-completion-match-var2 2)
305 (defvar ksh-completion-regexp-function
306 "\\(function\\)?[ \t]*\\([A-Za-z_0-9]+\\)[ \t]*([ \t]*)")
307 (defvar ksh-completion-type-function 2)
308 (defvar ksh-completion-match-function 2)
311 ;; Variable controlling fontification
313 ;(defvar ksh-keywords '("for" "in" "do" "done" "select" "case" "esac" "if"
314 ;"then" "elif" "else" "fi" "while" "until" "function" "time"
315 ;"alias" "bg" "break" "continue" "cd" "exit" "echo" "fc" "fg" "getopts" "jobs"
316 ;"kill" "let" "newgrp" "print" "pwd" "read" "readonly" "return" "set" "shift"
317 ;"test" "times" "trap" "typeset" "ulimit" "umask" "unalias" "unset" "wait" "whence"))
319 (setq ksh-keywords "alias\\|b\\(g\\|reak\\)\\|c\\(ase\\|d\\|ontinue\\)\\|do\\(\\|ne\\)\\|e\\(cho\\|l\\(if\\|se\\)\\|sac\\|x\\(it\\|port\\)\\)\\|f\\([cgi]\\|or\\|unction\\)\\|getopts\\|i[fn]\\|jobs\\|kill\\|let\\|newgrp\\|p\\(rint\\|wd\\)\\|re\\(ad\\(\\|only\\)\\|turn\\)\\|s\\(e\\(lect\\|t\\)\\|hift\\)\\|t\\(est\\|hen\\|imes?\\|rap\\|ypeset\\)\\|u\\(limit\\|mask\\|n\\(alias\\|set\\|til\\)\\)\\|w\\(ait\\|h\\(ence\\|ile\\)\\)")
321 ;; '("\\<function[ \t]+\\([^(; \t]+\\)" 1 font-lock-function-name-face)
322 (defvar ksh-font-lock-keywords (purecopy
324 ;; Fontify [[ ]] expressions
325 '("\\(\\[.*\\]\\)" 1 font-lock-string-face t)
332 ;; Fontify function names
333 '("\\<function[ \t]+\\([^(; \t]+\\)" 1 font-lock-function-name-face)
334 '("\\(^[ \t]*[A-Za-z_][A-Za-z_0-9]*[ \t]*()\\)" 1 font-lock-function-name-face)
336 '("\\<export[ \t]+\\(-[a-z][ \t]+\\)?\\<\\([A-Za-z_][A-Za-z0-9_]*\\)\\>" 2 font-lock-variable-name-face)
338 '("\\<alias[ \t]+\\(-[a-z][ \t]+\\)?\\<\\([A-Za-z_][A-Za-z0-9_]*\\)\\>" 2 font-lock-type-face)
339 ;; Fontify $variables/parameters
340 '("\\(\\$\\([A-Za-z_#-][A-Za-z0-9_]*\\|{[^}]+}\\|([^)]+)\\)\\)"
341 ;; The `if' is needed because the specific faces are unique to the
343 1 (if (string-match "XEmacs" emacs-version)
344 font-lock-preprocessor-face
345 font-lock-reference-face))
347 "Expressions to font-lock in ksh-mode.")
349 (defvar ksh-mode-hook nil
350 "Hook to run when entering ksh-mode.")
352 ;; For the ksh-mode function, a small suggestion.
355 ;; config font-lock mode
357 ;; First, some ugly stuff that is necessary to support GNU Emacs as
359 ;; Setting up things for font-lock
360 ;; (if (string-match "XEmacs" emacs-version)
362 ;; (make-local-variable 'font-lock-keywords)
363 ;; (setq font-lock-keywords ksh-font-lock-keywords))
365 ;; (make-local-variable 'font-lock-defaults)
366 ;; (setq font-lock-defaults '(ksh-font-lock-keywords nil t))
369 (put 'ksh-mode 'font-lock-defaults '(ksh-font-lock-keywords))
372 ;; Context/indentation regular expressions
374 ;; indenting expressions
376 ;(defconst ksh-then-do-re "^[^#\n]*\\s\"*\\b\\(then\\|do\\)\\b"
377 (defconst ksh-then-do-re "\\s *\\b\\(then\\|do\\)\\b"
378 "*Regexp used to locate grouping keywords: \"then\" and \"do\"" )
380 (defconst ksh-do-re "\\s *\\bdo\\(\\b\\|$\\)"
381 "*Regexp used to match keyword: do")
383 (defconst ksh-then-re "\\s *\\bthen\\(\\b\\|$\\)"
384 "*Regexp used to match keyword: then")
387 ;; Structure starting/indenting keywords
389 (defconst ksh-else-re "\\s *\\belse\\(\\b\\|$\\)"
390 "*Regexp used to match keyword: else")
392 (defconst ksh-elif-re "\\s *\\belif\\(\\b\\|$\\)"
393 "*Regexp used to match keyword: elif")
395 (defconst ksh-brace-re "\\S>*{[ \t\n]"
396 "*Regexp used to match syntactic entity: { ")
398 (defconst ksh-case-item-end-re "\\S>*;;[ \t\n]"
399 "*Regexp used to match case item end syntactic entity: ;;")
401 (defconst ksh-keywords-re
402 "\\s *\\b\\(else\\|if\\|elif\\|case\\|while\\|for\\|until\\|select\\)\\b"
403 "*Regexp used to detect compound command keywords: if, else, elif case,
404 while, for, until, and select")
407 (defconst ksh-if-re "\\s *\\b\\(if\\)\\b"
408 "*Regexp used to match keyword: if")
410 (defconst ksh-iteration-keywords-re
411 "\\s *\\b\\(while\\|for\\|until\\|select\\)\\b"
412 "*Match one of the keywords: while, until, for, select")
414 (defconst ksh-case-re "\\s *\\bcase\\b"
415 "*Regexp used to match keyword: case")
417 (defconst ksh-explicit-func-re
418 "^\\s *\\(function\\s [a-zA-z_][a-zA-Z0-1_]*\\)\\b"
419 "*Match an explicit function definition: function name")
421 (defconst ksh-implicit-func-re
422 "^\\s *\\([a-zA-z_][a-zA-Z0-1_]*\\)\\s *()\\s *"
423 "*Match an implicit function definition: name ()")
425 (defconst ksh-func-brace-re "^\\s *\\(.*{\\)[ \t\n]+"
426 "*Match a implicit function definition brace: name { ")
430 (defconst ksh-case-item-re "^[^#\n]*\\s\"*\\()\\)"
431 "*Regexp used to match case-items including ksh88")
433 (defconst ksh-paren-re "^[^#\n]*\\s\"*)[ \t\n]+"
434 "*Regexp used to match compound list & case items")
437 ;; structure ending keyword regular expressions
438 (defconst ksh-fi-re "\\s *\\bfi\\b"
439 "*Regexp used to match keyword: fi")
441 (defconst ksh-esac-re "\\s *\\besac\\b"
442 "*Regexp used to match keyword: esac")
444 (defconst ksh-done-re "\\s *\\bdone\\b"
445 "*Regexp used to match keyword: done")
447 (defconst ksh-brace-end-re "\\s *}\\s *"
448 "*Regexp used to match function brace-groups")
450 (defconst ksh-multiline-re "^.*\\\\$"
451 "*Regexp used to match a line with a statement using more lines.")
455 ;; Create mode specific tables
456 (defvar ksh-mode-syntax-table nil
457 "Syntax table used while in ksh mode.")
458 (if ksh-mode-syntax-table
460 (setq ksh-mode-syntax-table (make-syntax-table (standard-syntax-table)))
461 (modify-syntax-entry ?\( "." ksh-mode-syntax-table)
462 (modify-syntax-entry ?\) "." ksh-mode-syntax-table)
463 (modify-syntax-entry ?{ "." ksh-mode-syntax-table)
464 (modify-syntax-entry ?} "." ksh-mode-syntax-table)
465 (modify-syntax-entry ?\[ "(]" ksh-mode-syntax-table)
466 (modify-syntax-entry ?\] ")[" ksh-mode-syntax-table)
467 (modify-syntax-entry ?\' "\"" ksh-mode-syntax-table)
468 (modify-syntax-entry ?` "\"" ksh-mode-syntax-table)
469 (modify-syntax-entry ?\n ">" ksh-mode-syntax-table)
470 (modify-syntax-entry ?\f ">" ksh-mode-syntax-table)
471 (modify-syntax-entry ?# "<" ksh-mode-syntax-table)
472 (modify-syntax-entry ?_ "w" ksh-mode-syntax-table)
473 (modify-syntax-entry ?< "." ksh-mode-syntax-table)
474 (modify-syntax-entry ?> "." ksh-mode-syntax-table)
475 (modify-syntax-entry ?& "." ksh-mode-syntax-table)
476 (modify-syntax-entry ?| "." ksh-mode-syntax-table)
477 (modify-syntax-entry ?$ "\\" ksh-mode-syntax-table)
478 (modify-syntax-entry ?% "." ksh-mode-syntax-table)
479 (modify-syntax-entry ?= "." ksh-mode-syntax-table)
480 (modify-syntax-entry ?/ "." ksh-mode-syntax-table)
481 (modify-syntax-entry ?+ "." ksh-mode-syntax-table)
482 (modify-syntax-entry ?* "." ksh-mode-syntax-table)
483 (modify-syntax-entry ?- "." ksh-mode-syntax-table)
484 (modify-syntax-entry ?\; "." ksh-mode-syntax-table)
485 (modify-syntax-entry ?: "." ksh-mode-syntax-table)
488 (defvar ksh-mode-abbrev-table nil
489 "Abbrev table used while in ksh mode.")
490 (define-abbrev-table 'ksh-mode-abbrev-table ())
492 (defvar ksh-mode-map nil
493 "Keymap used in ksh mode")
497 (setq ksh-mode-map (make-sparse-keymap))
498 (define-key ksh-mode-map "\t" 'ksh-indent-command)
499 (define-key ksh-mode-map "\t" 'ksh-indent-line)
500 (define-key ksh-mode-map "\C-j" 'reindent-then-newline-and-indent)
501 (define-key ksh-mode-map "\e\t" 'ksh-complete-symbol)
502 (define-key ksh-mode-map "\C-c\t" 'ksh-completion-init-and-pickup)
508 "ksh-mode $Revision: 1.4 $ - Major mode for editing (Bourne, Korn or Bourne again)
510 Special key bindings and commands:
512 Variables controlling indentation style:
514 Indentation of ksh statements with respect to containing block.
517 Additional indentation for statements under case items.
518 Default value is nil which will align the statements one position
519 past the \")\" of the pattern.
521 Additional indentation for case items within a case statement.
524 Additional indentation for keywords \"do\" and \"then\".
527 Additional indentation of \"{\" under functions or brace groupings.
530 Additional indentation of line that is preceded of a line ending with a
531 \\ to make it continue on next line.
532 ksh-tab-always-indent
533 Controls the operation of the TAB key. If t (the default), always
534 reindent the current line. If nil, indent the current line only if
535 point is at the left margin or in the line's indentation; otherwise
538 If non-nil echo in the minibuffer the matching compound command
539 for the \"done\", \"}\", \"fi\", or \"esac\". Default value is t.
542 Controls whether nested constructs align from the keyword or
543 the current indentation. If non-nil, indentation will be relative to
544 the column the keyword starts. If nil, indentation will be relative to
545 the current indentation of the line the keyword is on.
546 The default value is non-nil.
549 Regular expression used to recognize comments. Customize to support
550 ksh-like languages. Default value is \"\^\\\\s *#\".
554 (setq ksh-indent default-tab-width)
555 (setq ksh-group-offset 0)
557 The following style is obtained:
561 bar # <-- ksh-group-offset is additive to ksh-indent
566 (setq ksh-indent default-tab-width)
567 (setq ksh-group-offset (- 0 ksh-indent))
569 The following style is obtained:
578 (setq ksh-case-item-offset 1)
579 (setq ksh-case-indent nil)
581 The following style is obtained:
584 foo) bar # <-- ksh-case-item-offset
585 baz;; # <-- ksh-case-indent aligns with \")\"
591 (setq ksh-case-item-offset 1)
592 (setq ksh-case-indent 6)
594 The following style is obtained:
597 foo) bar # <-- ksh-case-item-offset
598 baz;; # <-- ksh-case-indent
608 (font-lock-mode 1) ;; font-lock the buffer
610 (setq ksh-group-offset -8)
611 (setq ksh-brace-offset -8)
612 (setq ksh-tab-always-indent t)
613 (setq ksh-match-and-tell t)
614 (setq ksh-align-to-keyword t) ;; Turn on keyword alignment
619 (kill-all-local-variables)
620 (use-local-map ksh-mode-map)
621 (setq major-mode 'ksh-mode)
622 (setq mode-name "Ksh")
623 (setq local-abbrev-table ksh-mode-abbrev-table)
624 (set-syntax-table ksh-mode-syntax-table)
625 (make-local-variable 'indent-line-function)
626 (setq indent-line-function 'ksh-indent-line)
627 (make-local-variable 'indent-region-function)
628 (setq indent-region-function 'ksh-indent-region)
629 (make-local-variable 'comment-start)
630 (setq comment-start "# ")
631 (make-local-variable 'comment-end)
632 (setq comment-end "")
633 (make-local-variable 'comment-column)
634 (setq comment-column 32)
635 (make-local-variable 'comment-start-skip)
636 (setq comment-start-skip "#+ *")
638 ;; Let the user customize
639 (run-hooks 'ksh-mode-hook)
640 (if (not ksh-align-to-keyword)
641 (ksh-align-to-keyword -1)
648 (defun ksh-align-to-keyword (&optional arg)
649 "Toggle value of ksh-align-to-keyword and rebind the ksh-current-indentation
650 function. With arg, force alignment to keyword if and only if arg is positive."
652 (if (null arg) ;just toggle
653 (cond ((not ksh-align-to-keyword)
654 (setq ksh-align-to-keyword t)
655 (fset 'ksh-current-indentation 'current-column))
657 (setq ksh-align-to-keyword nil)
658 (fset 'ksh-current-indentation 'current-indentation))
661 (setq ksh-align-to-keyword t)
662 (fset 'ksh-current-indentation 'current-column))
664 (setq ksh-align-to-keyword nil)
665 (fset 'ksh-current-indentation 'current-indentation))
669 (defun ksh-current-line ()
670 "Return the vertical position of point in the buffer.
672 (+ (count-lines (point-min) (point))
673 (if (= (current-column) 0) 1 0))
677 (defun ksh-line-to-string ()
678 "From point, construct a string from all characters on
680 (skip-chars-forward " \t") ;; skip tabs as well as spaces
681 (buffer-substring (point)
686 (defun ksh-get-nest-level ()
687 "Return a 2 element list (nest-level nest-line) describing where the
688 current line should nest."
689 (let ((case-fold-search)
693 (while (and (not (bobp))
695 (if (and (not (looking-at "^\\s *$"))
699 (looking-at ksh-multiline-re)))
700 (not (looking-at ksh-comment-regexp)))
701 (setq level (cons (current-indentation)
707 (cons (current-indentation) (ksh-current-line))
713 (defun ksh-looking-at-compound-list ()
714 "Return true if current line contains compound list initiating keyword"
716 (looking-at ksh-do-re)
717 (looking-at ksh-then-re)
721 (defun ksh-looking-at-case-item ()
722 "Return true if current line is a case-item .vs. paren compound list"
726 ;; Handle paren indentation constructs for this line
727 (cond ((looking-at ksh-paren-re)
728 (goto-line (cdr (ksh-get-nest-level)))
730 ;; The question is whether this is really a case item or just
731 ;; parenthesized compound list.
732 (cond ((or (looking-at ksh-case-re)
733 (looking-at ksh-case-item-end-re)))
735 ;; turns out to be a parenthesized compound list
736 ;; so propigate the nil for cond
743 (defun ksh-get-case-indent ()
744 "Return the column of the closest open case statement"
747 (nest-list (ksh-get-compound-level ksh-case-re ksh-esac-re (point)))
751 (if ksh-match-and-tell
752 (message "No matching case for ;;"))
758 (defun ksh-search-forward-sexp (sexp-re fence-post)
759 "Search for an sexp. Return t on success with point at the
760 beginning of the sexp. Return nil on failure and restoring point
761 to it's original position"
765 (while (and (< (point) fence-post)
766 (not (looking-at sexp-re)))
769 (if (>= (point) fence-post)
770 (progn (goto-char old-pnt)
775 (defun ksh-search-backward-sexp (sexp-re fence-post)
784 (and (> (point) fence-post)
787 (if (looking-at sexp-re)
788 (save-excursion ;avoid comment foolage
789 (let ((key-fence (point)))
791 (back-to-indentation)
792 (while (and (ksh-search-forward-sexp sexp-re key-fence)
793 (< (point) key-fence)))
795 (if (= key-fence (point))
800 (if (< (point) fence-post)
801 (progn (goto-char old-pnt)
808 ;; Functions which make this mode what it is
811 (defun ksh-get-nester-column (nest-line)
812 "Return the column to indent to with respect to nest-line taking
813 into consideration keywords and other nesting constructs."
819 (start-line (ksh-current-line)))
822 ;; Handle case item indentation constructs for this line
823 ((ksh-looking-at-case-item)
825 (goto-line nest-line)
826 (back-to-indentation)
827 (let ((fence-post (ksh-eol-point)))
829 ;; Now know there is a case-item so detect whether
830 ;; it is first under case, just another case-item, or
831 ;; a case-item and case-item-end all rolled together.
833 (cond ((ksh-search-forward-sexp ksh-case-re fence-post)
834 (+ (ksh-current-indentation) ksh-case-item-offset))
836 ((ksh-looking-at-case-item)
837 (current-indentation))
839 ((looking-at ksh-case-item-end-re)
841 (+ (ksh-get-case-indent) ksh-case-item-offset))
844 (t;; Not a case-item. What to do relative to the nest-line?
846 (goto-line nest-line)
847 ; (setq start-post (point))
848 (setq fence-post (ksh-eol-point))
853 ;; Check if we are in a continued statement
854 ((and (looking-at ksh-multiline-re)
856 (goto-line (1- start-line))
857 (looking-at ksh-multiline-re)))
858 (+ (current-indentation) ksh-multiline-offset))
859 ;; In order to locate the column of the keyword,
860 ;; which might be embedded within a case-item,
861 ;; it is necessary to iterate over sexp.
864 (back-to-indentation)
865 (if (ksh-search-forward-sexp ksh-keywords-re fence-post)
868 ;; special pun intended 'case'
869 (if (looking-at ksh-case-re)
870 (+ (ksh-current-indentation)
871 ksh-case-item-offset)
872 (+ (ksh-current-indentation)
873 (if (null ksh-indent)
881 (back-to-indentation)
882 (if (ksh-search-forward-sexp ksh-then-do-re fence-post)
884 (if (null ksh-indent)
885 (+ (ksh-current-indentation) 1)
886 (+ (ksh-current-indentation) ksh-indent)))
889 ((looking-at ksh-brace-re)
890 (+ (current-indentation)
891 (if (null ksh-indent)
895 ;; Forces functions to first column
896 ((or (looking-at ksh-implicit-func-re)
897 (looking-at ksh-explicit-func-re))
898 (if (looking-at ksh-func-brace-re)
899 (if (null ksh-indent)
904 ;; Need to first detect the end of a case-item
905 ((looking-at ksh-case-item-end-re)
907 (+ (ksh-get-case-indent) ksh-case-item-offset))
909 ;; Now detect first statement under a case item
910 ((ksh-looking-at-case-item)
911 (if (null ksh-case-indent)
913 (re-search-forward ksh-case-item-re fence-post t)
914 (goto-char (match-end 1))
915 (+ (current-column) 1))
916 (+ (current-indentation) ksh-case-indent)))
918 ;; This is hosed when using current-column
919 ;; and there is a multi-command expression as the
921 (t (current-indentation)))
925 ;; Handle additional indentation constructs for this line
926 (cond ((ksh-looking-at-compound-list)
927 (+ nester-column ksh-group-offset))
928 ((looking-at ksh-brace-re)
929 (+ nester-column ksh-brace-offset))
937 (defun ksh-indent-command ()
938 "Indent current line relative to containing block and allow for
939 ksh-tab-always-indent customization"
941 (let (case-fold-search)
942 (cond ((save-excursion
943 (skip-chars-backward " \t")
946 (ksh-tab-always-indent
954 (defun ksh-indent-line ()
955 "Indent current line as far as it should go according
956 to the syntax/context"
958 (let (case-fold-search)
964 ;; Align this line to current nesting level
967 (level-list (ksh-get-nest-level)) ; Where to nest against
968 ;; (last-line-level (car level-list))
969 (this-line-level (current-indentation))
970 (nester-column (ksh-get-nester-column (cdr level-list)))
971 (struct-match (ksh-match-structure-and-reindent))
974 (setq nester-column struct-match))
975 (if (eq nester-column this-line-level)
979 (back-to-indentation)
980 (delete-region beg (point)))
981 (indent-to nester-column))
986 ;; Position point on this line
989 (this-line-level (current-indentation))
990 (this-bol (ksh-bol-point))
991 (this-point (- (point) this-bol))
993 (cond ((> this-line-level this-point);; point in initial white space
994 (back-to-indentation))
1002 (defun ksh-match-indent-level (begin-re end-re)
1003 "Match the compound command and indent. Return nil on no match,
1004 indentation to use for this line otherwise."
1006 (let* ((case-fold-search)
1009 (ksh-get-compound-level begin-re end-re (point))
1012 (if (null nest-list)
1014 (if ksh-match-and-tell
1015 (message "No matching compound command"))
1016 nil) ;; Propagate a miss.
1018 (nest-level (car nest-list))
1019 (match-line (cdr nest-list))
1021 (if ksh-match-and-tell
1023 (goto-line match-line)
1024 (message "Matched ... %s" (ksh-line-to-string))
1026 ) ;; if ksh-match-and-tell
1027 nest-level ;;Propagate a hit.
1031 ) ;; defun ksh-match-indent-level
1033 (defun ksh-match-structure-and-reindent ()
1034 "If the current line matches one of the indenting keywords
1035 or one of the control structure ending keywords then reindent. Also
1036 if ksh-match-and-tell is non-nil the matching structure will echo in
1039 (let (case-fold-search)
1042 (back-to-indentation)
1043 (cond ((looking-at ksh-else-re)
1044 (ksh-match-indent-level ksh-if-re ksh-fi-re))
1045 ((looking-at ksh-elif-re)
1046 (ksh-match-indent-level ksh-if-re ksh-fi-re))
1047 ((looking-at ksh-fi-re)
1048 (ksh-match-indent-level ksh-if-re ksh-fi-re))
1049 ((looking-at ksh-done-re)
1050 (ksh-match-indent-level ksh-iteration-keywords-re ksh-done-re))
1051 ((looking-at ksh-esac-re)
1052 (ksh-match-indent-level ksh-case-re ksh-esac-re))
1054 ((looking-at ksh-brace-end-re)
1056 ((ksh-match-indent-level ksh-implicit-func-re ksh-brace-end-re))
1057 ((ksh-match-indent-level ksh-explicit-func-re ksh-brace-end-re))
1058 ((ksh-match-indent-level ksh-func-brace-re ksh-brace-end-re))
1066 (defun ksh-forward-sexp ()
1067 "Special incantation to march over syntax expressions and
1068 avoid all sorts of nonsense"
1069 (if (char-equal ?< (char-syntax (char-after (point))))
1071 (if (char-equal ?. (char-syntax (char-after (point))))
1077 (skip-chars-forward ") \t") ;damn case
1081 (defun ksh-get-compound-level
1082 (begin-re end-re anchor-point &optional balance-list)
1083 "Determine how much to indent this structure. Return a list (level line)
1084 of the matching compound command or nil if no match found."
1086 (;; Locate the next compound begin keyword bounded by point-min
1088 (if (and (ksh-search-backward-sexp begin-re (point-min))
1089 (>= (point) (point-min))
1094 (nest-column (if (zerop match-point)
1097 (goto-char match-point)
1098 (ksh-current-indentation))))
1099 (nest-list (cons 0 0)) ;; sentinel cons since cdr is >= 1
1101 (if (zerop match-point)
1102 nil ;; graceful exit from recursion
1104 (if (nlistp balance-list)
1105 (setq balance-list (list)))
1106 ;; Now search forward from matching start keyword for end keyword
1107 ;; which will locate interceding compound commands
1108 (while (and (consp nest-list) (zerop (cdr nest-list))
1109 (ksh-search-forward-sexp end-re anchor-point)
1110 (> anchor-point (point))
1112 (if (not (memq (point) balance-list))
1114 (setq balance-list (cons (point) balance-list))
1115 (goto-char match-point) ;; beginning of compound cmd
1117 (ksh-get-compound-level begin-re end-re
1118 anchor-point balance-list))
1123 (cond ((consp nest-list)
1124 (if (zerop (cdr nest-list))
1126 (goto-char match-point)
1127 (cons nest-column (ksh-current-line)))
1137 (defun ksh-indent-region (start end)
1138 "From start to end, indent each line."
1139 ;; The algorithm is just moving through the region line by line with
1140 ;; the match noise turned off. Only modifies nonempty lines.
1142 (let (ksh-match-and-tell
1143 (endmark (copy-marker end)))
1147 (setq start (point))
1148 (while (> (marker-position endmark) start)
1149 (if (not (and (bolp) (eolp)))
1152 (setq start (point)))
1154 (set-marker endmark nil)
1160 ;; Completion code supplied by Haavard Rue <hrue@imf.unit.no>.
1163 ;; add a completion with a given type to the list
1165 (defun ksh-addto-alist (completion type)
1166 (setq ksh-completion-list
1167 (append ksh-completion-list
1168 (list (cons completion type)))))
1170 ;; init the list and pickup all
1172 (defun ksh-completion-init-and-pickup ()
1174 (let (case-fold-search)
1175 (ksh-completion-list-init)
1181 (defun ksh-completion-list-init ()
1183 (setq ksh-completion-list
1185 (cons "if" ksh-completion-type-misc)
1186 (cons "while" ksh-completion-type-misc)
1187 (cons "until" ksh-completion-type-misc)
1188 (cons "select" ksh-completion-type-misc)
1189 (cons "for" ksh-completion-type-misc)
1190 (cons "continue" ksh-completion-type-misc)
1191 (cons "function" ksh-completion-type-misc)
1192 (cons "fi" ksh-completion-type-misc)
1193 (cons "case" ksh-completion-type-misc)
1194 (cons "esac" ksh-completion-type-misc)
1195 (cons "break" ksh-completion-type-misc)
1196 (cons "exit" ksh-completion-type-misc)
1197 (cons "done" ksh-completion-type-misc)
1198 (cons "do" ksh-completion-type-misc))))
1200 (defun ksh-eol-point ()
1205 (defun ksh-bol-point ()
1210 (defun ksh-pickup-all ()
1211 "Pickup all completions in buffer."
1213 (ksh-pickup-completion-driver (point-min) (point-max) t))
1215 (defun ksh-pickup-this-line ()
1216 "Pickup all completions in current line."
1218 (ksh-pickup-completion-driver (ksh-bol-point) (ksh-eol-point) nil))
1220 (defun ksh-pickup-completion-driver (pmin pmax message)
1221 "Driver routine for ksh-pickup-completion."
1223 (message "pickup completion..."))
1226 (ksh-pickup-completion ksh-completion-regexp-var
1227 ksh-completion-type-var
1228 ksh-completion-match-var
1231 (ksh-pickup-completion ksh-completion-regexp-var2
1232 ksh-completion-type-var
1233 ksh-completion-match-var2
1236 (ksh-pickup-completion ksh-completion-regexp-function
1237 ksh-completion-type-function
1238 ksh-completion-match-function
1241 (message "pickup %d variables and %d functions." (+ i1 i2) i3))))
1243 (defun ksh-pickup-completion (regexp type match pmin pmax)
1244 "Pickup completion in region and addit to the list, if not already
1250 (re-search-forward regexp pmax t)
1251 (match-beginning match)
1252 (setq kw (buffer-substring
1253 (match-beginning match)
1254 (match-end match))))
1256 (setq obj (assoc kw ksh-completion-list))
1257 (if (or (equal nil obj)
1258 (and (not (equal nil obj))
1259 (not (= type (cdr obj)))))
1262 (ksh-addto-alist kw type))))))
1265 (defun ksh-complete-symbol ()
1266 "Perform completion."
1268 (let* ((case-fold-search)
1270 (beg (unwind-protect
1273 (while (= (char-syntax (following-char)) ?\')
1276 (pattern (buffer-substring beg end))
1279 ;; ` or $( mark a function
1289 (looking-at "\\$(")))
1290 (function (lambda (sym)
1291 (equal (cdr sym) ksh-completion-type-function)))
1293 ;; a $, ${ or ${# mark a variable
1301 (looking-at "\\${"))
1304 (looking-at "\\${#")))
1305 (function (lambda (sym)
1307 ksh-completion-type-var)))
1309 ;; don't know. use 'em all
1311 (function (lambda (sym) t))))))
1313 (completion (try-completion pattern ksh-completion-list predicate)))
1315 (cond ((eq completion t))
1317 ;; oops, what is this ?
1320 (message "Can't find completion for \"%s\"" pattern))
1324 ((not (string= pattern completion))
1325 (delete-region beg end)
1326 (insert completion))
1328 ;; write possible completion in the minibuffer,
1329 ;; use this instead of a separate buffer (usual)
1332 (let ((list (all-completions pattern ksh-completion-list predicate))
1336 (setq string (concat string (format "%s " (car list))))
1337 (setq list (cdr list))))
1338 (message string))))))
1341 ;;; ksh-mode.el ends here