home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / editors / emacs / xemacs / xemacs-1.006 / xemacs-1 / lib / xemacs-19.13 / lisp / modes / ksh-mode.el < prev    next >
Encoding:
Text File  |  1995-08-18  |  38.7 KB  |  1,235 lines

  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
  1235.