Mercurial > hg > xemacs-beta
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 |