diff options
Diffstat (limited to 'contrib/tcsh/csh-mode.el')
-rw-r--r-- | contrib/tcsh/csh-mode.el | 935 |
1 files changed, 935 insertions, 0 deletions
diff --git a/contrib/tcsh/csh-mode.el b/contrib/tcsh/csh-mode.el new file mode 100644 index 0000000..3b638db --- /dev/null +++ b/contrib/tcsh/csh-mode.el @@ -0,0 +1,935 @@ +;; 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 |