arc1@eagle.ukc.ac.uk (A.R.Curtis) (06/29/88)
For those who asked about the csh-mode here it is. Should have sharred it up I suppose but I guess it'll be ok. --------------------------------- cut ------------------------------------ ;; csh-mode for emacs: usual mode modifying behaviour for writing ;; shell scripts in C-shell (+ Tenex) ;; ;; PD software, free etc. ;; ;; $Header: csh-mode.el,v 1.2 88/06/27 09:51:46 arc1 Exp $ (defconst csh-SPACE 32 "The space character") (defconst csh-mode-default-exdent 2 "Supplied indentation level as extra for keywords") (defvar csh-match-and-tell t "*Non-nil means tell user about which keyword matches the current one when closing constructs") (defvar csh-shell-flags-default "-f" "*Flags passed to the shell in the file when inserting the shell execute header") ;; exdenting (defvar csh-match-case-regexp "^[ \|\t]*case.*" "*Regexp used to match keyword: case") (defvar csh-match-default-regexp "^[ \|\t]*default:.*" "*Regexp used to match keyword: default") (defvar csh-match-else-regexp "^[ \|\t]*else.*" "*Regexp used to match keyword: else") (defvar csh-match-ifthen-regexp "^[ \|\t]*if.*then.*" "*Regexp used to match keyword sequence: if ... then") (defvar csh-match-foreach-regexp "^[ \|\t]*foreach.*" "*Regexp used to match keyword: foreach") (defvar csh-match-while-regexp "^[ \|\t]*while.*" "*Regexp used to match keyword: while") ;; structure starting (defvar csh-match-switch-regexp "^[ \|\t]*switch.*" "*Regexp used to match keyword: switch") ;; indenting (defvar csh-match-breaksw-regexp "^[ \|\t]*breaksw.*" "*Regexp used to match keyword: breaksw") (defvar csh-match-break-regexp "^[ \|\t]*break.*" "*Regexp used to match keyword: break") ;; structure ending (defvar csh-match-end-regexp "^[ \|\t]*end.*" "*Regexp used to match keyword: end") (defvar csh-match-endsw-regexp "^[ \|\t]*endsw.*" "*Regexp used to match keyword: endsw") (defvar csh-match-endif-regexp "^[ \|\t]*endif.*" "*Regexp used to match keyword: endif") ;; matching patterns for constructs (defvar csh-match-case-or-default-regexp "^[ \\|\\t]*\\(case\\|default:\\).*" "*Match one of keywords: case, default") (defvar csh-match-foreach-or-while-regexp "^[ \\|\\t]*\\(foreach\\|while\\).*" "*Match one of keywords: foreach, while") (defvar csh-match-else-or-ifthen-regexp "^[ \\|\\t]*\\(else\\|if.*then\\).*" "*Match one of keywords: else, if...then") (defvar csh-mode-exdent csh-mode-default-exdent "*Distance to exdent current line under previous") (defvar csh-shell-flags csh-shell-flags-default "*Flags to supply to the shell invocation line") (defvar csh-mode-map nil "Keymap for C-shell mode") (defvar csh-mode-abbrev-table nil) (defvar csh-mode-syntax-table nil) (defun csh-mode-syntax-table () "Csh syntax modifications" nil) (if csh-mode-map nil (progn (setq csh-mode-map (make-sparse-keymap)) (define-key csh-mode-map "\e#" 'csh-insert-shell-execute-line) (define-key csh-mode-map "\C-i" 'csh-indent-line-function) (define-key csh-mode-map "\177" 'backward-delete-char-untabify) (define-key csh-mode-map "\C-m" 'csh-match-structure-and-return) ) ;; progn ) ;; defun (defun csh-mode-variables () "Define local variables for csh mode" (make-local-variable 'csh-mode-exdent) (make-local-variable 'csh-shell-flags) (make-local-variable 'shell-file-name) ;; in case another shell location (setq shell-file-name "/bin/csh") (make-local-variable 'indent-line-function) (setq indent-line-function 'csh-indent-line-function) ) ;; defun (defun csh-mode () "Run a mode suitable for writing C shell scripts Currently running $Revision: 1.2 $ This mode allows matching of complex expressions to indicate the nesting of constructs. 'end' may be matched with its corresponding 'foreach' or 'while' line (and a message is generated in the minibuffer to say what the match was, or to give an error if no match). The same applies to the other csh constructs. TAB performs mode specific indentation according to current syntax. RET maps to re-indentation of matching keywords in constructs. Both keys indicate the corresponding match unless the variable csh-match-and-tell has the value nil. Key mapping is \\{csh-mode-map} " (interactive) (kill-all-local-variables) (use-local-map csh-mode-map) (setq major-mode 'csh-mode) (setq mode-name "Csh Script") (csh-mode-variables) (run-hooks 'csh-mode-hook) ) ;; defun (defun indentation-on-this-line () "Return current indentation level (no. of columns) that this line is indented" (interactive) (save-excursion (beginning-of-line) (let ( (pos-at-bol (point)) (pos-at-ind (progn (back-to-indentation) (point))) ) (compute-line-indent pos-at-bol pos-at-ind) ) ;; let ) ;; excursion ) ;; defun (defun compute-line-indent (from to) "Given two points, add up the number of characters between from and to. Do the proper things for tabs" (if (equal from to) 0 (+ (cond ((equal (char-to-string (char-after from)) "\t") tab-width) (t 1) ) ;; cond (compute-line-indent (+ 1 from) to)) ) ;; if ) ;; defun (defun is-looking-at-exdenter () "Return true if current line contains an exdenting keyword" (or (looking-at csh-match-case-regexp) (looking-at csh-match-default-regexp) (looking-at csh-match-else-regexp) (looking-at csh-match-ifthen-regexp) (looking-at csh-match-foreach-regexp) (looking-at csh-match-while-regexp) ) ;; or ) ;; defun (defun is-looking-at-indenter () "Return true if current line contains an indenting keyword" (or (looking-at csh-match-breaksw-regexp) (looking-at csh-match-break-regexp) ) ;; or ) ;; defun (defun need-more-less () "Does the current level need refining? Return extra indentation to indicate this" (save-excursion (forward-line -1) (cond ((is-looking-at-exdenter) csh-mode-exdent) ((is-looking-at-indenter) (- 0 csh-mode-exdent )) (t 0) ) ;; cond ) ;; excursion ) ;; defun (defun csh-indent-line-function () "Indent current line as far as it should go according to the syntax" (interactive) (save-excursion (beginning-of-line) (if (bobp) nil (let* ( (last-line-level (progn (forward-line -1) ;; previous line tells us (indentation-on-this-line))) (this-line-level (progn (forward-line 1) (indentation-on-this-line))) (level-diff (- this-line-level last-line-level)) ) (cond ((> level-diff 0) (delete-char level-diff)) ((< level-diff 0) (insert-char csh-SPACE (- 0 level-diff))) (t nil) ) ;; cond (let ( (extra (need-more-less)) ;; re-adjust indent level ? ) (cond ((> extra 0) (insert-char csh-SPACE extra)) ((and (< extra 0) (not (bolp))) (delete-char extra)) (t nil) ) ;; cond ) ;; let (if csh-match-and-tell (csh-match-structure-and-return t) ;; indicate match but no newline nil) ;; if ) ;; let* ) ;; if ) ;; excursion (let* ( (this-line-level (indentation-on-this-line)) (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* ) ;; defun (defun csh-insert-shell-execute-line () "Go to the top of the buffer and insert the #! /bin/csh line if necessary. The flags passed to the shell are the value of `csh-shell-flags'" (interactive) (let ( (ins-nl-flag (and (bobp) (eobp))) (shell-rgexp (concat "^#![ \|\t]*" shell-file-name ".*")) ) (if ins-nl-flag (next-line 1) nil) ;; if (save-excursion (beginning-of-buffer) (if (looking-at shell-rgexp) nil (progn (insert-string "#! " shell-file-name " " csh-shell-flags) (if (not ins-nl-flag) (insert-string "\n") nil) ;; if ) ;; progn ) ;; if ) ;; excursion ) ;; let ) ;; defun ;; got a problem matching nearest pattern only. Want to retain the ;; flexibility but sacrificing temporarily to get things ;; working (defun csh-match-structure-and-return (&optional arg) "If the current line matches one of the indenting keywords or one of the control structure ending keywords then indicate where it matches if csh-match-and-tell is non-nil. Add newline if argument present" (interactive) (if csh-match-and-tell (save-excursion (beginning-of-line) (cond ((looking-at csh-match-breaksw-regexp) (structure-match csh-match-case-or-default-regexp)) ((looking-at csh-match-break-regexp) (structure-match csh-match-foreach-or-while-regexp)) ((looking-at csh-match-else-regexp) (grep-and-indent-again csh-match-ifthen-regexp)) ((looking-at csh-match-endsw-regexp) (structure-match csh-match-switch-regexp)) ((looking-at csh-match-endif-regexp) (grep-and-indent-again csh-match-else-or-ifthen-regexp)) ;; endif comes before end. Got to do something about this ((looking-at csh-match-end-regexp) (grep-and-indent-again csh-match-foreach-or-while-regexp)) (t nil) ) ;; cond ) ;; excursion nil ) ;; if (if arg nil (newline 1) ) ;; if ) ;; defun (defun grep-and-indent-again (pat) "Try to match PAT and then indent accordingly" (interactive) (let* ( (last-level (save-excursion (if (re-search-backward pat (point-min) t) (indentation-on-this-line) 0) ;; if ) ;; excursion ) ;; bind (diff-level (- (indentation-on-this-line) last-level)) ) ;; b-list (save-excursion (beginning-of-line) (cond ((> diff-level 0) (delete-char diff-level)) ((< diff-level 0) (insert-char csh-SPACE (- 0 diff-level))) (t nil) ) ;; cond ) ;; excursion ) ;; let* (structure-match pat) ) ;; defun (defun structure-match (pat) "Match against the pattern in PAT and print out the matched text. If none then indicate no match and say so in minibuffer" (interactive) (let ( (result (found-a-match-in pat)) ) (message "%s" (cond ((null result) "No matching open") (t result) ) ;; cond ) ;; message ) ;; let ) ;; defun (defun found-a-match-in (pat) "Go back and search for PAT. Text if successful, nil otherwise" (save-excursion (beginning-of-line) (if (re-search-backward pat (point-min) t) (format "Matched ... %s" (line-to-string)) nil) ) ;; excursion ) ;; defun (defun line-to-string () "From point, construct a string from all characters on current line" (setq answer "") (while (equal (char-after (point)) csh-SPACE) ;; skip white space (forward-char 1) ) ;; while (while (not (eolp)) (setq answer (concat answer (char-to-string (char-after (point))))) (forward-char 1) ) ;; while answer ) ;; defun ------------------------------- end cut ---------------------------------- -- Tony Curtis, Computing Lab. | arc1@uk.ac.ukc Univ. Kent at Canterbury | Canterbury, Kent CT2 7NF | tcu@uk.ac.ex.cs