Initial Commit
[packages] / xemacs-packages / prog-modes / ksh-mode.el
1 ;; ksh-mode.el --- sh (ksh, bash) script editing mode for GNU Emacs.
2
3 ;; Copyright (C) 1992-1999 Gary Ellison.
4
5 ;; This file is compatible with GNU Emacs but is not part of the official
6 ;; distribution.
7 ;;
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it at your option.
10 ;;   
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.
14 ;;
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.
18
19 ;; $Source: /usr/CVSroot/XEmacs/packages/xemacs-packages/prog-modes/ksh-mode.el,v $ --  
20 ;;
21 ;; LCD Archive Entry:
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|
25
26 ;; Author: Gary Ellison <gfe@interhack.net>
27 ;;                   Interhack Posse
28 ;;
29 ;; Maintainer: Gary Ellison <gfe@interhack.net>
30 ;; Created: Fri Jun 19
31 ;; $Revision: 1.4 $
32 ;; Keywords: shell, korn, bourne, sh, ksh, bash
33 ;;
34 ;; Delta On   $Date: 2000/10/06 09:12:14 $
35 ;; Last Modified By: Gary Ellison
36 ;; Status          : Highly Functional
37 ;;
38
39 ;;; Commentary:
40
41 ;;
42 ;; Description:
43 ;;   sh, ksh, and bash script editing commands for emacs.
44 ;; 
45 ;; Installation:
46 ;;   Put ksh-mode.el in some directory in your load-path.
47 ;;   Refer to the installation section of ksh-mode's function definition.
48 ;;
49 ;; Usage:
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.
54 ;; 
55 ;; Bugs:
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.
62 ;;   
63 ;;   Function ending brace "}" must be on a separate line for indent-line
64 ;;   to do the right thing.
65 ;;
66 ;;   Explicit function definition matching will proclaim in the minibuffer
67 ;;   "No matching compound command" followed by "Matched ... "
68 ;;
69 ;;   indent-for-comment fails to recognize a comment starting in column 0,
70 ;;   hence it moves the comment-start in comment-column.
71
72 ;;; Code:
73
74 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
75 ;;
76 ;; HISTORY 
77 ;; 13-May-99            Gary Ellison
78 ;;    Removed some testing baggage which leaked into previous release.
79 ;;
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.
83 ;;
84 ;; 28-Feb-97            Kevin Blake <kblake@nortel.ca>
85 ;;    font-lock enhancements
86 ;;
87 ;; 4-Feb-97             Jim Berry <jberry@FRB.GOV>
88 ;;    Converted from use of font-lock-keywords to font-lock-defaults.
89 ;;
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. 
93 ;;
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
97 ;;    shell does.
98 ;;
99 ;; 23-Feb-1995          Gary Ellison    
100 ;;    Merged Jonathan Stigelman <Stig@hackvan.com> into 2.5 souce.
101 ;;
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.
107 ;;
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
111 ;;      parsing.
112 ;;    - Fixed keyword regexps. Keywords were not being recoginized on the
113 ;;      same line as " ' `.
114 ;;
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.
118 ;;
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 :)
126 ;;
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
130 ;;      column.
131 ;;
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.
136 ;;
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.
141 ;;
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
145 ;;
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.
149 ;;
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.
155 ;;
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
160 ;;    doesn't change. 
161 ;;
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
168 ;;    role.
169 ;;    Integrate keyword completion feature supplied by
170 ;;    Haavard Rue <hrue@imf.unit.no>.
171 ;;
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>.
178 ;;
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 
183 ;;    past the keyword.
184 ;;
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 
193 ;;    section.
194 ;;    Fixed auto-mode-alist documentation.
195 ;;
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.
201 ;;
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
210 ;;    Better } handling
211 ;;    Implemented minimal indent-region-function
212 ;;    Mode documentation corrected.
213 ;;    Minor lisp source format changes.
214 ;;    
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
222 ;;    
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
226 ;;
227 ;; 19-Jun-1992          Gary Ellison
228 ;;    Last Modified: Fri Jun 19 17:19:14 1992 #3 (Gary Ellison)
229 ;;    Minimal case indent handling
230 ;;
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
234 ;;
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
239
240 (defconst ksh-mode-version "$Revision: 1.4 $"
241   "*Version numbers of this version of ksh-mode")
242
243 ;;
244 ;; Variables controlling indentation style
245 ;;
246
247 (defvar ksh-indent 2 
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
270 insert a tab.")
271
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.")
279
280 (defvar ksh-comment-regexp "^\\s *#"
281   "*Regular expression used to recognize comments. Customize to support
282 ksh-like languages.")
283
284 (defun ksh-current-indentation ()
285   nil)
286 ;;
287 (fset 'ksh-current-indentation 'current-column)
288 ;;
289 ;; Variables controlling completion
290 (defvar ksh-completion-list '())
291 (make-variable-buffer-local 'ksh-completion-list)
292 (set-default 'ksh-completion-list  '())
293
294 ;;
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
298 ;;
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)
309
310 ;;
311 ;; Variable controlling fontification
312 ;;
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"))
318
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\\)\\)")
320
321 ;;       '("\\<function[ \t]+\\([^(; \t]+\\)" 1 font-lock-function-name-face)
322 (defvar ksh-font-lock-keywords (purecopy
323       (list
324        ;; Fontify [[ ]] expressions
325        '("\\(\\[.*\\]\\)"  1 font-lock-string-face t)
326        ;; Fontify keywords
327        (cons (concat
328               "\\<\\("
329               ksh-keywords
330               "\\)\\>")
331              1)
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)
335        ;; Fontify exports
336        '("\\<export[ \t]+\\(-[a-z][ \t]+\\)?\\<\\([A-Za-z_][A-Za-z0-9_]*\\)\\>" 2 font-lock-variable-name-face)
337        ;; Fontify aliases
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
342          ;; two emacsen.
343          1 (if (string-match "XEmacs" emacs-version)
344                font-lock-preprocessor-face
345              font-lock-reference-face))
346        ))
347        "Expressions to font-lock in ksh-mode.")
348
349 (defvar ksh-mode-hook nil
350   "Hook to run when entering ksh-mode.")
351
352 ;; For the ksh-mode function, a small suggestion.
353
354 ;;
355 ;; config font-lock mode
356
357 ;; First, some ugly stuff that is necessary to support GNU Emacs as
358 ;; well as XEmacs.
359 ;; Setting up things for font-lock
360 ;;  (if (string-match "XEmacs" emacs-version)
361 ;;      (progn
362 ;;      (make-local-variable 'font-lock-keywords)
363 ;;      (setq font-lock-keywords ksh-font-lock-keywords))
364 ;;    ;; Emacs
365 ;;    (make-local-variable 'font-lock-defaults)
366 ;;    (setq font-lock-defaults '(ksh-font-lock-keywords nil t))
367 ;;    )
368
369 (put 'ksh-mode 'font-lock-defaults '(ksh-font-lock-keywords))
370
371 ;;
372 ;; Context/indentation regular expressions
373 ;; 
374 ;; indenting expressions
375 ;;
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\"" )
379
380 (defconst ksh-do-re          "\\s *\\bdo\\(\\b\\|$\\)"
381   "*Regexp used to match keyword: do")
382
383 (defconst ksh-then-re        "\\s *\\bthen\\(\\b\\|$\\)"
384   "*Regexp used to match keyword: then")
385
386 ;;
387 ;; Structure starting/indenting keywords
388 ;;
389 (defconst ksh-else-re           "\\s *\\belse\\(\\b\\|$\\)"
390   "*Regexp used to match keyword: else")
391
392 (defconst ksh-elif-re           "\\s *\\belif\\(\\b\\|$\\)"
393   "*Regexp used to match keyword: elif")
394
395 (defconst ksh-brace-re           "\\S>*{[ \t\n]"
396   "*Regexp used to match syntactic entity: { ")
397
398 (defconst ksh-case-item-end-re           "\\S>*;;[ \t\n]"
399   "*Regexp used to match case item end syntactic entity: ;;")
400
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")
405
406
407 (defconst ksh-if-re         "\\s *\\b\\(if\\)\\b"
408   "*Regexp used to match keyword: if")
409
410 (defconst ksh-iteration-keywords-re 
411   "\\s *\\b\\(while\\|for\\|until\\|select\\)\\b"
412   "*Match one of the keywords: while, until, for, select")
413
414 (defconst ksh-case-re           "\\s *\\bcase\\b"
415   "*Regexp used to match keyword: case")
416
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")
420
421 (defconst ksh-implicit-func-re
422   "^\\s *\\([a-zA-z_][a-zA-Z0-1_]*\\)\\s *()\\s *"
423   "*Match an implicit function definition: name ()")
424
425 (defconst ksh-func-brace-re "^\\s *\\(.*{\\)[ \t\n]+"
426   "*Match a implicit function definition brace: name { ")
427
428 ;;
429 ;; indenting 
430 (defconst ksh-case-item-re           "^[^#\n]*\\s\"*\\()\\)"
431   "*Regexp used to match case-items including ksh88")
432
433 (defconst ksh-paren-re           "^[^#\n]*\\s\"*)[ \t\n]+"
434   "*Regexp used to match compound list & case items")
435
436 ;;
437 ;; structure ending keyword regular expressions
438 (defconst ksh-fi-re            "\\s *\\bfi\\b"
439   "*Regexp used to match keyword: fi")
440
441 (defconst ksh-esac-re          "\\s *\\besac\\b"
442   "*Regexp used to match keyword: esac")
443
444 (defconst ksh-done-re          "\\s *\\bdone\\b"
445   "*Regexp used to match keyword: done")
446
447 (defconst ksh-brace-end-re  "\\s *}\\s *"
448   "*Regexp used to match function brace-groups")
449
450 (defconst ksh-multiline-re "^.*\\\\$"
451   "*Regexp used to match a line with a statement using more lines.")
452
453 ;;
454 ;;
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
459     ()
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)
486   )
487
488 (defvar ksh-mode-abbrev-table nil
489   "Abbrev table used while in ksh mode.")
490 (define-abbrev-table 'ksh-mode-abbrev-table ())
491
492 (defvar ksh-mode-map nil 
493   "Keymap used in ksh mode")
494
495 (if ksh-mode-map
496     ()
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)
503   )
504
505
506 ;;;###autoload
507 (defun ksh-mode ()
508   "ksh-mode $Revision: 1.4 $ - Major mode for editing (Bourne, Korn or Bourne again)
509 shell scripts.
510 Special key bindings and commands:
511 \\{ksh-mode-map}
512 Variables controlling indentation style:
513 ksh-indent
514     Indentation of ksh statements with respect to containing block.
515     Default value is 2.
516 ksh-case-indent
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.
520 ksh-case-item-offset
521     Additional indentation for case items within a case statement.
522     Default value is 2.
523 ksh-group-offset
524     Additional indentation for keywords \"do\" and \"then\".
525     Default value is -2.
526 ksh-brace-offset
527     Additional indentation of \"{\" under functions or brace groupings.
528     Default value is 0.
529 ksh-multiline-offset
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
536     insert a tab.
537 ksh-match-and-tell
538     If non-nil echo in the minibuffer the matching compound command
539     for the \"done\", \"}\", \"fi\", or \"esac\". Default value is t.
540
541 ksh-align-to-keyword
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.
547
548 ksh-comment-regexp
549   Regular expression used to recognize comments. Customize to support
550   ksh-like languages. Default value is \"\^\\\\s *#\".
551
552 Style Guide.
553  By setting
554     (setq ksh-indent default-tab-width)
555     (setq ksh-group-offset 0)
556
557     The following style is obtained:
558
559     if [ -z $foo ]
560             then
561                     bar    # <-- ksh-group-offset is additive to ksh-indent
562                     foo
563     fi
564
565  By setting
566     (setq ksh-indent default-tab-width)
567     (setq ksh-group-offset (- 0 ksh-indent))
568
569     The following style is obtained:
570
571     if [ -z $foo ]
572     then
573             bar
574             foo
575     fi
576
577  By setting
578     (setq ksh-case-item-offset 1)
579     (setq ksh-case-indent nil)
580
581     The following style is obtained:
582
583     case x in *
584      foo) bar           # <-- ksh-case-item-offset
585           baz;;         # <-- ksh-case-indent aligns with \")\"
586      foobar) foo
587              bar;;
588     esac
589
590  By setting
591     (setq ksh-case-item-offset 1)
592     (setq ksh-case-indent 6)
593
594     The following style is obtained:
595
596     case x in *
597      foo) bar           # <-- ksh-case-item-offset
598            baz;;        # <-- ksh-case-indent
599      foobar) foo
600            bar;;
601     esac
602     
603
604 Installation:
605
606  (setq ksh-mode-hook
607       (function (lambda ()
608          (font-lock-mode 1)             ;; font-lock the buffer
609          (setq ksh-indent 8)
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
615          )))"
616   ;;
617   ;; and away we go
618   (interactive)
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 "#+ *")
637   ;;
638   ;; Let the user customize
639   (run-hooks 'ksh-mode-hook)
640   (if (not ksh-align-to-keyword)
641       (ksh-align-to-keyword -1)
642     )
643   ) ;; defun
644
645 ;;
646 ;; Support functions
647
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."
651   (interactive)
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))
656             (t
657              (setq ksh-align-to-keyword nil)
658              (fset 'ksh-current-indentation 'current-indentation))
659             )
660     (cond ((natnump arg)
661            (setq ksh-align-to-keyword t)
662            (fset 'ksh-current-indentation 'current-column))
663           (t
664            (setq ksh-align-to-keyword nil)
665            (fset 'ksh-current-indentation 'current-indentation))
666           ))
667   )
668
669 (defun ksh-current-line ()
670   "Return the vertical position of point in the buffer.
671 Top line is 1."
672   (+ (count-lines (point-min) (point))
673      (if (= (current-column) 0) 1 0))
674   )
675
676
677 (defun ksh-line-to-string ()
678   "From point, construct a string from all characters on
679 current line"
680   (skip-chars-forward " \t") ;; skip tabs as well as spaces
681   (buffer-substring (point)
682                     (progn
683                       (end-of-line 1)
684                       (point))))
685
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)
690         (level))
691     (save-excursion
692       (forward-line -1)
693       (while (and (not (bobp))
694                   (null level))
695         (if (and (not (looking-at "^\\s *$"))
696                  (not (save-excursion
697                         (forward-line -1)
698                         (beginning-of-line)
699                         (looking-at ksh-multiline-re)))
700                  (not (looking-at ksh-comment-regexp)))
701             (setq level (cons (current-indentation)
702                               (ksh-current-line)))
703           (forward-line -1)
704           );; if
705         );; while
706       (if (null level)
707           (cons (current-indentation) (ksh-current-line))
708         level)
709       )
710     )
711   )
712
713 (defun ksh-looking-at-compound-list ()
714   "Return true if current line contains compound list initiating keyword"
715   (or 
716    (looking-at ksh-do-re)
717    (looking-at ksh-then-re)
718    ) ;; or
719   ) ;; defun
720
721 (defun ksh-looking-at-case-item ()
722   "Return true if current line is a case-item .vs. paren compound list"
723   (save-excursion
724     (beginning-of-line)
725     ;;
726     ;; Handle paren indentation constructs for this line
727     (cond ((looking-at ksh-paren-re)
728            (goto-line (cdr (ksh-get-nest-level)))
729            ;;
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)))
734                  ;;
735                  ;; turns out to be a parenthesized compound list
736                  ;; so propigate the nil for cond
737                  )
738            ))
739     )
740   ) ;; defun
741
742
743 (defun ksh-get-case-indent ()
744   "Return the column of the closest open case statement"
745   (save-excursion
746     (let (
747           (nest-list (ksh-get-compound-level ksh-case-re ksh-esac-re (point)))
748           )
749       (if (null nest-list)
750           (progn 
751             (if ksh-match-and-tell
752                 (message "No matching case for ;;"))
753             0)
754         (car nest-list)))
755     )
756   )
757
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"
762   (let
763       ((old-pnt (point))
764        )
765     (while (and (< (point) fence-post)
766                 (not (looking-at sexp-re)))
767       (ksh-forward-sexp))
768     
769     (if (>= (point) fence-post)
770         (progn (goto-char old-pnt)
771                nil)
772       t)
773     ))
774
775 (defun ksh-search-backward-sexp (sexp-re fence-post)
776   (let
777       ((old-pnt (point))
778        (sentinal nil)
779        )
780     (while
781         (progn
782           (if (not sentinal)
783               (backward-sexp 1))
784           (and (> (point) fence-post)
785                (not sentinal))
786           )
787       (if (looking-at sexp-re)
788           (save-excursion                       ;avoid comment foolage
789             (let ((key-fence (point)))
790               (beginning-of-line)
791               (back-to-indentation)
792               (while (and (ksh-search-forward-sexp sexp-re key-fence)
793                           (< (point) key-fence)))
794               
795               (if (= key-fence (point))
796                   (setq sentinal t))
797               ))
798         ))
799       
800       (if (< (point) fence-post)
801           (progn (goto-char old-pnt)
802                  nil)
803         t)
804       ))
805
806
807 ;;
808 ;; Functions which make this mode what it is
809 ;;
810
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."
814   (save-excursion 
815     (let ((fence-post)
816 ;         (start-post)
817           (nester-column)
818           (case-fold-search)
819           (start-line (ksh-current-line)))
820       (cond
821        ;;
822        ;; Handle case item indentation constructs for this line
823        ((ksh-looking-at-case-item)
824         (save-excursion
825           (goto-line nest-line)
826           (back-to-indentation)
827           (let ((fence-post (ksh-eol-point)))
828             ;;
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.
832             ;;
833             (cond ((ksh-search-forward-sexp ksh-case-re fence-post)
834                    (+ (ksh-current-indentation) ksh-case-item-offset))
835                   
836                   ((ksh-looking-at-case-item)
837                    (current-indentation))
838                   
839                   ((looking-at ksh-case-item-end-re)
840                    (end-of-line)
841                    (+ (ksh-get-case-indent) ksh-case-item-offset))
842                   )
843             )))
844        (t;; Not a case-item.  What to do relative to the nest-line?
845         (save-excursion
846           (goto-line nest-line)
847 ;         (setq start-post (point))
848           (setq fence-post (ksh-eol-point))
849           (setq nester-column
850                 (save-excursion
851                   (cond
852                    ;;
853                    ;; Check if we are in a continued statement
854                    ((and (looking-at ksh-multiline-re)
855                          (save-excursion
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.
862                    ((progn
863                       (save-excursion
864                         (back-to-indentation)
865                         (if (ksh-search-forward-sexp ksh-keywords-re fence-post)
866                             (progn
867                               ;;
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)
874                                        2 ksh-indent)))) 
875                           nil))
876                       ))
877                    ;;
878                    ;;  handle then or do
879                    ((progn
880                       (save-excursion
881                         (back-to-indentation)
882                         (if (ksh-search-forward-sexp ksh-then-do-re fence-post)
883                             (progn
884                               (if (null ksh-indent)
885                                   (+ (ksh-current-indentation) 1)
886                                 (+ (ksh-current-indentation) ksh-indent)))
887                           nil))))
888
889                    ((looking-at ksh-brace-re)
890                     (+ (current-indentation)
891                        (if (null ksh-indent)
892                            2 ksh-indent)
893                        ))
894                    ;;
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)
900                             2 ksh-indent)
901                       ksh-brace-offset))
902
903                    ;;
904                    ;; Need to first detect the end of a case-item
905                    ((looking-at ksh-case-item-end-re)
906                     (end-of-line)
907                     (+ (ksh-get-case-indent) ksh-case-item-offset))
908                    ;;
909                    ;; Now detect first statement under a case item
910                    ((ksh-looking-at-case-item)
911                     (if (null ksh-case-indent)
912                         (progn
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)))
917                    
918                    ;; This is hosed when using current-column
919                    ;; and there is a multi-command expression as the
920                    ;; nester.
921                    (t (current-indentation)))
922                   )
923                 ));; excursion over
924         ;;
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))
930               (t nester-column))
931         );; Not a case-item
932        )
933       );;let
934     );; excursion
935   ) ;; defun
936
937 (defun ksh-indent-command ()
938   "Indent current line relative to containing block and allow for
939 ksh-tab-always-indent customization"
940   (interactive)
941   (let (case-fold-search)
942     (cond ((save-excursion
943              (skip-chars-backward " \t")
944              (bolp))
945            (ksh-indent-line))
946           (ksh-tab-always-indent
947            (save-excursion
948              (ksh-indent-line)))
949           (t (insert-tab))
950           ))
951   )
952
953
954 (defun ksh-indent-line ()
955   "Indent current line as far as it should go according
956 to the syntax/context"
957   (interactive)
958   (let (case-fold-search)
959     (save-excursion
960       (beginning-of-line)
961       (if (bobp)
962           nil
963         ;;
964         ;; Align this line to current nesting level
965         (let*
966             (
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))
972              )
973           (if struct-match
974               (setq nester-column struct-match))
975           (if (eq nester-column this-line-level)
976               nil
977             (beginning-of-line)
978             (let ((beg (point)))
979               (back-to-indentation)
980               (delete-region beg (point)))
981             (indent-to nester-column))
982           );; let*
983         );; if
984       );; excursion
985     ;;
986     ;; Position point on this line
987     (let*
988         (
989          (this-line-level (current-indentation))
990          (this-bol (ksh-bol-point))
991          (this-point (- (point) this-bol))
992          )
993       (cond ((> this-line-level this-point);; point in initial white space
994              (back-to-indentation))
995             (t nil)
996             );; cond
997       );; let*
998     );; let
999   );; defun
1000
1001
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."
1005   (interactive)
1006   (let* ((case-fold-search)
1007          (nest-list 
1008           (save-excursion
1009             (ksh-get-compound-level begin-re end-re (point))
1010             ))
1011          ) ;; bindings
1012     (if (null nest-list)
1013         (progn
1014           (if ksh-match-and-tell
1015               (message "No matching compound command"))
1016           nil) ;; Propagate a miss.
1017       (let* (
1018              (nest-level (car nest-list))
1019              (match-line (cdr nest-list))
1020              ) ;; bindings
1021         (if ksh-match-and-tell
1022             (save-excursion
1023               (goto-line match-line)
1024               (message "Matched ... %s" (ksh-line-to-string))
1025               ) ;; excursion
1026           ) ;; if ksh-match-and-tell
1027         nest-level ;;Propagate a hit.
1028         ) ;; let*
1029       ) ;; if
1030     ) ;; let*
1031   ) ;; defun ksh-match-indent-level
1032
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
1037 the minibuffer"
1038   (interactive)
1039   (let (case-fold-search)
1040     (save-excursion
1041       (beginning-of-line)
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))
1053             ;;
1054             ((looking-at ksh-brace-end-re)
1055              (cond
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))
1059               (t nil)))
1060             (t nil)
1061             );; cond
1062       )
1063     ))
1064
1065
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))))
1070       (end-of-line)
1071     (if (char-equal ?. (char-syntax (char-after (point))))
1072         (forward-char)
1073       (forward-sexp 1))
1074     )
1075   (if (eolp)
1076       (forward-line))
1077   (skip-chars-forward ") \t")           ;damn case
1078   )
1079
1080
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."
1085   (let* 
1086       (;; Locate the next compound begin keyword bounded by point-min
1087        (match-point
1088         (if (and (ksh-search-backward-sexp begin-re (point-min))
1089                  (>= (point) (point-min))
1090                  )
1091             (point)
1092           0))
1093
1094        (nest-column (if (zerop match-point)
1095                         1 
1096                       (progn
1097                         (goto-char match-point)
1098                         (ksh-current-indentation))))
1099        (nest-list (cons 0 0))    ;; sentinel cons since cdr is >= 1
1100        )
1101     (if (zerop match-point)
1102         nil ;; graceful exit from recursion
1103       (progn
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))
1111                     )
1112           (if (not (memq (point) balance-list))
1113               (progn
1114                 (setq balance-list (cons (point) balance-list))
1115                 (goto-char match-point)  ;; beginning of compound cmd
1116                 (setq nest-list
1117                       (ksh-get-compound-level begin-re end-re
1118                                               anchor-point balance-list))
1119                 )
1120             (ksh-forward-sexp)
1121             ))
1122
1123         (cond ((consp nest-list)
1124                (if (zerop (cdr nest-list))
1125                  (progn
1126                    (goto-char match-point)
1127                    (cons nest-column (ksh-current-line)))
1128                  nest-list))
1129               (t nil)
1130               )
1131         )
1132       )
1133     )
1134   )
1135
1136
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.
1141   (save-excursion
1142     (let (ksh-match-and-tell
1143           (endmark (copy-marker end)))
1144       
1145       (goto-char start)
1146       (beginning-of-line)
1147       (setq start (point))
1148       (while (> (marker-position endmark) start)
1149         (if (not (and (bolp) (eolp)))
1150             (ksh-indent-line))
1151         (forward-line 1)
1152         (setq start (point)))
1153
1154       (set-marker endmark nil)
1155       )
1156     )
1157   )
1158
1159 ;;
1160 ;; Completion code supplied by Haavard Rue <hrue@imf.unit.no>.
1161 ;;
1162 ;;
1163 ;; add a completion with a given type to the list
1164 ;;
1165 (defun ksh-addto-alist (completion type)
1166   (setq ksh-completion-list
1167         (append ksh-completion-list
1168                 (list (cons completion type)))))
1169 ;;
1170 ;; init the list and pickup all 
1171 ;;
1172 (defun ksh-completion-init-and-pickup ()
1173   (interactive)
1174   (let (case-fold-search)
1175     (ksh-completion-list-init)
1176     (ksh-pickup-all)))
1177
1178 ;;
1179 ;; init the list
1180 ;;
1181 (defun ksh-completion-list-init ()
1182   (interactive)
1183   (setq ksh-completion-list
1184         (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))))
1199
1200 (defun ksh-eol-point ()
1201   (save-excursion
1202     (end-of-line)
1203     (point)))
1204
1205 (defun ksh-bol-point ()
1206   (save-excursion
1207     (beginning-of-line)
1208     (point)))
1209
1210 (defun ksh-pickup-all ()
1211   "Pickup all completions in buffer."
1212   (interactive)
1213   (ksh-pickup-completion-driver (point-min) (point-max) t))
1214
1215 (defun ksh-pickup-this-line ()
1216   "Pickup all completions in current line."
1217   (interactive)
1218   (ksh-pickup-completion-driver (ksh-bol-point) (ksh-eol-point) nil))
1219
1220 (defun ksh-pickup-completion-driver (pmin pmax message)
1221   "Driver routine for ksh-pickup-completion."
1222   (if message
1223       (message "pickup completion..."))
1224   (let* (
1225          (i1
1226           (ksh-pickup-completion  ksh-completion-regexp-var
1227                                  ksh-completion-type-var
1228                                  ksh-completion-match-var
1229                                  pmin pmax))
1230          (i2
1231           (ksh-pickup-completion  ksh-completion-regexp-var2
1232                                  ksh-completion-type-var
1233                                  ksh-completion-match-var2
1234                                  pmin pmax))
1235          (i3
1236           (ksh-pickup-completion  ksh-completion-regexp-function
1237                                  ksh-completion-type-function
1238                                  ksh-completion-match-function
1239                                  pmin pmax)))
1240     (if message
1241         (message "pickup %d variables and %d functions." (+ i1 i2) i3))))
1242
1243 (defun ksh-pickup-completion (regexp type match pmin pmax)
1244   "Pickup completion in region and addit to the list, if not already
1245 there." 
1246   (let ((i 0) kw obj)
1247     (save-excursion
1248       (goto-char pmin)
1249       (while (and
1250               (re-search-forward regexp pmax t)
1251               (match-beginning match)
1252               (setq kw  (buffer-substring
1253                          (match-beginning match)
1254                          (match-end match))))
1255         (progn
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)))))
1260               (progn
1261                 (setq i (1+ i))
1262                 (ksh-addto-alist kw type))))))
1263     i))
1264
1265 (defun ksh-complete-symbol ()
1266   "Perform completion."
1267   (interactive)
1268   (let* ((case-fold-search)
1269          (end (point))
1270          (beg (unwind-protect
1271                   (save-excursion
1272                     (backward-sexp 1)
1273                     (while (= (char-syntax (following-char)) ?\')
1274                       (forward-char 1))
1275                     (point))))
1276          (pattern (buffer-substring beg end))
1277          (predicate 
1278           ;;
1279           ;; ` or $( mark a function
1280           ;;
1281           (save-excursion
1282             (goto-char beg)
1283             (if (or
1284                  (save-excursion
1285                    (backward-char 1)
1286                    (looking-at "`"))
1287                  (save-excursion
1288                    (backward-char 2)
1289                    (looking-at "\\$(")))
1290                 (function (lambda (sym)
1291                             (equal (cdr sym) ksh-completion-type-function)))
1292               ;;
1293               ;; a $, ${ or ${# mark a variable
1294               ;;
1295               (if (or
1296                    (save-excursion
1297                      (backward-char 1)
1298                      (looking-at "\\$"))
1299                    (save-excursion
1300                      (backward-char 2)
1301                      (looking-at "\\${"))
1302                    (save-excursion
1303                      (backward-char 3)
1304                      (looking-at "\\${#")))
1305                   (function (lambda (sym)
1306                               (equal (cdr sym)
1307                                      ksh-completion-type-var)))
1308                 ;;
1309                 ;; don't know. use 'em all
1310                 ;;
1311                 (function (lambda (sym) t))))))
1312          ;;
1313          (completion (try-completion pattern ksh-completion-list predicate)))
1314     ;;
1315     (cond ((eq completion t))
1316           ;;
1317           ;; oops, what is this ?
1318           ;;
1319           ((null completion)
1320            (message "Can't find completion for \"%s\"" pattern))
1321           ;;
1322           ;; insert
1323           ;;
1324           ((not (string= pattern completion))
1325            (delete-region beg end)
1326            (insert completion))
1327           ;;
1328           ;; write possible completion in the minibuffer,
1329           ;; use this instead of a separate buffer (usual)
1330           ;;
1331           (t
1332            (let ((list (all-completions pattern ksh-completion-list predicate))
1333                  (string ""))
1334              (while list
1335                (progn
1336                  (setq string (concat string (format "%s " (car list))))
1337                  (setq list (cdr list))))
1338              (message string))))))
1339
1340 (provide 'ksh-mode)
1341 ;;; ksh-mode.el ends here