comparison lisp/modes/ksh-mode.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;; ksh-mode.el --- sh (ksh, bash) script editing mode for GNU Emacs.
2
3 ;; Copyright (C) 1992-95 Gary Ellison.
4
5 ;; This file is part of XEmacs.
6
7 ;; XEmacs is free software; you can redistribute it and/or modify it
8 ;; under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation; either version 2, or (at your option)
10 ;; any later version.
11
12 ;; XEmacs is distributed in the hope that it will be useful, but
13 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ;; General Public License for more details.
16
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with XEmacs; see the file COPYING. If not, write to the Free
19 ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 ;; LCD Archive Entry:
22 ;; ksh-mode|Gary F. Ellison|Gary_F_Ellison@ATT.COM
23 ;; Mode for editing sh/ksh/bash scripts
24 ;; 23-Feb-95|2.6|~/modes/ksh-mode.el.Z|
25
26 ;; Author: Gary F. Ellison <Gary.F.Ellison@ATT.COM>
27 ;; AT&T Bell Laboratories
28 ;; 6200 East Broad Street
29 ;; Columbus, Ohio 43213 USA
30 ;;
31 ;; Maintainer: Gary F. Ellison <Gary.F.Ellison@ATT.COM>
32 ;; Created: Fri Jun 19
33 ;; Version: 2.6
34 ;; Keywords: languages, shell, korn, bourne, sh, ksh, bash, unix
35 ;;
36 ;; Delta On : 2/23/95
37 ;; Last Modified By: Gary Ellison
38 ;; Last Modified On: Thu Feb 23 11:32:03 1995
39 ;; Update Count : 33
40 ;; Status : Highly Functional
41 ;;
42
43 ;;; Commentary:
44
45 ;;
46 ;; Description:
47 ;; sh, ksh, and bash script editing commands for emacs.
48 ;;
49 ;; Installation:
50 ;; Put ksh-mode.el in some directory in your load-path.
51 ;; Refer to the installation section of ksh-mode's function definition.
52 ;;
53 ;; Usage:
54 ;; This major mode assists shell script writers with indentation
55 ;; control and control structure construct matching in much the same
56 ;; fashion as other programming language modes. Invoke describe-mode
57 ;; for more information.
58 ;;
59 ;; Bugs:
60 ;; When the ksh-align-to-keyword is non-nil and the nester
61 ;; is a multi-command expression with a compound command
62 ;; the lines following the compound end will align incorrectly
63 ;; to the compound command instead of it's current indentation.
64 ;; The fix will probably require the detection of syntax elements
65 ;; in the nesting line.
66 ;;
67 ;; Function ending brace "}" must be on a separate line for indent-line
68 ;; to do the right thing.
69 ;;
70 ;; Explicit function definition matching will proclaim in the minibuffer
71 ;; "No matching compound command" followed by "Matched ... "
72 ;;
73 ;; indent-for-comment fails to recognize a comment starting in column 0,
74 ;; hence it moves the comment-start in comment-column.
75
76 ;;; Code:
77
78 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
79 ;;
80 ;; HISTORY
81 ;; 8-Aug-95 Jack Repenning <jackr@sgi.com>
82 ;; Fix documentation of `ksh-align-to-keyword' to conform to the 23
83 ;; Feb default change. Search for keywords obeying case, since the
84 ;; shell does.
85 ;;
86 ;; 23-Feb-1995 Gary Ellison
87 ;; Merged Jonathan Stigelman <Stig@hackvan.com> into 2.5 souce.
88 ;;
89 ;; 23 Feb 1995 Jonathan Stigelman <Stig@hackvan.com>
90 ;; Reshuffled documentation to make the format more consistant with other
91 ;; elisp. Added autoload and removed autoloading instructions from the
92 ;; ksh-mode docstring. Changed default value for `ksh-align-to-keyword'
93 ;; to nil because it doesn't work properly.
94 ;;
95 ;; 2-Aug-1994 Gary Ellison
96 ;; Last Modified: Mon Jun 13 16:52:55 1994 #29 (Gary Ellison)
97 ;; - Syntax table modifications to better support sexp navigation and
98 ;; parsing.
99 ;; - Fixed keyword regexps. Keywords were not being recoginized on the
100 ;; same line as " ' `.
101 ;;
102 ;; 13-Jun-1994 Gary Ellison
103 ;; Last Modified: Wed Mar 30 14:12:26 1994 #28 (Gary Ellison)
104 ;; - Minor excursion problem fixed in ksh-indent-command.
105 ;;
106 ;; 30-Mar-1994 Gary Ellison
107 ;; Last Modified: Fri Mar 25 15:42:29 1994 #25 (Gary Ellison)
108 ;; - Implement user customizable ksh-comment-regexp.
109 ;; - Make the keyword vs line indentation alignment customizable
110 ;; by calling ksh-align-to-keyword based on variable of same
111 ;; name. (If the code is obfuscated or convoluted I can attribute
112 ;; this to a severe head cold and not malice :)
113 ;;
114 ;; 25-Mar-1994 Gary Ellison
115 ;; Last Modified: Fri Feb 4 13:06:30 1994 #23 (Gary Ellison)
116 ;; - Nest relative to the line indentation not the keywords
117 ;; column.
118 ;;
119 ;; 4-Feb-1994 Gary Ellison
120 ;; Last Modified: Wed Nov 10 10:03:01 1993 #18 (Gary Ellison)
121 ;; - Add direct support for font-lock-mode. Thanks Espen Skoglund
122 ;; for the regular expressions.
123 ;;
124 ;; 10-Nov-1993 Gary Ellison
125 ;; Last Modified: Tue Oct 12 15:23:06 1993 #17 (Gary Ellison)
126 ;; Fix message on ksh-match-and-tell to not get invalid format
127 ;; when a % appears in the string.
128 ;;
129 ;; 12-Oct-1993 Espen Skoglund <espensk@stud.cs.uit.no>.
130 ;; Last Modified: Tue Oct 12 15:03:01 1993 #16 (Gary Ellison)
131 ;; Apply Line continuation patch supplied by Espen Skoglund
132 ;;
133 ;; 1-Sep-1993 Gary Ellison
134 ;; Last Modified: Tue Aug 17 17:18:18 1993 #14 (Gary Ellison)
135 ;; Get rid of this-line hack in ksh-get-nester-column.
136 ;;
137 ;; 17-Aug-1993 Gary Ellison
138 ;; Last Modified: Mon Jun 21 14:00:43 1993 #13 (Gary Ellison)
139 ;; Code uses builtin current-indentation instead of lisp defun
140 ;; ksh-indentation-on-this-line (thanks to Tom Tromey).
141 ;; More and better doc strings.
142 ;;
143 ;; 5-Aug-1993 Tom Tromey <tromey@cns.caltech.edu>
144 ;; Last Modified: Thu Aug 5 11:09:12 1993 #12 (Tom Tromey)
145 ;; ksh-indent-region skips blank lines. Uses let binding instead
146 ;; of setq. No longer marks buffer modified if indentation
147 ;; doesn't change.
148 ;;
149 ;; 21-Jun-1993 Gary Ellison
150 ;; Last Modified: Mon Mar 29 15:05:34 1993 #11 (Gary Ellison)
151 ;; Use make-local-variables instead of make-variables-buffer-local
152 ;; ksh-indent now supports nil (keyword aligned) or number (offset)
153 ;; Support ksh-tab-always-indent feature
154 ;; Variables offsetting indentation renamed to better reflect their
155 ;; role.
156 ;; Integrate keyword completion feature supplied by
157 ;; Haavard Rue <hrue@imf.unit.no>.
158 ;;
159 ;; 29-Mar-1993 Gary Ellison
160 ;; Last Modified: Tue Sep 29 16:14:02 1992 #10 (Gary Ellison)
161 ;; Integrate line continuation patch supplied by
162 ;; Haavard Rue <hrue@imf.unit.no>
163 ;; Name back to ksh-mode to avoid confusion with sh-mode
164 ;; by Thomas W. Strong, Jr. <strong+@cmu.edu>.
165 ;;
166 ;; 29-Sep-1992 Gary Ellison
167 ;; Last Modified: Wed Sep 2 08:51:40 1992 #9 (Gary Ellison)
168 ;; Full support of ksh88 case items.
169 ;; Align statements under "do" and "then" keywords one position
170 ;; past the keyword.
171 ;;
172 ;; 2-Sep-1992 Gary Ellison
173 ;; Last Modified: Tue Aug 4 14:34:35 1992 #8 (Gary Ellison)
174 ;; Use make-variable-buffer-local instead of make-local-variable
175 ;; Get rid of superflous ksh-default variables.
176 ;; Use end of word match \b for "then", "do", "else", "elif"
177 ;; Support process substitution lists and exclude ksh 88 case items
178 ;; Use default-tab-width for indentation defaults.
179 ;; Moved installation instructions to the mode level documentation
180 ;; section.
181 ;; Fixed auto-mode-alist documentation.
182 ;;
183 ;; 24-Jul-1992 Gary Ellison
184 ;; Last Modified: Fri Jul 24 09:45:11 1992 #7 (Gary Ellison)
185 ;; Modified ksh-indent-region to use marker versus fixed end point.
186 ;; comment-start-skip regexp no longer fooled by parameter substitution.
187 ;; Added constant ksh-mode-version.
188 ;;
189 ;; 21-Jul-1992 Gary Ellison
190 ;; Last Modified: Tue Jul 21 15:53:57 1992 #6 (Gary Ellison)
191 ;; Indent with tabs instead of spaces.
192 ;; Can handle just about all styles.
193 ;; Anti-newline in REs.
194 ;; Word delim "\b" in REs
195 ;; More syntax entries.
196 ;; Variables with regexp suffix abbreviated to re
197 ;; Better } handling
198 ;; Implemented minimal indent-region-function
199 ;; Mode documentation corrected.
200 ;; Minor lisp source format changes.
201 ;;
202 ;; 29-Jun-1992 Gary Ellison
203 ;; Last Modified: Mon Jun 29 15:39:35 1992 #5 (Gary Ellison)
204 ;; Optimize line-to-string
205 ;; Implicit/Explicit functions aok
206 ;; More indentation variables
207 ;; Superfluous defun killed.
208 ;; renamed to sh-mode
209 ;;
210 ;; 22-Jun-1992 Gary Ellison
211 ;; Last Modified: Mon Jun 22 15:01:14 1992 #4 (Gary Ellison)
212 ;; Cleanup pre att.emacs posting
213 ;;
214 ;; 19-Jun-1992 Gary Ellison
215 ;; Last Modified: Fri Jun 19 17:19:14 1992 #3 (Gary Ellison)
216 ;; Minimal case indent handling
217 ;;
218 ;; 19-Jun-1992 Gary Ellison
219 ;; Last Modified: Fri Jun 19 16:23:26 1992 #2 (Gary Ellison)
220 ;; Nesting handled except for case statement
221 ;;
222 ;; 19-Jun-1992 Gary Ellison
223 ;; Last Modified: Fri Jun 19 10:03:07 1992 #1 (Gary Ellison)
224 ;; Conception of this mode.
225 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
226
227 (defconst ksh-mode-version "2.6"
228 "*Version numbers of this version of ksh-mode")
229
230 ;;
231 ;; Variables controlling indentation style
232 ;;
233
234 (defvar ksh-indent 2
235 ;; perhaps c-basic-offset would be okay to use as a default, but using
236 ;; default-tab-width as the default is ridiculous --Stig
237 "*Indentation of ksh statements with respect to containing block. A value
238 of nil indicates compound list keyword \(\"do\" and \"then\"\) alignment.")
239 (defvar ksh-case-item-offset ksh-indent
240 "*Additional indentation for case items within a case statement.")
241 (defvar ksh-case-indent nil
242 "*Additional indentation for statements under case items.")
243 (defvar ksh-group-offset (- ksh-indent)
244 "*Additional indentation for keywords \"do\" and \"then\".")
245 (defvar ksh-brace-offset 0
246 "*Additional indentation of \"{\" under functions or brace groupings.")
247 (defvar ksh-multiline-offset 1
248 "*Additional indentation of line that is preceded of a line ending with a
249 \\ to make it continue on next line.")
250 (defvar ksh-match-and-tell t
251 "*If non-nil echo in the minibuffer the matching compound command
252 for the \"done\", \"}\", \"fi\", or \"esac\". ")
253 (defvar ksh-tab-always-indent t
254 "*Controls the operation of the TAB key. If t (the default), always
255 reindent the current line. If nil, indent the current line only if
256 point is at the left margin or in the line's indentation; otherwise
257 insert a tab.")
258
259 (defvar ksh-align-to-keyword nil
260 ;; #### - this is broken, so it should be disabled by default --Stig
261 "*Controls whether nested constructs align from the keyword or
262 the current indentation. If non-nil, indentation will be relative to
263 the column the keyword starts. If nil, indentation will be relative to
264 the current indentation of the line the keyword is on.
265 The default value is nil.
266 The non-nil case doesn't work very well.")
267
268 (defvar ksh-comment-regexp "^\\s *#"
269 "*Regular expression used to recognize comments. Customize to support
270 ksh-like languages.")
271
272 (defun ksh-current-indentation ()
273 nil
274 )
275 ;;
276 (fset 'ksh-current-indentation 'current-column)
277 ;;
278 ;; Variables controlling completion
279 (defvar ksh-completion-list '())
280 (make-variable-buffer-local 'ksh-completion-list)
281 (set-default 'ksh-completion-list '())
282
283 ;;
284 ;; -type- : type number, 0:misc, 1:variable, 2:function
285 ;; -regexp-: regexp used to parse the script
286 ;; -match- : used by match-beginning/end to pickup target
287 ;;
288 (defvar ksh-completion-type-misc 0)
289 (defvar ksh-completion-regexp-var "\\([A-Za-z_0-9]+\\)=")
290 (defvar ksh-completion-type-var 1)
291 (defvar ksh-completion-match-var 1)
292 (defvar ksh-completion-regexp-var2 "\\$\\({\\|{#\\)?\\([A-Za-z_0-9]+\\)[#%:}]?")
293 (defvar ksh-completion-match-var2 2)
294 (defvar ksh-completion-regexp-function
295 "\\(function\\)?[ \t]*\\([A-Za-z_0-9]+\\)[ \t]*([ \t]*)")
296 (defvar ksh-completion-type-function 2)
297 (defvar ksh-completion-match-function 2)
298
299 ;;
300 ;; Variable controlling fontification
301 ;;
302 (defvar ksh-keywords '("for" "in" "do" "done" "select" "case" "esac" "if"
303 "then" "elif" "else" "fi" "while" "until" "function" "time"
304 "alias" "bg" "break" "continue" "cd" "echo" "fc" "fg" "getopts" "jobs" "kill"
305 "let" "newgrp" "print" "pwd" "read" "readonly" "return" "set" "shift" "test"
306 "times" "trap" "typeset" "ulimit" "umask" "unalias" "unset" "wait" "whence"))
307
308 ;; '("\\<function[ \t]+\\([^(; \t]+\\)" 1 font-lock-function-name-face)
309 (defconst ksh-font-lock-keywords
310 (list
311 ;; Fontify [[ ]] expressions
312 '("\\(\\[.*\\]\\)" 1 font-lock-doc-string-face t)
313 ;; Fontify keywords
314 (cons (concat
315 "\\(\\<"
316 (mapconcat 'identity ksh-keywords "\\>\\|\\<")
317 "\\>\\)")
318 1)
319 ;; Fontify function names
320 '("\\<function[ \t]+\\([^(; \t]+\\)" 1 font-lock-function-name-face)
321 '("\\(^[ \t]*[A-Za-z_][A-Za-z_0-9]*[ \t]*()\\)" 1 font-lock-function-name-face)
322 ))
323
324 (put 'ksh-mode 'font-lock-keywords 'ksh-font-lock-keywords)
325
326 ;; XEmacs change -- This can incorrectly set some Perl scripts to
327 ;; ksh-mode. It also won't work for some other shells which ksh-mode
328 ;; nominally works with.
329 ;(defun ksh-check-hook ()
330 ; (save-excursion
331 ; (save-restriction
332 ; (widen)
333 ; (goto-char (point-min))
334 ; (cond ((looking-at "#![ \t]*/.*/k?sh[ \t]*")
335 ; (ksh-mode))))))
336 ;
337 ;(add-hook 'find-file-hooks 'ksh-check-hook)
338
339 ;;
340 ;; Context/indentation regular expressions
341 ;;
342 ;; indenting expressions
343 ;;
344 (defconst ksh-then-do-re "^[^#\n]*\\s\"*\\b\\(then\\|do\\)\\b"
345 "*Regexp used to locate grouping keywords: \"then\" and \"do\"" )
346
347 ;;(defconst ksh-do-re "^[ \t]*\\bdo\\(\\b\\|$\\)"
348 (defconst ksh-do-re "^\\s *\\bdo\\(\\b\\|$\\)"
349 "*Regexp used to match keyword: do")
350
351 (defconst ksh-then-re "^\\s *\\bthen\\(\\b\\|$\\)"
352 "*Regexp used to match keyword: then")
353
354 ;;
355 ;; Structure starting/indenting keywords
356 ;;
357 (defconst ksh-else-re "^\\s *\\belse\\(\\b\\|$\\)"
358 "*Regexp used to match keyword: else")
359
360 (defconst ksh-elif-re "^\\s *\\belif\\(\\b\\|$\\)"
361 "*Regexp used to match keyword: elif")
362
363 (defconst ksh-brace-re "^\\S>*{[ \t\n]"
364 "*Regexp used to match syntactic entity: { ")
365
366 (defconst ksh-case-item-end-re "^\\S>*;;[ \t\n]"
367 "*Regexp used to match case item end syntactic entity: ;;")
368
369 (defconst ksh-keywords-re
370 "^[^#\n]*\\s\"*\\b\\(else\\|if\\|elif\\|case\\|while\\|for\\|until\\|select\\)\\b"
371 "*Regexp used to detect compound command keywords: if, else, elif case,
372 while, for, until, and select")
373
374
375 (defconst ksh-if-re "^[^#\n]*\\s\"*\\b\\(if\\)\\b"
376 "*Regexp used to match keyword: if")
377
378 (defconst ksh-iteration-keywords-re
379 "^[^#\n]*\\s\"*\\b\\(while\\|for\\|until\\|select\\)\\b"
380 "*Match one of the keywords: while, until, for, select")
381
382 (defconst ksh-case-re "^[^#\n]*\\s\"*\\b\\(case\\)\\b"
383 "*Regexp used to match keyword: case")
384
385 (defconst ksh-explicit-func-re
386 "^\\s *\\(function\\s [a-zA-z_][a-zA-Z0-1_]*\\)\\b"
387 "*Match an explicit function definition: function name")
388
389 (defconst ksh-implicit-func-re
390 "^\\s *\\([a-zA-z_][a-zA-Z0-1_]*\\)\\s *()\\s *"
391 "*Match an implicit function definition: name ()")
392
393 (defconst ksh-func-brace-re "^\\s *\\(.*{\\)[ \t\n]+"
394 "*Match a implicit function definition brace: name { ")
395
396 ;;
397 ;; indenting
398 (defconst ksh-case-item-re "^[^#\n]*\\s\"*\\()\\)"
399 "*Regexp used to match case-items including ksh88")
400
401 (defconst ksh-paren-re "^[^#\n]*\\s\"*)[ \t\n]+"
402 "*Regexp used to match compound list & case items")
403
404 ;;
405 ;; structure ending keyword regular expressions
406 (defconst ksh-fi-re "^\\s *fi\\b"
407 "*Regexp used to match keyword: fi")
408
409 (defconst ksh-esac-re "^\\s *esac\\b"
410 "*Regexp used to match keyword: esac")
411
412 (defconst ksh-done-re "^\\s *done\\b"
413 "*Regexp used to match keyword: done")
414
415 (defconst ksh-brace-end-re "^\\s *}\\s *"
416 "*Regexp used to match function brace-groups")
417
418 (defconst ksh-multiline-re "^.*\\\\$"
419 "*Regexp used to match a line with a statement using more lines.")
420
421 ;;
422 ;;
423 ;; Create mode specific tables
424 (defvar ksh-mode-syntax-table nil
425 "Syntax table used while in ksh mode.")
426 (if ksh-mode-syntax-table
427 ()
428 (setq ksh-mode-syntax-table (make-syntax-table))
429 (modify-syntax-entry ?\' "\"" ksh-mode-syntax-table)
430 (modify-syntax-entry ?` "\"" ksh-mode-syntax-table)
431 (modify-syntax-entry ?\n ">" ksh-mode-syntax-table)
432 (modify-syntax-entry ?\f ">" ksh-mode-syntax-table)
433 (modify-syntax-entry ?# "<" ksh-mode-syntax-table)
434 (modify-syntax-entry ?_ "w" ksh-mode-syntax-table)
435 (modify-syntax-entry ?< "." ksh-mode-syntax-table)
436 (modify-syntax-entry ?> "." ksh-mode-syntax-table)
437 (modify-syntax-entry ?& "." ksh-mode-syntax-table)
438 (modify-syntax-entry ?| "." ksh-mode-syntax-table)
439 (modify-syntax-entry ?$ "." ksh-mode-syntax-table)
440 (modify-syntax-entry ?% "." ksh-mode-syntax-table)
441 (modify-syntax-entry ?= "." ksh-mode-syntax-table)
442 (modify-syntax-entry ?/ "." ksh-mode-syntax-table)
443 (modify-syntax-entry ?+ "." ksh-mode-syntax-table)
444 (modify-syntax-entry ?* "." ksh-mode-syntax-table)
445 (modify-syntax-entry ?- "." ksh-mode-syntax-table)
446 (modify-syntax-entry ?\; "." ksh-mode-syntax-table)
447 )
448
449 (defvar ksh-mode-abbrev-table nil
450 "Abbrev table used while in ksh mode.")
451 (define-abbrev-table 'ksh-mode-abbrev-table ())
452
453 (defvar ksh-mode-map nil
454 "Keymap used in ksh mode")
455
456 (if ksh-mode-map
457 ()
458 (setq ksh-mode-map (make-sparse-keymap))
459 (define-key ksh-mode-map "\t" 'ksh-indent-command)
460 ;; (define-key ksh-mode-map "\n" 'reindent-then-newline-and-indent)
461 ;; (define-key ksh-mode-map '[return] 'reindent-then-newline-and-indent)
462 ;; (define-key ksh-mode-map "\t" 'ksh-indent-line)
463 ;; (define-key ksh-mode-map "\177" 'backward-delete-char-untabify)
464 (define-key ksh-mode-map "\C-j" 'reindent-then-newline-and-indent)
465 (define-key ksh-mode-map "\e\t" 'ksh-complete-symbol)
466 (define-key ksh-mode-map "\C-c\t" 'ksh-completion-init-and-pickup)
467 )
468
469
470 ;;;###autoload
471 (defun ksh-mode ()
472 "ksh-mode 2.6 - Major mode for editing (Bourne, Korn or Bourne again)
473 shell scripts.
474 Special key bindings and commands:
475 \\{ksh-mode-map}
476 Variables controlling indentation style:
477 ksh-indent
478 Indentation of ksh statements with respect to containing block.
479 Default value is 2.
480 ksh-case-indent
481 Additional indentation for statements under case items.
482 Default value is nil which will align the statements one position
483 past the \")\" of the pattern.
484 ksh-case-item-offset
485 Additional indentation for case items within a case statement.
486 Default value is 2.
487 ksh-group-offset
488 Additional indentation for keywords \"do\" and \"then\".
489 Default value is -2.
490 ksh-brace-offset
491 Additional indentation of \"{\" under functions or brace groupings.
492 Default value is 0.
493 ksh-multiline-offset
494 Additional indentation of line that is preceded of a line ending with a
495 \\ to make it continue on next line.
496 ksh-tab-always-indent
497 Controls the operation of the TAB key. If t (the default), always
498 reindent the current line. If nil, indent the current line only if
499 point is at the left margin or in the line's indentation; otherwise
500 insert a tab.
501 ksh-match-and-tell
502 If non-nil echo in the minibuffer the matching compound command
503 for the \"done\", \"}\", \"fi\", or \"esac\". Default value is t.
504
505 ksh-align-to-keyword
506 Controls whether nested constructs align from the keyword or
507 the current indentation. If non-nil, indentation will be relative to
508 the column the keyword starts. If nil, indentation will be relative to
509 the current indentation of the line the keyword is on.
510 The default value is non-nil.
511
512 ksh-comment-regexp
513 Regular expression used to recognize comments. Customize to support
514 ksh-like languages. Default value is \"\^\\\\s *#\".
515
516 Style Guide.
517 By setting
518 (setq ksh-indent default-tab-width)
519 (setq ksh-group-offset 0)
520
521 The following style is obtained:
522
523 if [ -z $foo ]
524 then
525 bar # <-- ksh-group-offset is additive to ksh-indent
526 foo
527 fi
528
529 By setting
530 (setq ksh-indent default-tab-width)
531 (setq ksh-group-offset (- 0 ksh-indent))
532
533 The following style is obtained:
534
535 if [ -z $foo ]
536 then
537 bar
538 foo
539 fi
540
541 By setting
542 (setq ksh-case-item-offset 1)
543 (setq ksh-case-indent nil)
544
545 The following style is obtained:
546
547 case x in *
548 foo) bar # <-- ksh-case-item-offset
549 baz;; # <-- ksh-case-indent aligns with \")\"
550 foobar) foo
551 bar;;
552 esac
553
554 By setting
555 (setq ksh-case-item-offset 1)
556 (setq ksh-case-indent 6)
557
558 The following style is obtained:
559
560 case x in *
561 foo) bar # <-- ksh-case-item-offset
562 baz;; # <-- ksh-case-indent
563 foobar) foo
564 bar;;
565 esac
566
567
568 Installation:
569 Put ksh-mode.el in some directory in your load-path.
570 Put the following forms in your .emacs file.
571
572 (setq auto-mode-alist
573 (append auto-mode-alist
574 (list
575 '(\"\\\\.sh$\" . ksh-mode)
576 '(\"\\\\.ksh$\" . ksh-mode)
577 '(\"\\\\.bashrc\" . ksh-mode)
578 '(\"\\\\..*profile\" . ksh-mode))))
579
580 (setq ksh-mode-hook
581 (function (lambda ()
582 (font-lock-mode 1) ;; font-lock the buffer
583 (setq ksh-indent 8)
584 (setq ksh-group-offset -8))
585 (setq ksh-brace-offset -8)
586 (setq ksh-tab-always-indent t)
587 (setq ksh-match-and-tell t)
588 (setq ksh-align-to-keyword t) ;; Turn on keyword alignment
589 )))"
590 (interactive)
591 (kill-all-local-variables)
592 (use-local-map ksh-mode-map)
593 (setq major-mode 'ksh-mode)
594 (setq mode-name "Ksh")
595 (setq local-abbrev-table ksh-mode-abbrev-table)
596 (set-syntax-table ksh-mode-syntax-table)
597 (make-local-variable 'indent-line-function)
598 (setq indent-line-function 'ksh-indent-line)
599 (make-local-variable 'indent-region-function)
600 (setq indent-region-function 'ksh-indent-region)
601 (make-local-variable 'comment-start)
602 (setq comment-start "# ")
603 (make-local-variable 'comment-end)
604 (setq comment-end "")
605 (make-local-variable 'comment-column)
606 (setq comment-column 32)
607 (make-local-variable 'comment-start-skip)
608 (setq comment-start-skip "#+ *")
609 ;;
610 ;; config font-lock mode
611 (make-local-variable 'font-lock-keywords)
612 (setq font-lock-keywords ksh-font-lock-keywords)
613 ;;
614 ;; Let the user customize
615 (run-hooks 'ksh-mode-hook)
616 (if (not ksh-align-to-keyword)
617 (ksh-align-to-keyword -1)
618 )
619 ) ;; defun
620
621 ;;
622 ;; Support functions
623
624 (defun ksh-align-to-keyword (&optional arg)
625 "Toggle value of ksh-align-to-keyword and rebind the ksh-current-indentation
626 function. With arg, force alignment to keyword if and only if arg is positive."
627 (interactive)
628 (if (null arg) ;just toggle
629 (cond ((not ksh-align-to-keyword)
630 (setq ksh-align-to-keyword t)
631 (fset 'ksh-current-indentation 'current-column))
632 (t
633 (setq ksh-align-to-keyword nil)
634 (fset 'ksh-current-indentation 'current-indentation))
635 )
636 (cond ((natnump arg)
637 (setq ksh-align-to-keyword t)
638 (fset 'ksh-current-indentation 'current-column))
639 (t
640 (setq ksh-align-to-keyword nil)
641 (fset 'ksh-current-indentation 'current-indentation))
642 ))
643 )
644
645 (defun ksh-current-line ()
646 "Return the vertical position of point in the buffer.
647 Top line is 1."
648 (+ (count-lines (point-min) (point))
649 (if (= (current-column) 0) 1 0))
650 )
651
652
653 (defun ksh-line-to-string ()
654 "From point, construct a string from all characters on
655 current line"
656 (skip-chars-forward " \t") ;; skip tabs as well as spaces
657 (buffer-substring (point)
658 (progn
659 (end-of-line 1)
660 (point))))
661
662 (defun ksh-get-nest-level ()
663 "Return a 2 element list (nest-level nest-line) describing where the
664 current line should nest."
665 (let ((case-fold-search)
666 (level))
667 (save-excursion
668 (forward-line -1)
669 (while (and (not (bobp))
670 (null level))
671 (if (and (not (looking-at "^\\s *$"))
672 (not (save-excursion
673 (forward-line -1)
674 (beginning-of-line)
675 (looking-at ksh-multiline-re)))
676 (not (looking-at ksh-comment-regexp)))
677 (setq level (cons (current-indentation)
678 (ksh-current-line)))
679 (forward-line -1)
680 );; if
681 );; while
682 (if (null level)
683 (cons (current-indentation) (ksh-current-line))
684 level)
685 )
686 )
687 )
688
689 (defun ksh-looking-at-compound-list ()
690 "Return true if current line contains compound list initiating keyword"
691 (or
692 (looking-at ksh-do-re)
693 (looking-at ksh-then-re)
694 ) ;; or
695 ) ;; defun
696
697 (defun ksh-looking-at-case-item ()
698 "Return true if current line is a case-item .vs. paren compound list"
699 (save-excursion
700 (beginning-of-line)
701 ;;
702 ;; Handle paren indentation constructs for this line
703 (cond ((looking-at ksh-paren-re)
704 (goto-line (cdr (ksh-get-nest-level)))
705 ;;
706 ;; The question is whether this is really a case item or just
707 ;; parenthesized compound list.
708 (cond ((or (looking-at ksh-case-re)
709 (looking-at ksh-case-item-end-re)))
710 ;;
711 ;; turns out to be a parenthesized compound list
712 ;; so propigate the nil for cond
713 )
714 ))
715 )
716 ) ;; defun
717
718 (defun ksh-get-case-indent ()
719 "Return the column of the closest open case statement"
720 (save-excursion
721 (let (
722 (nest-list (ksh-get-compound-level ksh-case-re ksh-esac-re (point)))
723 )
724 (if (null nest-list)
725 (progn
726 (if ksh-match-and-tell
727 (message "No matching case for ;;"))
728 0)
729 (car nest-list)))
730 )
731 )
732
733 ;;
734 ;; Functions which make this mode what it is
735 ;;
736
737 (defun ksh-get-nester-column (nest-line)
738 "Return the column to indent to with respect to nest-line taking
739 into consideration keywords and other nesting constructs."
740 (save-excursion
741 (let ((fence-post)
742 (nester-column)
743 (case-fold-search)
744 (start-line (ksh-current-line)))
745 ;;
746 ;; Handle case item indentation constructs for this line
747 (cond ((ksh-looking-at-case-item)
748 (save-excursion
749 (goto-line nest-line)
750 (let ((fence-post (save-excursion (end-of-line) (point))))
751 ;;
752 ;; Now know there is a case-item so detect whether
753 ;; it is first under case, just another case-item, or
754 ;; a case-item and case-item-end all rolled together.
755 ;;
756 (cond ((re-search-forward ksh-case-re fence-post t)
757 (goto-char (match-beginning 1))
758 (+ (ksh-current-indentation) ksh-case-item-offset))
759
760 ((ksh-looking-at-case-item)
761 (current-indentation))
762
763 ((looking-at ksh-case-item-end-re)
764 (end-of-line)
765 (+ (ksh-get-case-indent) ksh-case-item-offset))
766 )
767 )))
768 (t;; Not a case-item. What to do relative to the nest-line?
769 (save-excursion
770 (goto-line nest-line)
771 (setq fence-post (save-excursion (end-of-line) (point)))
772 (setq nester-column
773 (save-excursion
774 (cond
775 ;;
776 ;; Check if we are in a continued statement
777 ((and (looking-at ksh-multiline-re)
778 (save-excursion
779 (goto-line (1- start-line))
780 (looking-at ksh-multiline-re)))
781 (+ (current-indentation) ksh-multiline-offset))
782
783 ;; In order to locate the column of the keyword,
784 ;; which might be embedded within a case-item,
785 ;; it is necessary to use re-search-forward.
786 ;; Search by literal case, since shell is
787 ;; case-sensitive.
788 ((re-search-forward ksh-keywords-re fence-post t)
789 (goto-char (match-beginning 1))
790 (if (looking-at ksh-case-re)
791 (+ (ksh-current-indentation) ksh-case-item-offset)
792 (+ (ksh-current-indentation)
793 (if (null ksh-indent)
794 2 ksh-indent)
795 )))
796
797 ((re-search-forward ksh-then-do-re fence-post t)
798 (if (null ksh-indent)
799 (progn
800 (goto-char (match-end 1))
801 (+ (ksh-current-indentation) 1))
802 (progn
803 (goto-char (match-beginning 1))
804 (+ (ksh-current-indentation) ksh-indent))
805 ))
806
807 ((looking-at ksh-brace-re)
808 (+ (current-indentation)
809 (if (null ksh-indent)
810 2 ksh-indent)
811 ))
812 ;;
813 ;; Forces functions to first column
814 ((or (looking-at ksh-implicit-func-re)
815 (looking-at ksh-explicit-func-re))
816 (if (looking-at ksh-func-brace-re)
817 (if (null ksh-indent)
818 2 ksh-indent)
819 ksh-brace-offset))
820
821 ;;
822 ;; Need to first detect the end of a case-item
823 ((looking-at ksh-case-item-end-re)
824 (end-of-line)
825 (+ (ksh-get-case-indent) ksh-case-item-offset))
826 ;;
827 ;; Now detect first statement under a case item
828 ((ksh-looking-at-case-item)
829 (if (null ksh-case-indent)
830 (progn
831 (re-search-forward ksh-case-item-re fence-post t)
832 (goto-char (match-end 1))
833 (+ (current-column) 1))
834 (+ (current-indentation) ksh-case-indent)))
835
836 ;; This is hosed when using current-column
837 ;; and there is a multi-command expression as the
838 ;; nester.
839 (t (current-indentation)))
840 )
841 ));; excursion over
842 ;;
843 ;; Handle additional indentation constructs for this line
844 (cond ((ksh-looking-at-compound-list)
845 (+ nester-column ksh-group-offset))
846 ((looking-at ksh-brace-re)
847 (+ nester-column ksh-brace-offset))
848 (t nester-column))
849 );; Not a case-item
850 )
851 );;let
852 );; excursion
853 );; defun
854
855 (defun ksh-indent-command ()
856 "Indent current line relative to containing block and allow for
857 ksh-tab-always-indent customization"
858 (interactive)
859 (let (case-fold-search)
860 (cond ((save-excursion
861 (skip-chars-backward " \t")
862 (bolp))
863 (ksh-indent-line))
864 (ksh-tab-always-indent
865 (save-excursion
866 (ksh-indent-line)))
867 (t (insert-tab))
868 ))
869 )
870
871
872 (defun ksh-indent-line ()
873 "Indent current line as far as it should go according
874 to the syntax/context"
875 (interactive)
876 (let (case-fold-search)
877 (save-excursion
878 (beginning-of-line)
879 (if (bobp)
880 nil
881 ;;
882 ;; Align this line to current nesting level
883 (let*
884 (
885 (level-list (ksh-get-nest-level)) ; Where to nest against
886 ;; (last-line-level (car level-list))
887 (this-line-level (current-indentation))
888 (nester-column (ksh-get-nester-column (cdr level-list)))
889 (struct-match (ksh-match-structure-and-reindent))
890 )
891 (if struct-match
892 (setq nester-column struct-match))
893 (if (eq nester-column this-line-level)
894 nil
895 (beginning-of-line)
896 (let ((beg (point)))
897 (back-to-indentation)
898 (delete-region beg (point)))
899 (indent-to nester-column))
900 );; let*
901 );; if
902 );; excursion
903 ;;
904 ;; Position point on this line
905 (let*
906 (
907 (this-line-level (current-indentation))
908 (this-bol (save-excursion
909 (beginning-of-line)
910 (point)))
911 (this-point (- (point) this-bol))
912 )
913 (cond ((> this-line-level this-point);; point in initial white space
914 (back-to-indentation))
915 (t nil)
916 );; cond
917 );; let*
918 );; let
919 );; defun
920
921
922 (defun ksh-match-indent-level (begin-re end-re)
923 "Match the compound command and indent. Return nil on no match,
924 indentation to use for this line otherwise."
925 (interactive)
926 (let* ((case-fold-search)
927 (nest-list
928 (save-excursion
929 (ksh-get-compound-level begin-re end-re (point))
930 ))
931 ) ;; bindings
932 (if (null nest-list)
933 (progn
934 (if ksh-match-and-tell
935 (message "No matching compound command"))
936 nil) ;; Propagate a miss.
937 (let* (
938 (nest-level (car nest-list))
939 (match-line (cdr nest-list))
940 ) ;; bindings
941 (if ksh-match-and-tell
942 (save-excursion
943 (goto-line match-line)
944 (message "Matched ... %s" (ksh-line-to-string))
945 ) ;; excursion
946 ) ;; if ksh-match-and-tell
947 nest-level ;;Propagate a hit.
948 ) ;; let*
949 ) ;; if
950 ) ;; let*
951 ) ;; defun ksh-match-indent-level
952
953 (defun ksh-match-structure-and-reindent ()
954 "If the current line matches one of the indenting keywords
955 or one of the control structure ending keywords then reindent. Also
956 if ksh-match-and-tell is non-nil the matching structure will echo in
957 the minibuffer"
958 (interactive)
959 (let (case-fold-search)
960 (save-excursion
961 (beginning-of-line)
962 (cond ((looking-at ksh-else-re)
963 (ksh-match-indent-level ksh-if-re ksh-fi-re))
964 ((looking-at ksh-elif-re)
965 (ksh-match-indent-level ksh-if-re ksh-fi-re))
966 ((looking-at ksh-fi-re)
967 (ksh-match-indent-level ksh-if-re ksh-fi-re))
968 ((looking-at ksh-done-re)
969 (ksh-match-indent-level ksh-iteration-keywords-re ksh-done-re))
970 ((looking-at ksh-esac-re)
971 (ksh-match-indent-level ksh-case-re ksh-esac-re))
972 ;;
973 ((looking-at ksh-brace-end-re)
974 (cond
975 ((ksh-match-indent-level ksh-implicit-func-re ksh-brace-end-re))
976 ((ksh-match-indent-level ksh-explicit-func-re ksh-brace-end-re))
977 ((ksh-match-indent-level ksh-func-brace-re ksh-brace-end-re))
978 (t nil)))
979 (t nil)
980 );; cond
981 )
982 ))
983
984 (defun ksh-get-compound-level
985 (begin-re end-re anchor-point &optional balance-list)
986 "Determine how much to indent this structure. Return a list (level line)
987 of the matching compound command or nil if no match found."
988 (let*
989 (;; Locate the next compound begin keyword bounded by point-min
990 (match-point (if (re-search-backward begin-re (point-min) t)
991 (match-beginning 1) 0))
992 (nest-column (if (zerop match-point)
993 1
994 (progn
995 (goto-char match-point)
996 (ksh-current-indentation))))
997 (nest-list (cons 0 0)) ;; sentinel cons since cdr is >= 1
998 )
999 (if (zerop match-point)
1000 nil ;; graceful exit from recursion
1001 (progn
1002 (if (nlistp balance-list)
1003 (setq balance-list (list)))
1004 ;; Now search forward from matching start keyword for end keyword
1005 (while (and (consp nest-list) (zerop (cdr nest-list))
1006 (re-search-forward end-re anchor-point t))
1007 (if (not (memq (point) balance-list))
1008 (progn
1009 (setq balance-list (cons (point) balance-list))
1010 (goto-char match-point) ;; beginning of compound cmd
1011 (setq nest-list
1012 (ksh-get-compound-level begin-re end-re
1013 anchor-point balance-list))
1014 )))
1015
1016 (cond ((consp nest-list)
1017 (if (zerop (cdr nest-list))
1018 (progn
1019 (goto-char match-point)
1020 (cons nest-column (ksh-current-line)))
1021 nest-list))
1022 (t nil)
1023 )
1024 )
1025 )
1026 )
1027 )
1028
1029
1030 (defun ksh-indent-region (start end)
1031 "From start to end, indent each line."
1032 ;; The algorithm is just moving through the region line by line with
1033 ;; the match noise turned off. Only modifies nonempty lines.
1034 (save-excursion
1035 (let (ksh-match-and-tell
1036 (endmark (copy-marker end)))
1037
1038 (goto-char start)
1039 (beginning-of-line)
1040 (setq start (point))
1041 (while (> (marker-position endmark) start)
1042 (if (not (and (bolp) (eolp)))
1043 (ksh-indent-line))
1044 (forward-line 1)
1045 (setq start (point)))
1046
1047 (set-marker endmark nil)
1048 )
1049 )
1050 )
1051
1052 ;;
1053 ;; Completion code supplied by Haavard Rue <hrue@imf.unit.no>.
1054 ;;
1055 ;;
1056 ;; add a completion with a given type to the list
1057 ;;
1058 (defun ksh-addto-alist (completion type)
1059 (setq ksh-completion-list
1060 (append ksh-completion-list
1061 (list (cons completion type)))))
1062 ;;
1063 ;; init the list and pickup all
1064 ;;
1065 (defun ksh-completion-init-and-pickup ()
1066 (interactive)
1067 (let (case-fold-search)
1068 (ksh-completion-list-init)
1069 (ksh-pickup-all)))
1070
1071 ;;
1072 ;; init the list
1073 ;;
1074 (defun ksh-completion-list-init ()
1075 (interactive)
1076 (setq ksh-completion-list
1077 (list
1078 (cons "if" ksh-completion-type-misc)
1079 (cons "while" ksh-completion-type-misc)
1080 (cons "until" ksh-completion-type-misc)
1081 (cons "select" ksh-completion-type-misc)
1082 (cons "for" ksh-completion-type-misc)
1083 (cons "continue" ksh-completion-type-misc)
1084 (cons "function" ksh-completion-type-misc)
1085 (cons "fi" ksh-completion-type-misc)
1086 (cons "case" ksh-completion-type-misc)
1087 (cons "esac" ksh-completion-type-misc)
1088 (cons "break" ksh-completion-type-misc)
1089 (cons "exit" ksh-completion-type-misc)
1090 (cons "done" ksh-completion-type-misc)
1091 (cons "do" ksh-completion-type-misc))))
1092
1093 (defun ksh-eol-point ()
1094 (save-excursion
1095 (end-of-line)
1096 (point)))
1097
1098 (defun ksh-bol-point ()
1099 (save-excursion
1100 (beginning-of-line)
1101 (point)))
1102
1103 (defun ksh-pickup-all ()
1104 "Pickup all completions in buffer."
1105 (interactive)
1106 (ksh-pickup-completion-driver (point-min) (point-max) t))
1107
1108 (defun ksh-pickup-this-line ()
1109 "Pickup all completions in current line."
1110 (interactive)
1111 (ksh-pickup-completion-driver (ksh-bol-point) (ksh-eol-point) nil))
1112
1113 (defun ksh-pickup-completion-driver (pmin pmax message)
1114 "Driver routine for ksh-pickup-completion."
1115 (if message
1116 (message "pickup completion..."))
1117 (let* (
1118 (i1
1119 (ksh-pickup-completion ksh-completion-regexp-var
1120 ksh-completion-type-var
1121 ksh-completion-match-var
1122 pmin pmax))
1123 (i2
1124 (ksh-pickup-completion ksh-completion-regexp-var2
1125 ksh-completion-type-var
1126 ksh-completion-match-var2
1127 pmin pmax))
1128 (i3
1129 (ksh-pickup-completion ksh-completion-regexp-function
1130 ksh-completion-type-function
1131 ksh-completion-match-function
1132 pmin pmax)))
1133 (if message
1134 (message "pickup %d variables and %d functions." (+ i1 i2) i3))))
1135
1136 (defun ksh-pickup-completion (regexp type match pmin pmax)
1137 "Pickup completion in region and addit to the list, if not already
1138 there."
1139 (let ((i 0) kw obj)
1140 (save-excursion
1141 (goto-char pmin)
1142 (while (and
1143 (re-search-forward regexp pmax t)
1144 (match-beginning match)
1145 (setq kw (buffer-substring
1146 (match-beginning match)
1147 (match-end match))))
1148 (progn
1149 (setq obj (assoc kw ksh-completion-list))
1150 (if (or (equal nil obj)
1151 (and (not (equal nil obj))
1152 (not (= type (cdr obj)))))
1153 (progn
1154 (setq i (1+ i))
1155 (ksh-addto-alist kw type))))))
1156 i))
1157
1158 (defun ksh-complete-symbol ()
1159 "Perform completion."
1160 (interactive)
1161 (let* ((case-fold-search)
1162 (end (point))
1163 (beg (unwind-protect
1164 (save-excursion
1165 (backward-sexp 1)
1166 (while (= (char-syntax (following-char)) ?\')
1167 (forward-char 1))
1168 (point))))
1169 (pattern (buffer-substring beg end))
1170 (predicate
1171 ;;
1172 ;; ` or $( mark a function
1173 ;;
1174 (save-excursion
1175 (goto-char beg)
1176 (if (or
1177 (save-excursion
1178 (backward-char 1)
1179 (looking-at "`"))
1180 (save-excursion
1181 (backward-char 2)
1182 (looking-at "\\$(")))
1183 (function (lambda (sym)
1184 (equal (cdr sym) ksh-completion-type-function)))
1185 ;;
1186 ;; a $, ${ or ${# mark a variable
1187 ;;
1188 (if (or
1189 (save-excursion
1190 (backward-char 1)
1191 (looking-at "\\$"))
1192 (save-excursion
1193 (backward-char 2)
1194 (looking-at "\\${"))
1195 (save-excursion
1196 (backward-char 3)
1197 (looking-at "\\${#")))
1198 (function (lambda (sym)
1199 (equal (cdr sym)
1200 ksh-completion-type-var)))
1201 ;;
1202 ;; don't know. use 'em all
1203 ;;
1204 (function (lambda (sym) t))))))
1205 ;;
1206 (completion (try-completion pattern ksh-completion-list predicate)))
1207 ;;
1208 (cond ((eq completion t))
1209 ;;
1210 ;; oops, what is this ?
1211 ;;
1212 ((null completion)
1213 (message "Can't find completion for \"%s\"" pattern))
1214 ;;
1215 ;; insert
1216 ;;
1217 ((not (string= pattern completion))
1218 (delete-region beg end)
1219 (insert completion))
1220 ;;
1221 ;; write possible completion in the minibuffer,
1222 ;; use this instead of a seperate buffer (usual)
1223 ;;
1224 (t
1225 (let ((list (all-completions pattern ksh-completion-list predicate))
1226 (string ""))
1227 (while list
1228 (progn
1229 (setq string (concat string (format "%s " (car list))))
1230 (setq list (cdr list))))
1231 (message string))))))
1232
1233 (provide 'ksh-mode)
1234 ;;; ksh-mode.el ends here