1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-22 11:17:19 +00:00
freebsd/contrib/tcsh/csh-mode.el
2004-07-11 02:17:56 +00:00

936 lines
29 KiB
EmacsLisp

;; csh-mode.el --- csh (and tcsh) script editing mode for Emacs.
;;
;; Version: 1.2
;; Date: April 2, 1999
;; Maintainer: Dan Harkless <software@harkless.org>
;;
;; Description:
;; csh and tcsh script editing mode for Emacs.
;;
;; Installation:
;; Put csh-mode.el in some directory in your load-path and load it.
;;
;; Usage:
;; This major mode assists shell script writers with indentation
;; control and control structure construct matching in much the same
;; fashion as other programming language modes. Invoke describe-mode
;; for more information.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Author key:
;; DH - Dan Harkless <software@harkless.org>
;; CM - Carlo Migliorini <migliorini@sodalia.it>
;; JR - Jack Repenning <jackr@sgi.com>
;; GE - Gary Ellison <Gary.F.Ellison@att.com>
;;
;; *** REVISION HISTORY ***
;;
;; DATE MOD. BY REASON FOR MODIFICATION
;; --------- -- --------------------------------------------------------------
;; 2 Apr 99 DH 1.2: Noticed an out-of-date comment referencing .bashrc etc.
;; 11 Dec 96 DH 1.1: ksh-mode just indented continuation lines by 1 space.
;; csh-mode looks at the first line and indents properly to line
;; up under the open-paren, quote, or command.
;; 11 Dec 96 DH Added fontification for history substitutions.
;; 10 Dec 96 DH Added indentation and fontification for labels. Added
;; fontification for variables and backquoted strings.
;; 9 Dec 96 DH 1.0: Brought csh-mode up to the level of functionality of
;; the original ksh-mode.
;; 7 Oct 96 CM 0.1: Hacked ksh-mode.el into minimally functional csh-mode.el
;; by doing search-and-replace and some keyword changes.
;; 8 Aug 96 JR (Last modification to ksh-mode 2.6.)
;; [...]
;; 19 Jun 92 GE (Conception of ksh-mode.)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconst csh-mode-version "1.2"
"*Version number of this version of csh-mode")
(defvar csh-mode-hook
'(lambda ()
(auto-fill-mode 1))
"Hook to run each time csh-mode is entered.")
;;
;; -------------------------------------------> Variables controlling completion
;;
(defvar csh-completion-list '())
(make-variable-buffer-local 'csh-completion-list)
(set-default 'csh-completion-list '())
;;
;; -type- : type number, 0:misc, 1:variable, 2:function
;; -regexp-: regexp used to parse the script
;; -match- : used by match-beginning/end to pickup target
;;
(defvar csh-completion-type-misc 0)
(defvar csh-completion-regexp-var "\\([A-Za-z_0-9]+\\)=")
(defvar csh-completion-type-var 1)
(defvar csh-completion-match-var 1)
(defvar csh-completion-regexp-var2 "\\$\\({\\|{#\\)?\\([A-Za-z_0-9]+\\)[#%:}]?")
(defvar csh-completion-match-var2 2)
(defvar csh-completion-regexp-function
"\\(function\\)?[ \t]*\\([A-Za-z_0-9]+\\)[ \t]*([ \t]*)")
(defvar csh-completion-type-function 2)
(defvar csh-completion-match-function 2)
;;
;; ------------------------------------> Variables controlling indentation style
;;
(defvar csh-indent 4
"*Indentation of csh statements with respect to containing block. A value
of nil indicates compound list keyword \(\"do\" and \"then\"\) alignment.")
(defvar csh-case-item-offset csh-indent
"*Additional indentation for case items within a case statement.")
(defvar csh-case-indent nil
"*Additional indentation for statements under case items.")
(defvar csh-comment-regexp "^\\s *#"
"*Regular expression used to recognize comments. Customize to support
csh-like languages.")
(defvar csh-match-and-tell t
"*If non-nil echo in the minibuffer the matching compound command
for the \"breaksw\", \"end\", or \"endif\".")
(defvar csh-tab-always-indent t
"*Controls the operation of the TAB key. If t (the default), always
reindent the current line. If nil, indent the current line only if
point is at the left margin or in the line's indentation; otherwise
insert a tab.")
;;
;; ----------------------------------------> Constants containing syntax regexps
;;
(defconst csh-case-default-re
"^\\s *\\(case\\|default\\)\\b"
"Regexp used to locate grouping keywords case and default" )
(defconst csh-case-item-re "^\\s *\\(case .*\\|default\\):"
"Regexp used to match case-items")
(defconst csh-end-re "^\\s *end\\b"
"Regexp used to match keyword: end")
(defconst csh-endif-re "^\\s *endif\\b"
"Regexp used to match keyword: endif")
(defconst csh-endsw-re "^\\s *endsw\\b"
"Regexp used to match keyword: endsw")
(defconst csh-else-re "^\\s *\\belse\\(\\b\\|$\\)"
"Regexp used to match keyword: else")
(defconst csh-else-if-re "^\\s *\\belse if\\(\\b\\|$\\)"
"Regexp used to match keyword pair: else if")
(defconst csh-if-re "^\\s *if\\b.+\\(\\\\\\|\\bthen\\b\\)"
"Regexp used to match non-one-line if statements")
(defconst csh-iteration-keywords-re "^[^#\n]*\\s\"*\\b\\(while\\|foreach\\)\\b"
"Match one of the keywords: while, foreach")
(defconst csh-keywords-re
"^\\s *\\(else\\b\\|foreach\\b\\|if\\b.+\\(\\\\\\|\\bthen\\b\\)\\|switch\\b\\|while\\b\\)"
"Regexp used to detect compound command keywords: else, if, foreach, while")
(defconst csh-label-re "^\\s *[^!#$\n ]+:"
"Regexp used to match flow-control labels")
(defconst csh-multiline-re "^.*\\\\$"
"Regexp used to match a line with a statement using more lines.")
(defconst csh-switch-re "^\\s *switch\\b"
"Regexp used to match keyword: switch")
;;
;; ----------------------------------------> Variables controlling fontification
;;
(defvar csh-keywords '("@" "alias" "bg" "break" "breaksw" "case" "cd" "chdir"
"continue" "default" "dirs" "echo" "else" "end" "endif"
"endsw" "eval" "exec" "exit" "fg" "foreach" "glob" "goto"
"hashstat" "history" "if" "jobs" "kill" "limit" "login"
"logout" "limit" "notify" "onintr" "popd" "printenv"
"pushd" "rehash" "repeat" "set" "setenv" "shift" "source"
"stop" "suspend" "switch" "then" "time" "umask" "unalias"
"unhash" "unlimit" "unset" "unsetenv" "wait" "while"
;; tcsh-keywords
"alloc" "bindkey" "builtins" "complete" "echotc"
"filetest" "hup" "log" "ls-F" "nice" "nohup" "sched"
"settc" "setty" "telltc" "uncomplete" "where" "which"))
(require 'font-lock) ; need to do this before referring to font-lock-* below
(defconst csh-font-lock-keywords
;; NOTE: The order of some of the items in this list is significant. Do not
;; alphabetize or otherwise blindly rearrange.
(list
;; Comments on line 1, which are missed by syntactic fontification.
'("^#.*" 0 font-lock-comment-face)
;; Label definitions (1 means first parenthesized exp in regexp).
'("^\\s *\\([^!#$\n ]+\\):" 1 font-lock-function-name-face)
;; Label references.
'("\\b\\(goto\\|onintr\\)\\b\\s +\\([^!#$ \n\t]+\\)"
2 font-lock-function-name-face)
;; Variable settings.
'("\\(@\\|set\\|setenv\\)\\s +\\([0-9A-Za-z_]+\\b\\)"
2 font-lock-variable-name-face)
;; Variable references not inside of strings.
'("\\$[][0-9A-Za-z_#:?]+" 0 font-lock-variable-name-face)
;; Backquoted strings. 'keep' means to just fontify non-fontified text.
'("`\\(.*\\)`" 1 font-lock-reference-face keep)
;; NOTE: The following variables need to be anchored to the beginning of
;; line to prevent re-fontifying text in comments. Due to this, we
;; can only catch a finite number of occurrences. More can be added.
;; The 't' means to override previous fontification.
;;
;; Variable references inside of " strings.
'("^[^#\n]*\".*\\(\\$[][0-9A-Za-z_#:?]+\\).*\""
1 font-lock-variable-name-face t) ; 1
'("^[^#\n]*\".*\\(\\$[][0-9A-Za-z_#:?]+\\).*\\$[][0-9A-Za-z_#:?]+.*\""
1 font-lock-variable-name-face t) ; 2
(cons (concat "^[^#\n]*\".*\\(\\$[][0-9A-Za-z_#:?]+\\).*"
"\\$[][0-9A-Za-z_#:?]+.*\\$[][0-9A-Za-z_#:?]+.*\"")
(list 1 font-lock-variable-name-face t)) ; 3
;;
;; History substitutions.
'("^![^~= \n\t]+" 0 font-lock-reference-face t) ; BOL
'("^[^#\n]*[^#\\\n]\\(![^~= \n\t]+\\)" 1 font-lock-reference-face t) ; 1
'("^[^#\n]*[^#\\\n]\\(![^~= \n\t]+\\).*![^~= \n\t]+"
1 font-lock-reference-face t) ; 2
;; Keywords.
(cons (concat
"\\(\\<"
(mapconcat 'identity csh-keywords "\\>\\|\\<")
"\\>\\)")
1)
))
(put 'csh-mode 'font-lock-keywords 'csh-font-lock-keywords)
;;
;; -------------------------------------------------------> Mode-specific tables
;;
(defvar csh-mode-abbrev-table nil
"Abbrev table used while in csh mode.")
(define-abbrev-table 'csh-mode-abbrev-table ())
(defvar csh-mode-map nil
"Keymap used in csh mode")
(if csh-mode-map
()
(setq csh-mode-map (make-sparse-keymap))
;;(define-key csh-mode-map "\177" 'backward-delete-char-untabify)
(define-key csh-mode-map "\C-c\t" 'csh-completion-init-and-pickup)
(define-key csh-mode-map "\C-j" 'reindent-then-newline-and-indent)
(define-key csh-mode-map "\e\t" 'csh-complete-symbol)
(define-key csh-mode-map "\n" 'reindent-then-newline-and-indent)
(define-key csh-mode-map '[return] 'reindent-then-newline-and-indent)
(define-key csh-mode-map "\t" 'csh-indent-command)
;;(define-key csh-mode-map "\t" 'csh-indent-line)
)
(defvar csh-mode-syntax-table nil
"Syntax table used while in csh mode.")
(if csh-mode-syntax-table
;; If it's already set up, don't change it.
()
;; Else, create it from the standard table and modify entries that need to be.
(setq csh-mode-syntax-table (make-syntax-table))
(modify-syntax-entry ?& "." csh-mode-syntax-table) ; & -punctuation
(modify-syntax-entry ?* "." csh-mode-syntax-table) ; * -punctuation
(modify-syntax-entry ?- "." csh-mode-syntax-table) ; - -punctuation
(modify-syntax-entry ?= "." csh-mode-syntax-table) ; = -punctuation
(modify-syntax-entry ?+ "." csh-mode-syntax-table) ; + -punctuation
(modify-syntax-entry ?| "." csh-mode-syntax-table) ; | -punctuation
(modify-syntax-entry ?< "." csh-mode-syntax-table) ; < -punctuation
(modify-syntax-entry ?> "." csh-mode-syntax-table) ; > -punctuation
(modify-syntax-entry ?/ "." csh-mode-syntax-table) ; / -punctuation
(modify-syntax-entry ?\' "\"" csh-mode-syntax-table) ; ' -string quote
(modify-syntax-entry ?. "w" csh-mode-syntax-table) ; . -word constituent
(modify-syntax-entry ?? "w" csh-mode-syntax-table) ; ? -word constituent
;; \n - comment ender, first character of 2-char comment sequence
(modify-syntax-entry ?\n "> 1" csh-mode-syntax-table) ; # -word constituent
;; - whitespace, first character of 2-char comment sequence
(modify-syntax-entry ? " 1" csh-mode-syntax-table) ;
;; \t - whitespace, first character of 2-char comment sequence
(modify-syntax-entry ?\t " 1" csh-mode-syntax-table) ; # -word constituent
;; # - word constituent, second character of 2-char comment sequence
(modify-syntax-entry ?# "w 2" csh-mode-syntax-table) ; # -word constituent
)
;;
;; ------------------------------------------------------------------> Functions
;;
(defun csh-current-line ()
"Return the vertical position of point in the buffer.
Top line is 1."
(+ (count-lines (point-min) (point))
(if (= (current-column) 0) 1 0))
)
(defun csh-get-compound-level
(begin-re end-re anchor-point &optional balance-list)
"Determine how much to indent this structure. Return a list (level line)
of the matching compound command or nil if no match found."
(let*
(;; Locate the next compound begin keyword bounded by point-min
(match-point (if (re-search-backward begin-re (point-min) t)
(match-beginning 0) 0))
(nest-column (if (zerop match-point)
1
(progn
(goto-char match-point)
(current-indentation))))
(nest-list (cons 0 0)) ;; sentinel cons since cdr is >= 1
)
(if (zerop match-point)
nil ;; graceful exit from recursion
(progn
(if (nlistp balance-list)
(setq balance-list (list)))
;; Now search forward from matching start keyword for end keyword
(while (and (consp nest-list) (zerop (cdr nest-list))
(re-search-forward end-re anchor-point t))
(if (not (memq (point) balance-list))
(progn
(setq balance-list (cons (point) balance-list))
(goto-char match-point) ;; beginning of compound cmd
(setq nest-list
(csh-get-compound-level begin-re end-re
anchor-point balance-list))
)))
(cond ((consp nest-list)
(if (zerop (cdr nest-list))
(progn
(goto-char match-point)
(cons nest-column (csh-current-line)))
nest-list))
(t nil)
)
)
)
)
)
(defun csh-get-nest-level ()
"Return a 2 element list (nest-level nest-line) describing where the
current line should nest."
(let ((case-fold-search)
(level))
(save-excursion
(forward-line -1)
(while (and (not (bobp))
(null level))
(if (and (not (looking-at "^\\s *$"))
(not (save-excursion
(forward-line -1)
(beginning-of-line)
(looking-at csh-multiline-re)))
(not (looking-at csh-comment-regexp)))
(setq level (cons (current-indentation)
(csh-current-line)))
(forward-line -1)
);; if
);; while
(if (null level)
(cons (current-indentation) (csh-current-line))
level)
)
)
)
(defun csh-get-nester-column (nest-line)
"Return the column to indent to with respect to nest-line taking
into consideration keywords and other nesting constructs."
(save-excursion
(let ((fence-post)
(case-fold-search)
(start-line (csh-current-line)))
;;
;; Handle case item indentation constructs for this line
(cond ((looking-at csh-case-item-re)
;; This line is a case item...
(save-excursion
(goto-line nest-line)
(let ((fence-post (save-excursion (end-of-line) (point))))
(cond ((re-search-forward csh-switch-re fence-post t)
;; If this is the first case under the switch, indent.
(goto-char (match-beginning 0))
(+ (current-indentation) csh-case-item-offset))
((re-search-forward csh-case-item-re fence-post t)
;; If this is another case right under a previous case
;; without intervening code, stay at the same
;; indentation.
(goto-char (match-beginning 0))
(current-indentation))
(t
;; Else, this is a new case. Outdent.
(- (current-indentation) csh-case-item-offset))
)
)))
(t;; Not a case-item. What to do relative to the nest-line?
(save-excursion
(goto-line nest-line)
(setq fence-post (save-excursion (end-of-line) (point)))
(save-excursion
(cond
;;
;; Check if we are in a continued statement
((and (looking-at csh-multiline-re)
(save-excursion
(goto-line (1- start-line))
(looking-at csh-multiline-re)))
(if (looking-at ".*[\'\"]\\\\")
;; If this is a continued string, indent under
;; opening quote.
(progn
(re-search-forward "[\'\"]")
(forward-char -1))
(if (looking-at ".*([^\)\n]*\\\\")
;; Else if this is a continued parenthesized
;; list, indent after paren.
(re-search-forward "(" fence-post t)
;; Else, indent after whitespace after first word.
(re-search-forward "[^ \t]+[ \t]+" fence-post t)))
(current-column))
;; In order to locate the column of the keyword,
;; which might be embedded within a case-item,
;; it is necessary to use re-search-forward.
;; Search by literal case, since shell is
;; case-sensitive.
((re-search-forward csh-keywords-re fence-post t)
(goto-char (match-beginning 1))
(if (looking-at csh-switch-re)
(+ (current-indentation) csh-case-item-offset)
(+ (current-indentation)
(if (null csh-indent)
2 csh-indent)
)))
((re-search-forward csh-case-default-re fence-post t)
(if (null csh-indent)
(progn
(goto-char (match-end 1))
(+ (current-indentation) 1))
(progn
(goto-char (match-beginning 1))
(+ (current-indentation) csh-indent))
))
;;
;; Now detect first statement under a case item
((looking-at csh-case-item-re)
(if (null csh-case-indent)
(progn
(re-search-forward csh-case-item-re fence-post t)
(goto-char (match-end 1))
(+ (current-column) 1))
(+ (current-indentation) csh-case-indent)))
;;
;; If this is the first statement under a control-flow
;; label, indent one level.
((csh-looking-at-label)
(+ (current-indentation) csh-indent))
;; This is hosed when using current-column
;; and there is a multi-command expression as the
;; nester.
(t (current-indentation)))
)
));; excursion over
);; Not a case-item
);;let
);; excursion
);; defun
(defun csh-indent-command ()
"Indent current line relative to containing block and allow for
csh-tab-always-indent customization"
(interactive)
(let (case-fold-search)
(cond ((save-excursion
(skip-chars-backward " \t")
(bolp))
(csh-indent-line))
(csh-tab-always-indent
(save-excursion
(csh-indent-line)))
(t (insert-tab))
))
)
(defun csh-indent-line ()
"Indent current line as far as it should go according
to the syntax/context"
(interactive)
(let (case-fold-search)
(save-excursion
(beginning-of-line)
(if (bobp)
nil
;;
;; Align this line to current nesting level
(let*
(
(level-list (csh-get-nest-level)) ; Where to nest against
;; (last-line-level (car level-list))
(this-line-level (current-indentation))
(nester-column (csh-get-nester-column (cdr level-list)))
(struct-match (csh-match-structure-and-reindent))
)
(if struct-match
(setq nester-column struct-match))
(if (eq nester-column this-line-level)
nil
(beginning-of-line)
(let ((beg (point)))
(back-to-indentation)
(delete-region beg (point)))
(indent-to nester-column))
);; let*
);; if
);; excursion
;;
;; Position point on this line
(let*
(
(this-line-level (current-indentation))
(this-bol (save-excursion
(beginning-of-line)
(point)))
(this-point (- (point) this-bol))
)
(cond ((> this-line-level this-point);; point in initial white space
(back-to-indentation))
(t nil)
);; cond
);; let*
);; let
);; defun
(defun csh-indent-region (start end)
"From start to end, indent each line."
;; The algorithm is just moving through the region line by line with
;; the match noise turned off. Only modifies nonempty lines.
(save-excursion
(let (csh-match-and-tell
(endmark (copy-marker end)))
(goto-char start)
(beginning-of-line)
(setq start (point))
(while (> (marker-position endmark) start)
(if (not (and (bolp) (eolp)))
(csh-indent-line))
(forward-line 1)
(setq start (point)))
(set-marker endmark nil)
)
)
)
(defun csh-line-to-string ()
"From point, construct a string from all characters on
current line"
(skip-chars-forward " \t") ;; skip tabs as well as spaces
(buffer-substring (point)
(progn
(end-of-line 1)
(point))))
(defun csh-looking-at-label ()
"Return true if current line is a label (not the default: case label)."
(and
(looking-at csh-label-re)
(not (looking-at "^\\s *default:"))))
(defun csh-match-indent-level (begin-re end-re)
"Match the compound command and indent. Return nil on no match,
indentation to use for this line otherwise."
(interactive)
(let* ((case-fold-search)
(nest-list
(save-excursion
(csh-get-compound-level begin-re end-re (point))
))
) ;; bindings
(if (null nest-list)
(progn
(if csh-match-and-tell
(message "No matching compound command"))
nil) ;; Propagate a miss.
(let* (
(nest-level (car nest-list))
(match-line (cdr nest-list))
) ;; bindings
(if csh-match-and-tell
(save-excursion
(goto-line match-line)
(message "Matched ... %s" (csh-line-to-string))
) ;; excursion
) ;; if csh-match-and-tell
nest-level ;;Propagate a hit.
) ;; let*
) ;; if
) ;; let*
) ;; defun csh-match-indent-level
(defun csh-match-structure-and-reindent ()
"If the current line matches one of the indenting keywords
or one of the control structure ending keywords then reindent. Also
if csh-match-and-tell is non-nil the matching structure will echo in
the minibuffer"
(interactive)
(let (case-fold-search)
(save-excursion
(beginning-of-line)
(cond ((looking-at csh-else-re)
(csh-match-indent-level csh-if-re csh-endif-re))
((looking-at csh-else-if-re)
(csh-match-indent-level csh-if-re csh-endif-re))
((looking-at csh-endif-re)
(csh-match-indent-level csh-if-re csh-endif-re))
((looking-at csh-end-re)
(csh-match-indent-level csh-iteration-keywords-re csh-end-re))
((looking-at csh-endsw-re)
(csh-match-indent-level csh-switch-re csh-endsw-re))
((csh-looking-at-label)
;; Flush control-flow labels left since they don't nest.
0)
;;
(t nil)
);; cond
)
))
;;;###autoload
(defun csh-mode ()
"csh-mode 2.0 - Major mode for editing csh and tcsh scripts.
Special key bindings and commands:
\\{csh-mode-map}
Variables controlling indentation style:
csh-indent
Indentation of csh statements with respect to containing block.
Default value is 4.
csh-case-indent
Additional indentation for statements under case items.
Default value is nil which will align the statements one position
past the \")\" of the pattern.
csh-case-item-offset
Additional indentation for case items within a case statement.
Default value is 2.
csh-tab-always-indent
Controls the operation of the TAB key. If t (the default), always
reindent the current line. If nil, indent the current line only if
point is at the left margin or in the line's indentation; otherwise
insert a tab.
csh-match-and-tell
If non-nil echo in the minibuffer the matching compound command
for the \"done\", \"}\", \"fi\", or \"endsw\". Default value is t.
csh-comment-regexp
Regular expression used to recognize comments. Customize to support
csh-like languages. Default value is \"\^\\\\s *#\".
Style Guide.
By setting
(setq csh-indent default-tab-width)
The following style is obtained:
if [ -z $foo ]
then
bar # <-- csh-group-offset is additive to csh-indent
foo
fi
By setting
(setq csh-indent default-tab-width)
(setq csh-group-offset (- 0 csh-indent))
The following style is obtained:
if [ -z $foo ]
then
bar
foo
fi
By setting
(setq csh-case-item-offset 1)
(setq csh-case-indent nil)
The following style is obtained:
case x in *
foo) bar # <-- csh-case-item-offset
baz;; # <-- csh-case-indent aligns with \")\"
foobar) foo
bar;;
endsw
By setting
(setq csh-case-item-offset 1)
(setq csh-case-indent 6)
The following style is obtained:
case x in *
foo) bar # <-- csh-case-item-offset
baz;; # <-- csh-case-indent
foobar) foo
bar;;
endsw
Installation:
Put csh-mode.el in some directory in your load-path.
Put the following forms in your .emacs file.
(setq auto-mode-alist
(append auto-mode-alist
(list
'(\"\\\\.csh$\" . csh-mode)
'(\"\\\\.login\" . csh-mode))))
(setq csh-mode-hook
(function (lambda ()
(font-lock-mode 1) ;; font-lock the buffer
(setq csh-indent 8)
(setq csh-tab-always-indent t)
(setq csh-match-and-tell t)
(setq csh-align-to-keyword t) ;; Turn on keyword alignment
)))"
(interactive)
(kill-all-local-variables)
(use-local-map csh-mode-map)
(setq major-mode 'csh-mode)
(setq mode-name "Csh")
(setq local-abbrev-table csh-mode-abbrev-table)
(set-syntax-table csh-mode-syntax-table)
(make-local-variable 'indent-line-function)
(setq indent-line-function 'csh-indent-line)
(make-local-variable 'indent-region-function)
(setq indent-region-function 'csh-indent-region)
(make-local-variable 'comment-start)
(setq comment-start "# ")
(make-local-variable 'comment-end)
(setq comment-end "")
(make-local-variable 'comment-column)
(setq comment-column 32)
(make-local-variable 'comment-start-skip)
(setq comment-start-skip "#+ *")
;;
;; config font-lock mode
(make-local-variable 'font-lock-keywords)
(setq font-lock-keywords csh-font-lock-keywords)
;;
;; Let the user customize
(run-hooks 'csh-mode-hook)
) ;; defun
;;
;; Completion code supplied by Haavard Rue <hrue@imf.unit.no>.
;;
;;
;; add a completion with a given type to the list
;;
(defun csh-addto-alist (completion type)
(setq csh-completion-list
(append csh-completion-list
(list (cons completion type)))))
(defun csh-bol-point ()
(save-excursion
(beginning-of-line)
(point)))
(defun csh-complete-symbol ()
"Perform completion."
(interactive)
(let* ((case-fold-search)
(end (point))
(beg (unwind-protect
(save-excursion
(backward-sexp 1)
(while (= (char-syntax (following-char)) ?\')
(forward-char 1))
(point))))
(pattern (buffer-substring beg end))
(predicate
;;
;; ` or $( mark a function
;;
(save-excursion
(goto-char beg)
(if (or
(save-excursion
(backward-char 1)
(looking-at "`"))
(save-excursion
(backward-char 2)
(looking-at "\\$(")))
(function (lambda (sym)
(equal (cdr sym) csh-completion-type-function)))
;;
;; a $, ${ or ${# mark a variable
;;
(if (or
(save-excursion
(backward-char 1)
(looking-at "\\$"))
(save-excursion
(backward-char 2)
(looking-at "\\${"))
(save-excursion
(backward-char 3)
(looking-at "\\${#")))
(function (lambda (sym)
(equal (cdr sym)
csh-completion-type-var)))
;;
;; don't know. use 'em all
;;
(function (lambda (sym) t))))))
;;
(completion (try-completion pattern csh-completion-list predicate)))
;;
(cond ((eq completion t))
;;
;; oops, what is this ?
;;
((null completion)
(message "Can't find completion for \"%s\"" pattern))
;;
;; insert
;;
((not (string= pattern completion))
(delete-region beg end)
(insert completion))
;;
;; write possible completion in the minibuffer,
;; use this instead of a seperate buffer (usual)
;;
(t
(let ((list (all-completions pattern csh-completion-list predicate))
(string ""))
(while list
(progn
(setq string (concat string (format "%s " (car list))))
(setq list (cdr list))))
(message string))))))
;;
;; init the list and pickup all
;;
(defun csh-completion-init-and-pickup ()
(interactive)
(let (case-fold-search)
(csh-completion-list-init)
(csh-pickup-all)))
;;
;; init the list
;;
(defun csh-completion-list-init ()
(interactive)
(setq csh-completion-list
(list
(cons "break" csh-completion-type-misc)
(cons "breaksw" csh-completion-type-misc)
(cons "case" csh-completion-type-misc)
(cons "continue" csh-completion-type-misc)
(cons "endif" csh-completion-type-misc)
(cons "exit" csh-completion-type-misc)
(cons "foreach" csh-completion-type-misc)
(cons "if" csh-completion-type-misc)
(cons "while" csh-completion-type-misc))))
(defun csh-eol-point ()
(save-excursion
(end-of-line)
(point)))
(defun csh-pickup-all ()
"Pickup all completions in buffer."
(interactive)
(csh-pickup-completion-driver (point-min) (point-max) t))
(defun csh-pickup-completion (regexp type match pmin pmax)
"Pickup completion in region and addit to the list, if not already
there."
(let ((i 0) kw obj)
(save-excursion
(goto-char pmin)
(while (and
(re-search-forward regexp pmax t)
(match-beginning match)
(setq kw (buffer-substring
(match-beginning match)
(match-end match))))
(progn
(setq obj (assoc kw csh-completion-list))
(if (or (equal nil obj)
(and (not (equal nil obj))
(not (= type (cdr obj)))))
(progn
(setq i (1+ i))
(csh-addto-alist kw type))))))
i))
(defun csh-pickup-completion-driver (pmin pmax message)
"Driver routine for csh-pickup-completion."
(if message
(message "pickup completion..."))
(let* (
(i1
(csh-pickup-completion csh-completion-regexp-var
csh-completion-type-var
csh-completion-match-var
pmin pmax))
(i2
(csh-pickup-completion csh-completion-regexp-var2
csh-completion-type-var
csh-completion-match-var2
pmin pmax))
(i3
(csh-pickup-completion csh-completion-regexp-function
csh-completion-type-function
csh-completion-match-function
pmin pmax)))
(if message
(message "pickup %d variables and %d functions." (+ i1 i2) i3))))
(defun csh-pickup-this-line ()
"Pickup all completions in current line."
(interactive)
(csh-pickup-completion-driver (csh-bol-point) (csh-eol-point) nil))
(provide 'csh-mode)
;;; csh-mode.el ends here