159243Sobrien;; csh-mode.el --- csh (and tcsh) script editing mode for Emacs. 259243Sobrien;; 359243Sobrien;; Version: 1.2 459243Sobrien;; Date: April 2, 1999 5131962Smp;; Maintainer: Dan Harkless <software@harkless.org> 659243Sobrien;; 759243Sobrien;; Description: 859243Sobrien;; csh and tcsh script editing mode for Emacs. 959243Sobrien;; 1059243Sobrien;; Installation: 1159243Sobrien;; Put csh-mode.el in some directory in your load-path and load it. 1259243Sobrien;; 1359243Sobrien;; Usage: 1459243Sobrien;; This major mode assists shell script writers with indentation 1559243Sobrien;; control and control structure construct matching in much the same 1659243Sobrien;; fashion as other programming language modes. Invoke describe-mode 1759243Sobrien;; for more information. 1859243Sobrien;; 1959243Sobrien;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2059243Sobrien;; 2159243Sobrien;; Author key: 22131962Smp;; DH - Dan Harkless <software@harkless.org> 2359243Sobrien;; CM - Carlo Migliorini <migliorini@sodalia.it> 2459243Sobrien;; JR - Jack Repenning <jackr@sgi.com> 2559243Sobrien;; GE - Gary Ellison <Gary.F.Ellison@att.com> 2659243Sobrien;; 2759243Sobrien;; *** REVISION HISTORY *** 2859243Sobrien;; 2959243Sobrien;; DATE MOD. BY REASON FOR MODIFICATION 3059243Sobrien;; --------- -- -------------------------------------------------------------- 3159243Sobrien;; 2 Apr 99 DH 1.2: Noticed an out-of-date comment referencing .bashrc etc. 3259243Sobrien;; 11 Dec 96 DH 1.1: ksh-mode just indented continuation lines by 1 space. 3359243Sobrien;; csh-mode looks at the first line and indents properly to line 3459243Sobrien;; up under the open-paren, quote, or command. 3559243Sobrien;; 11 Dec 96 DH Added fontification for history substitutions. 3659243Sobrien;; 10 Dec 96 DH Added indentation and fontification for labels. Added 3759243Sobrien;; fontification for variables and backquoted strings. 3859243Sobrien;; 9 Dec 96 DH 1.0: Brought csh-mode up to the level of functionality of 3959243Sobrien;; the original ksh-mode. 4059243Sobrien;; 7 Oct 96 CM 0.1: Hacked ksh-mode.el into minimally functional csh-mode.el 4159243Sobrien;; by doing search-and-replace and some keyword changes. 4259243Sobrien;; 8 Aug 96 JR (Last modification to ksh-mode 2.6.) 4359243Sobrien;; [...] 4459243Sobrien;; 19 Jun 92 GE (Conception of ksh-mode.) 4559243Sobrien;; 4659243Sobrien;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 4759243Sobrien 4859243Sobrien 4959243Sobrien(defconst csh-mode-version "1.2" 5059243Sobrien "*Version number of this version of csh-mode") 5159243Sobrien 5259243Sobrien(defvar csh-mode-hook 5359243Sobrien '(lambda () 5459243Sobrien (auto-fill-mode 1)) 5559243Sobrien "Hook to run each time csh-mode is entered.") 5659243Sobrien 5759243Sobrien 5859243Sobrien;; 5959243Sobrien;; -------------------------------------------> Variables controlling completion 6059243Sobrien;; 6159243Sobrien(defvar csh-completion-list '()) 6259243Sobrien(make-variable-buffer-local 'csh-completion-list) 6359243Sobrien(set-default 'csh-completion-list '()) 6459243Sobrien;; 6559243Sobrien;; -type- : type number, 0:misc, 1:variable, 2:function 6659243Sobrien;; -regexp-: regexp used to parse the script 6759243Sobrien;; -match- : used by match-beginning/end to pickup target 6859243Sobrien;; 6959243Sobrien(defvar csh-completion-type-misc 0) 7059243Sobrien(defvar csh-completion-regexp-var "\\([A-Za-z_0-9]+\\)=") 7159243Sobrien(defvar csh-completion-type-var 1) 7259243Sobrien(defvar csh-completion-match-var 1) 7359243Sobrien(defvar csh-completion-regexp-var2 "\\$\\({\\|{#\\)?\\([A-Za-z_0-9]+\\)[#%:}]?") 7459243Sobrien(defvar csh-completion-match-var2 2) 7559243Sobrien(defvar csh-completion-regexp-function 7659243Sobrien "\\(function\\)?[ \t]*\\([A-Za-z_0-9]+\\)[ \t]*([ \t]*)") 7759243Sobrien(defvar csh-completion-type-function 2) 7859243Sobrien(defvar csh-completion-match-function 2) 7959243Sobrien 8059243Sobrien 8159243Sobrien;; 8259243Sobrien;; ------------------------------------> Variables controlling indentation style 8359243Sobrien;; 8459243Sobrien(defvar csh-indent 4 8559243Sobrien "*Indentation of csh statements with respect to containing block. A value 8659243Sobrienof nil indicates compound list keyword \(\"do\" and \"then\"\) alignment.") 8759243Sobrien 8859243Sobrien(defvar csh-case-item-offset csh-indent 8959243Sobrien "*Additional indentation for case items within a case statement.") 9059243Sobrien(defvar csh-case-indent nil 9159243Sobrien "*Additional indentation for statements under case items.") 9259243Sobrien(defvar csh-comment-regexp "^\\s *#" 9359243Sobrien "*Regular expression used to recognize comments. Customize to support 9459243Sobriencsh-like languages.") 9559243Sobrien(defvar csh-match-and-tell t 9659243Sobrien "*If non-nil echo in the minibuffer the matching compound command 9759243Sobrienfor the \"breaksw\", \"end\", or \"endif\".") 9859243Sobrien(defvar csh-tab-always-indent t 9959243Sobrien "*Controls the operation of the TAB key. If t (the default), always 10059243Sobrienreindent the current line. If nil, indent the current line only if 10159243Sobrienpoint is at the left margin or in the line's indentation; otherwise 10259243Sobrieninsert a tab.") 10359243Sobrien 10459243Sobrien 10559243Sobrien;; 10659243Sobrien;; ----------------------------------------> Constants containing syntax regexps 10759243Sobrien;; 10859243Sobrien(defconst csh-case-default-re 10959243Sobrien "^\\s *\\(case\\|default\\)\\b" 11059243Sobrien "Regexp used to locate grouping keywords case and default" ) 11159243Sobrien 11259243Sobrien(defconst csh-case-item-re "^\\s *\\(case .*\\|default\\):" 11359243Sobrien "Regexp used to match case-items") 11459243Sobrien 11559243Sobrien(defconst csh-end-re "^\\s *end\\b" 11659243Sobrien "Regexp used to match keyword: end") 11759243Sobrien 11859243Sobrien(defconst csh-endif-re "^\\s *endif\\b" 11959243Sobrien "Regexp used to match keyword: endif") 12059243Sobrien 12159243Sobrien(defconst csh-endsw-re "^\\s *endsw\\b" 12259243Sobrien "Regexp used to match keyword: endsw") 12359243Sobrien 12459243Sobrien(defconst csh-else-re "^\\s *\\belse\\(\\b\\|$\\)" 12559243Sobrien "Regexp used to match keyword: else") 12659243Sobrien 12759243Sobrien(defconst csh-else-if-re "^\\s *\\belse if\\(\\b\\|$\\)" 12859243Sobrien "Regexp used to match keyword pair: else if") 12959243Sobrien 13059243Sobrien(defconst csh-if-re "^\\s *if\\b.+\\(\\\\\\|\\bthen\\b\\)" 13159243Sobrien "Regexp used to match non-one-line if statements") 13259243Sobrien 13359243Sobrien(defconst csh-iteration-keywords-re "^[^#\n]*\\s\"*\\b\\(while\\|foreach\\)\\b" 13459243Sobrien "Match one of the keywords: while, foreach") 13559243Sobrien 13659243Sobrien(defconst csh-keywords-re 13759243Sobrien "^\\s *\\(else\\b\\|foreach\\b\\|if\\b.+\\(\\\\\\|\\bthen\\b\\)\\|switch\\b\\|while\\b\\)" 13859243Sobrien "Regexp used to detect compound command keywords: else, if, foreach, while") 13959243Sobrien 14059243Sobrien(defconst csh-label-re "^\\s *[^!#$\n ]+:" 14159243Sobrien "Regexp used to match flow-control labels") 14259243Sobrien 14359243Sobrien(defconst csh-multiline-re "^.*\\\\$" 14459243Sobrien "Regexp used to match a line with a statement using more lines.") 14559243Sobrien 14659243Sobrien(defconst csh-switch-re "^\\s *switch\\b" 14759243Sobrien "Regexp used to match keyword: switch") 14859243Sobrien 14959243Sobrien 15059243Sobrien;; 15159243Sobrien;; ----------------------------------------> Variables controlling fontification 15259243Sobrien;; 15359243Sobrien(defvar csh-keywords '("@" "alias" "bg" "break" "breaksw" "case" "cd" "chdir" 15459243Sobrien "continue" "default" "dirs" "echo" "else" "end" "endif" 15559243Sobrien "endsw" "eval" "exec" "exit" "fg" "foreach" "glob" "goto" 15659243Sobrien "hashstat" "history" "if" "jobs" "kill" "limit" "login" 15759243Sobrien "logout" "limit" "notify" "onintr" "popd" "printenv" 15859243Sobrien "pushd" "rehash" "repeat" "set" "setenv" "shift" "source" 15959243Sobrien "stop" "suspend" "switch" "then" "time" "umask" "unalias" 16059243Sobrien "unhash" "unlimit" "unset" "unsetenv" "wait" "while" 16159243Sobrien ;; tcsh-keywords 16259243Sobrien "alloc" "bindkey" "builtins" "complete" "echotc" 16359243Sobrien "filetest" "hup" "log" "ls-F" "nice" "nohup" "sched" 16459243Sobrien "settc" "setty" "telltc" "uncomplete" "where" "which")) 16559243Sobrien 16659243Sobrien(require 'font-lock) ; need to do this before referring to font-lock-* below 16759243Sobrien 16859243Sobrien(defconst csh-font-lock-keywords 16959243Sobrien ;; NOTE: The order of some of the items in this list is significant. Do not 17059243Sobrien ;; alphabetize or otherwise blindly rearrange. 17159243Sobrien (list 17259243Sobrien ;; Comments on line 1, which are missed by syntactic fontification. 17359243Sobrien '("^#.*" 0 font-lock-comment-face) 17459243Sobrien 17559243Sobrien ;; Label definitions (1 means first parenthesized exp in regexp). 17659243Sobrien '("^\\s *\\([^!#$\n ]+\\):" 1 font-lock-function-name-face) 17759243Sobrien 17859243Sobrien ;; Label references. 17959243Sobrien '("\\b\\(goto\\|onintr\\)\\b\\s +\\([^!#$ \n\t]+\\)" 18059243Sobrien 2 font-lock-function-name-face) 18159243Sobrien 18259243Sobrien ;; Variable settings. 18359243Sobrien '("\\(@\\|set\\|setenv\\)\\s +\\([0-9A-Za-z_]+\\b\\)" 18459243Sobrien 2 font-lock-variable-name-face) 18559243Sobrien 18659243Sobrien ;; Variable references not inside of strings. 18759243Sobrien '("\\$[][0-9A-Za-z_#:?]+" 0 font-lock-variable-name-face) 18859243Sobrien 18959243Sobrien ;; Backquoted strings. 'keep' means to just fontify non-fontified text. 19059243Sobrien '("`\\(.*\\)`" 1 font-lock-reference-face keep) 19159243Sobrien 19259243Sobrien ;; NOTE: The following variables need to be anchored to the beginning of 19359243Sobrien ;; line to prevent re-fontifying text in comments. Due to this, we 19459243Sobrien ;; can only catch a finite number of occurrences. More can be added. 19559243Sobrien ;; The 't' means to override previous fontification. 19659243Sobrien ;; 19759243Sobrien ;; Variable references inside of " strings. 19859243Sobrien '("^[^#\n]*\".*\\(\\$[][0-9A-Za-z_#:?]+\\).*\"" 19959243Sobrien 1 font-lock-variable-name-face t) ; 1 20059243Sobrien '("^[^#\n]*\".*\\(\\$[][0-9A-Za-z_#:?]+\\).*\\$[][0-9A-Za-z_#:?]+.*\"" 20159243Sobrien 1 font-lock-variable-name-face t) ; 2 20259243Sobrien (cons (concat "^[^#\n]*\".*\\(\\$[][0-9A-Za-z_#:?]+\\).*" 20359243Sobrien "\\$[][0-9A-Za-z_#:?]+.*\\$[][0-9A-Za-z_#:?]+.*\"") 20459243Sobrien (list 1 font-lock-variable-name-face t)) ; 3 20559243Sobrien ;; 20659243Sobrien ;; History substitutions. 20759243Sobrien '("^![^~= \n\t]+" 0 font-lock-reference-face t) ; BOL 20859243Sobrien '("^[^#\n]*[^#\\\n]\\(![^~= \n\t]+\\)" 1 font-lock-reference-face t) ; 1 20959243Sobrien '("^[^#\n]*[^#\\\n]\\(![^~= \n\t]+\\).*![^~= \n\t]+" 21059243Sobrien 1 font-lock-reference-face t) ; 2 21159243Sobrien 21259243Sobrien ;; Keywords. 21359243Sobrien (cons (concat 21459243Sobrien "\\(\\<" 21559243Sobrien (mapconcat 'identity csh-keywords "\\>\\|\\<") 21659243Sobrien "\\>\\)") 21759243Sobrien 1) 21859243Sobrien )) 21959243Sobrien 22059243Sobrien(put 'csh-mode 'font-lock-keywords 'csh-font-lock-keywords) 22159243Sobrien 22259243Sobrien 22359243Sobrien;; 22459243Sobrien;; -------------------------------------------------------> Mode-specific tables 22559243Sobrien;; 22659243Sobrien(defvar csh-mode-abbrev-table nil 22759243Sobrien "Abbrev table used while in csh mode.") 22859243Sobrien(define-abbrev-table 'csh-mode-abbrev-table ()) 22959243Sobrien 23059243Sobrien(defvar csh-mode-map nil 23159243Sobrien "Keymap used in csh mode") 23259243Sobrien(if csh-mode-map 23359243Sobrien () 23459243Sobrien (setq csh-mode-map (make-sparse-keymap)) 23559243Sobrien;;(define-key csh-mode-map "\177" 'backward-delete-char-untabify) 23659243Sobrien (define-key csh-mode-map "\C-c\t" 'csh-completion-init-and-pickup) 23759243Sobrien (define-key csh-mode-map "\C-j" 'reindent-then-newline-and-indent) 23859243Sobrien (define-key csh-mode-map "\e\t" 'csh-complete-symbol) 23959243Sobrien (define-key csh-mode-map "\n" 'reindent-then-newline-and-indent) 24059243Sobrien (define-key csh-mode-map '[return] 'reindent-then-newline-and-indent) 24159243Sobrien (define-key csh-mode-map "\t" 'csh-indent-command) 24259243Sobrien;;(define-key csh-mode-map "\t" 'csh-indent-line) 24359243Sobrien ) 24459243Sobrien 24559243Sobrien(defvar csh-mode-syntax-table nil 24659243Sobrien "Syntax table used while in csh mode.") 24759243Sobrien(if csh-mode-syntax-table 24859243Sobrien ;; If it's already set up, don't change it. 24959243Sobrien () 25059243Sobrien ;; Else, create it from the standard table and modify entries that need to be. 25159243Sobrien (setq csh-mode-syntax-table (make-syntax-table)) 25259243Sobrien (modify-syntax-entry ?& "." csh-mode-syntax-table) ; & -punctuation 25359243Sobrien (modify-syntax-entry ?* "." csh-mode-syntax-table) ; * -punctuation 25459243Sobrien (modify-syntax-entry ?- "." csh-mode-syntax-table) ; - -punctuation 25559243Sobrien (modify-syntax-entry ?= "." csh-mode-syntax-table) ; = -punctuation 25659243Sobrien (modify-syntax-entry ?+ "." csh-mode-syntax-table) ; + -punctuation 25759243Sobrien (modify-syntax-entry ?| "." csh-mode-syntax-table) ; | -punctuation 25859243Sobrien (modify-syntax-entry ?< "." csh-mode-syntax-table) ; < -punctuation 25959243Sobrien (modify-syntax-entry ?> "." csh-mode-syntax-table) ; > -punctuation 26059243Sobrien (modify-syntax-entry ?/ "." csh-mode-syntax-table) ; / -punctuation 26159243Sobrien (modify-syntax-entry ?\' "\"" csh-mode-syntax-table) ; ' -string quote 26259243Sobrien (modify-syntax-entry ?. "w" csh-mode-syntax-table) ; . -word constituent 26359243Sobrien (modify-syntax-entry ?? "w" csh-mode-syntax-table) ; ? -word constituent 26459243Sobrien 26559243Sobrien ;; \n - comment ender, first character of 2-char comment sequence 26659243Sobrien (modify-syntax-entry ?\n "> 1" csh-mode-syntax-table) ; # -word constituent 26759243Sobrien 26859243Sobrien ;; - whitespace, first character of 2-char comment sequence 26959243Sobrien (modify-syntax-entry ? " 1" csh-mode-syntax-table) ; 27059243Sobrien 27159243Sobrien ;; \t - whitespace, first character of 2-char comment sequence 27259243Sobrien (modify-syntax-entry ?\t " 1" csh-mode-syntax-table) ; # -word constituent 27359243Sobrien 27459243Sobrien ;; # - word constituent, second character of 2-char comment sequence 27559243Sobrien (modify-syntax-entry ?# "w 2" csh-mode-syntax-table) ; # -word constituent 27659243Sobrien ) 27759243Sobrien 27859243Sobrien 27959243Sobrien;; 28059243Sobrien;; ------------------------------------------------------------------> Functions 28159243Sobrien;; 28259243Sobrien(defun csh-current-line () 28359243Sobrien "Return the vertical position of point in the buffer. 28459243SobrienTop line is 1." 28559243Sobrien (+ (count-lines (point-min) (point)) 28659243Sobrien (if (= (current-column) 0) 1 0)) 28759243Sobrien ) 28859243Sobrien 28959243Sobrien(defun csh-get-compound-level 29059243Sobrien (begin-re end-re anchor-point &optional balance-list) 29159243Sobrien "Determine how much to indent this structure. Return a list (level line) 29259243Sobrienof the matching compound command or nil if no match found." 29359243Sobrien (let* 29459243Sobrien (;; Locate the next compound begin keyword bounded by point-min 29559243Sobrien (match-point (if (re-search-backward begin-re (point-min) t) 29659243Sobrien (match-beginning 0) 0)) 29759243Sobrien (nest-column (if (zerop match-point) 29859243Sobrien 1 29959243Sobrien (progn 30059243Sobrien (goto-char match-point) 30159243Sobrien (current-indentation)))) 30259243Sobrien (nest-list (cons 0 0)) ;; sentinel cons since cdr is >= 1 30359243Sobrien ) 30459243Sobrien (if (zerop match-point) 30559243Sobrien nil ;; graceful exit from recursion 30659243Sobrien (progn 30759243Sobrien (if (nlistp balance-list) 30859243Sobrien (setq balance-list (list))) 30959243Sobrien ;; Now search forward from matching start keyword for end keyword 31059243Sobrien (while (and (consp nest-list) (zerop (cdr nest-list)) 31159243Sobrien (re-search-forward end-re anchor-point t)) 31259243Sobrien (if (not (memq (point) balance-list)) 31359243Sobrien (progn 31459243Sobrien (setq balance-list (cons (point) balance-list)) 31559243Sobrien (goto-char match-point) ;; beginning of compound cmd 31659243Sobrien (setq nest-list 31759243Sobrien (csh-get-compound-level begin-re end-re 31859243Sobrien anchor-point balance-list)) 31959243Sobrien ))) 32059243Sobrien 32159243Sobrien (cond ((consp nest-list) 32259243Sobrien (if (zerop (cdr nest-list)) 32359243Sobrien (progn 32459243Sobrien (goto-char match-point) 32559243Sobrien (cons nest-column (csh-current-line))) 32659243Sobrien nest-list)) 32759243Sobrien (t nil) 32859243Sobrien ) 32959243Sobrien ) 33059243Sobrien ) 33159243Sobrien ) 33259243Sobrien ) 33359243Sobrien 33459243Sobrien(defun csh-get-nest-level () 33559243Sobrien "Return a 2 element list (nest-level nest-line) describing where the 33659243Sobriencurrent line should nest." 33759243Sobrien (let ((case-fold-search) 33859243Sobrien (level)) 33959243Sobrien (save-excursion 34059243Sobrien (forward-line -1) 34159243Sobrien (while (and (not (bobp)) 34259243Sobrien (null level)) 34359243Sobrien (if (and (not (looking-at "^\\s *$")) 34459243Sobrien (not (save-excursion 34559243Sobrien (forward-line -1) 34659243Sobrien (beginning-of-line) 34759243Sobrien (looking-at csh-multiline-re))) 34859243Sobrien (not (looking-at csh-comment-regexp))) 34959243Sobrien (setq level (cons (current-indentation) 35059243Sobrien (csh-current-line))) 35159243Sobrien (forward-line -1) 35259243Sobrien );; if 35359243Sobrien );; while 35459243Sobrien (if (null level) 35559243Sobrien (cons (current-indentation) (csh-current-line)) 35659243Sobrien level) 35759243Sobrien ) 35859243Sobrien ) 35959243Sobrien ) 36059243Sobrien 36159243Sobrien(defun csh-get-nester-column (nest-line) 36259243Sobrien "Return the column to indent to with respect to nest-line taking 36359243Sobrieninto consideration keywords and other nesting constructs." 36459243Sobrien (save-excursion 36559243Sobrien (let ((fence-post) 36659243Sobrien (case-fold-search) 36759243Sobrien (start-line (csh-current-line))) 36859243Sobrien ;; 36959243Sobrien ;; Handle case item indentation constructs for this line 37059243Sobrien (cond ((looking-at csh-case-item-re) 37159243Sobrien ;; This line is a case item... 37259243Sobrien (save-excursion 37359243Sobrien (goto-line nest-line) 37459243Sobrien (let ((fence-post (save-excursion (end-of-line) (point)))) 37559243Sobrien (cond ((re-search-forward csh-switch-re fence-post t) 37659243Sobrien ;; If this is the first case under the switch, indent. 37759243Sobrien (goto-char (match-beginning 0)) 37859243Sobrien (+ (current-indentation) csh-case-item-offset)) 37959243Sobrien 38059243Sobrien ((re-search-forward csh-case-item-re fence-post t) 38159243Sobrien ;; If this is another case right under a previous case 38259243Sobrien ;; without intervening code, stay at the same 38359243Sobrien ;; indentation. 38459243Sobrien (goto-char (match-beginning 0)) 38559243Sobrien (current-indentation)) 38659243Sobrien 38759243Sobrien (t 38859243Sobrien ;; Else, this is a new case. Outdent. 38959243Sobrien (- (current-indentation) csh-case-item-offset)) 39059243Sobrien ) 39159243Sobrien ))) 39259243Sobrien (t;; Not a case-item. What to do relative to the nest-line? 39359243Sobrien (save-excursion 39459243Sobrien (goto-line nest-line) 39559243Sobrien (setq fence-post (save-excursion (end-of-line) (point))) 39659243Sobrien (save-excursion 39759243Sobrien (cond 39859243Sobrien ;; 39959243Sobrien ;; Check if we are in a continued statement 40059243Sobrien ((and (looking-at csh-multiline-re) 40159243Sobrien (save-excursion 40259243Sobrien (goto-line (1- start-line)) 40359243Sobrien (looking-at csh-multiline-re))) 40459243Sobrien (if (looking-at ".*[\'\"]\\\\") 40559243Sobrien ;; If this is a continued string, indent under 40659243Sobrien ;; opening quote. 40759243Sobrien (progn 40859243Sobrien (re-search-forward "[\'\"]") 40959243Sobrien (forward-char -1)) 41059243Sobrien (if (looking-at ".*([^\)\n]*\\\\") 41159243Sobrien ;; Else if this is a continued parenthesized 41259243Sobrien ;; list, indent after paren. 41359243Sobrien (re-search-forward "(" fence-post t) 41459243Sobrien ;; Else, indent after whitespace after first word. 41559243Sobrien (re-search-forward "[^ \t]+[ \t]+" fence-post t))) 41659243Sobrien (current-column)) 41759243Sobrien 41859243Sobrien ;; In order to locate the column of the keyword, 41959243Sobrien ;; which might be embedded within a case-item, 42059243Sobrien ;; it is necessary to use re-search-forward. 42159243Sobrien ;; Search by literal case, since shell is 42259243Sobrien ;; case-sensitive. 42359243Sobrien ((re-search-forward csh-keywords-re fence-post t) 42459243Sobrien (goto-char (match-beginning 1)) 42559243Sobrien (if (looking-at csh-switch-re) 42659243Sobrien (+ (current-indentation) csh-case-item-offset) 42759243Sobrien (+ (current-indentation) 42859243Sobrien (if (null csh-indent) 42959243Sobrien 2 csh-indent) 43059243Sobrien ))) 43159243Sobrien 43259243Sobrien ((re-search-forward csh-case-default-re fence-post t) 43359243Sobrien (if (null csh-indent) 43459243Sobrien (progn 43559243Sobrien (goto-char (match-end 1)) 43659243Sobrien (+ (current-indentation) 1)) 43759243Sobrien (progn 43859243Sobrien (goto-char (match-beginning 1)) 43959243Sobrien (+ (current-indentation) csh-indent)) 44059243Sobrien )) 44159243Sobrien 44259243Sobrien ;; 44359243Sobrien ;; Now detect first statement under a case item 44459243Sobrien ((looking-at csh-case-item-re) 44559243Sobrien (if (null csh-case-indent) 44659243Sobrien (progn 44759243Sobrien (re-search-forward csh-case-item-re fence-post t) 44859243Sobrien (goto-char (match-end 1)) 44959243Sobrien (+ (current-column) 1)) 45059243Sobrien (+ (current-indentation) csh-case-indent))) 45159243Sobrien 45259243Sobrien ;; 45359243Sobrien ;; If this is the first statement under a control-flow 45459243Sobrien ;; label, indent one level. 45559243Sobrien ((csh-looking-at-label) 45659243Sobrien (+ (current-indentation) csh-indent)) 45759243Sobrien 45859243Sobrien ;; This is hosed when using current-column 45959243Sobrien ;; and there is a multi-command expression as the 46059243Sobrien ;; nester. 46159243Sobrien (t (current-indentation))) 46259243Sobrien ) 46359243Sobrien ));; excursion over 46459243Sobrien );; Not a case-item 46559243Sobrien );;let 46659243Sobrien );; excursion 46759243Sobrien );; defun 46859243Sobrien 46959243Sobrien(defun csh-indent-command () 47059243Sobrien "Indent current line relative to containing block and allow for 47159243Sobriencsh-tab-always-indent customization" 47259243Sobrien (interactive) 47359243Sobrien (let (case-fold-search) 47459243Sobrien (cond ((save-excursion 47559243Sobrien (skip-chars-backward " \t") 47659243Sobrien (bolp)) 47759243Sobrien (csh-indent-line)) 47859243Sobrien (csh-tab-always-indent 47959243Sobrien (save-excursion 48059243Sobrien (csh-indent-line))) 48159243Sobrien (t (insert-tab)) 48259243Sobrien )) 48359243Sobrien ) 48459243Sobrien 48559243Sobrien(defun csh-indent-line () 48659243Sobrien "Indent current line as far as it should go according 48759243Sobriento the syntax/context" 48859243Sobrien (interactive) 48959243Sobrien (let (case-fold-search) 49059243Sobrien (save-excursion 49159243Sobrien (beginning-of-line) 49259243Sobrien (if (bobp) 49359243Sobrien nil 49459243Sobrien ;; 49559243Sobrien ;; Align this line to current nesting level 49659243Sobrien (let* 49759243Sobrien ( 49859243Sobrien (level-list (csh-get-nest-level)) ; Where to nest against 49959243Sobrien ;; (last-line-level (car level-list)) 50059243Sobrien (this-line-level (current-indentation)) 50159243Sobrien (nester-column (csh-get-nester-column (cdr level-list))) 50259243Sobrien (struct-match (csh-match-structure-and-reindent)) 50359243Sobrien ) 50459243Sobrien (if struct-match 50559243Sobrien (setq nester-column struct-match)) 50659243Sobrien (if (eq nester-column this-line-level) 50759243Sobrien nil 50859243Sobrien (beginning-of-line) 50959243Sobrien (let ((beg (point))) 51059243Sobrien (back-to-indentation) 51159243Sobrien (delete-region beg (point))) 51259243Sobrien (indent-to nester-column)) 51359243Sobrien );; let* 51459243Sobrien );; if 51559243Sobrien );; excursion 51659243Sobrien ;; 51759243Sobrien ;; Position point on this line 51859243Sobrien (let* 51959243Sobrien ( 52059243Sobrien (this-line-level (current-indentation)) 52159243Sobrien (this-bol (save-excursion 52259243Sobrien (beginning-of-line) 52359243Sobrien (point))) 52459243Sobrien (this-point (- (point) this-bol)) 52559243Sobrien ) 52659243Sobrien (cond ((> this-line-level this-point);; point in initial white space 52759243Sobrien (back-to-indentation)) 52859243Sobrien (t nil) 52959243Sobrien );; cond 53059243Sobrien );; let* 53159243Sobrien );; let 53259243Sobrien );; defun 53359243Sobrien 53459243Sobrien(defun csh-indent-region (start end) 53559243Sobrien "From start to end, indent each line." 53659243Sobrien ;; The algorithm is just moving through the region line by line with 53759243Sobrien ;; the match noise turned off. Only modifies nonempty lines. 53859243Sobrien (save-excursion 53959243Sobrien (let (csh-match-and-tell 54059243Sobrien (endmark (copy-marker end))) 54159243Sobrien 54259243Sobrien (goto-char start) 54359243Sobrien (beginning-of-line) 54459243Sobrien (setq start (point)) 54559243Sobrien (while (> (marker-position endmark) start) 54659243Sobrien (if (not (and (bolp) (eolp))) 54759243Sobrien (csh-indent-line)) 54859243Sobrien (forward-line 1) 54959243Sobrien (setq start (point))) 55059243Sobrien 55159243Sobrien (set-marker endmark nil) 55259243Sobrien ) 55359243Sobrien ) 55459243Sobrien ) 55559243Sobrien 55659243Sobrien(defun csh-line-to-string () 55759243Sobrien "From point, construct a string from all characters on 55859243Sobriencurrent line" 55959243Sobrien (skip-chars-forward " \t") ;; skip tabs as well as spaces 56059243Sobrien (buffer-substring (point) 56159243Sobrien (progn 56259243Sobrien (end-of-line 1) 56359243Sobrien (point)))) 56459243Sobrien 56559243Sobrien(defun csh-looking-at-label () 56659243Sobrien "Return true if current line is a label (not the default: case label)." 56759243Sobrien (and 56859243Sobrien (looking-at csh-label-re) 56959243Sobrien (not (looking-at "^\\s *default:")))) 57059243Sobrien 57159243Sobrien(defun csh-match-indent-level (begin-re end-re) 57259243Sobrien "Match the compound command and indent. Return nil on no match, 57359243Sobrienindentation to use for this line otherwise." 57459243Sobrien (interactive) 57559243Sobrien (let* ((case-fold-search) 57659243Sobrien (nest-list 57759243Sobrien (save-excursion 57859243Sobrien (csh-get-compound-level begin-re end-re (point)) 57959243Sobrien )) 58059243Sobrien ) ;; bindings 58159243Sobrien (if (null nest-list) 58259243Sobrien (progn 58359243Sobrien (if csh-match-and-tell 58459243Sobrien (message "No matching compound command")) 58559243Sobrien nil) ;; Propagate a miss. 58659243Sobrien (let* ( 58759243Sobrien (nest-level (car nest-list)) 58859243Sobrien (match-line (cdr nest-list)) 58959243Sobrien ) ;; bindings 59059243Sobrien (if csh-match-and-tell 59159243Sobrien (save-excursion 59259243Sobrien (goto-line match-line) 59359243Sobrien (message "Matched ... %s" (csh-line-to-string)) 59459243Sobrien ) ;; excursion 59559243Sobrien ) ;; if csh-match-and-tell 59659243Sobrien nest-level ;;Propagate a hit. 59759243Sobrien ) ;; let* 59859243Sobrien ) ;; if 59959243Sobrien ) ;; let* 60059243Sobrien ) ;; defun csh-match-indent-level 60159243Sobrien 60259243Sobrien(defun csh-match-structure-and-reindent () 60359243Sobrien "If the current line matches one of the indenting keywords 60459243Sobrienor one of the control structure ending keywords then reindent. Also 60559243Sobrienif csh-match-and-tell is non-nil the matching structure will echo in 60659243Sobrienthe minibuffer" 60759243Sobrien (interactive) 60859243Sobrien (let (case-fold-search) 60959243Sobrien (save-excursion 61059243Sobrien (beginning-of-line) 61159243Sobrien (cond ((looking-at csh-else-re) 61259243Sobrien (csh-match-indent-level csh-if-re csh-endif-re)) 61359243Sobrien ((looking-at csh-else-if-re) 61459243Sobrien (csh-match-indent-level csh-if-re csh-endif-re)) 61559243Sobrien ((looking-at csh-endif-re) 61659243Sobrien (csh-match-indent-level csh-if-re csh-endif-re)) 61759243Sobrien ((looking-at csh-end-re) 61859243Sobrien (csh-match-indent-level csh-iteration-keywords-re csh-end-re)) 61959243Sobrien ((looking-at csh-endsw-re) 62059243Sobrien (csh-match-indent-level csh-switch-re csh-endsw-re)) 62159243Sobrien ((csh-looking-at-label) 62259243Sobrien ;; Flush control-flow labels left since they don't nest. 62359243Sobrien 0) 62459243Sobrien ;; 62559243Sobrien (t nil) 62659243Sobrien );; cond 62759243Sobrien ) 62859243Sobrien )) 62959243Sobrien 63059243Sobrien;;;###autoload 63159243Sobrien(defun csh-mode () 63259243Sobrien "csh-mode 2.0 - Major mode for editing csh and tcsh scripts. 63359243SobrienSpecial key bindings and commands: 63459243Sobrien\\{csh-mode-map} 63559243SobrienVariables controlling indentation style: 63659243Sobriencsh-indent 63759243Sobrien Indentation of csh statements with respect to containing block. 63859243Sobrien Default value is 4. 63959243Sobriencsh-case-indent 64059243Sobrien Additional indentation for statements under case items. 64159243Sobrien Default value is nil which will align the statements one position 64259243Sobrien past the \")\" of the pattern. 64359243Sobriencsh-case-item-offset 64459243Sobrien Additional indentation for case items within a case statement. 64559243Sobrien Default value is 2. 64659243Sobriencsh-tab-always-indent 64759243Sobrien Controls the operation of the TAB key. If t (the default), always 64859243Sobrien reindent the current line. If nil, indent the current line only if 64959243Sobrien point is at the left margin or in the line's indentation; otherwise 65059243Sobrien insert a tab. 65159243Sobriencsh-match-and-tell 65259243Sobrien If non-nil echo in the minibuffer the matching compound command 65359243Sobrien for the \"done\", \"}\", \"fi\", or \"endsw\". Default value is t. 65459243Sobrien 65559243Sobriencsh-comment-regexp 65659243Sobrien Regular expression used to recognize comments. Customize to support 65759243Sobrien csh-like languages. Default value is \"\^\\\\s *#\". 65859243Sobrien 65959243SobrienStyle Guide. 66059243Sobrien By setting 66159243Sobrien (setq csh-indent default-tab-width) 66259243Sobrien 66359243Sobrien The following style is obtained: 66459243Sobrien 66559243Sobrien if [ -z $foo ] 66659243Sobrien then 66759243Sobrien bar # <-- csh-group-offset is additive to csh-indent 66859243Sobrien foo 66959243Sobrien fi 67059243Sobrien 67159243Sobrien By setting 67259243Sobrien (setq csh-indent default-tab-width) 67359243Sobrien (setq csh-group-offset (- 0 csh-indent)) 67459243Sobrien 67559243Sobrien The following style is obtained: 67659243Sobrien 67759243Sobrien if [ -z $foo ] 67859243Sobrien then 67959243Sobrien bar 68059243Sobrien foo 68159243Sobrien fi 68259243Sobrien 68359243Sobrien By setting 68459243Sobrien (setq csh-case-item-offset 1) 68559243Sobrien (setq csh-case-indent nil) 68659243Sobrien 68759243Sobrien The following style is obtained: 68859243Sobrien 68959243Sobrien case x in * 69059243Sobrien foo) bar # <-- csh-case-item-offset 69159243Sobrien baz;; # <-- csh-case-indent aligns with \")\" 69259243Sobrien foobar) foo 69359243Sobrien bar;; 69459243Sobrien endsw 69559243Sobrien 69659243Sobrien By setting 69759243Sobrien (setq csh-case-item-offset 1) 69859243Sobrien (setq csh-case-indent 6) 69959243Sobrien 70059243Sobrien The following style is obtained: 70159243Sobrien 70259243Sobrien case x in * 70359243Sobrien foo) bar # <-- csh-case-item-offset 70459243Sobrien baz;; # <-- csh-case-indent 70559243Sobrien foobar) foo 70659243Sobrien bar;; 70759243Sobrien endsw 70859243Sobrien 70959243Sobrien 71059243SobrienInstallation: 71159243Sobrien Put csh-mode.el in some directory in your load-path. 71259243Sobrien Put the following forms in your .emacs file. 71359243Sobrien 71459243Sobrien (setq auto-mode-alist 71559243Sobrien (append auto-mode-alist 71659243Sobrien (list 71759243Sobrien '(\"\\\\.csh$\" . csh-mode) 71859243Sobrien '(\"\\\\.login\" . csh-mode)))) 71959243Sobrien 72059243Sobrien (setq csh-mode-hook 72159243Sobrien (function (lambda () 72259243Sobrien (font-lock-mode 1) ;; font-lock the buffer 72359243Sobrien (setq csh-indent 8) 72459243Sobrien (setq csh-tab-always-indent t) 72559243Sobrien (setq csh-match-and-tell t) 72659243Sobrien (setq csh-align-to-keyword t) ;; Turn on keyword alignment 72759243Sobrien )))" 72859243Sobrien (interactive) 72959243Sobrien (kill-all-local-variables) 73059243Sobrien (use-local-map csh-mode-map) 73159243Sobrien (setq major-mode 'csh-mode) 73259243Sobrien (setq mode-name "Csh") 73359243Sobrien (setq local-abbrev-table csh-mode-abbrev-table) 73459243Sobrien (set-syntax-table csh-mode-syntax-table) 73559243Sobrien (make-local-variable 'indent-line-function) 73659243Sobrien (setq indent-line-function 'csh-indent-line) 73759243Sobrien (make-local-variable 'indent-region-function) 73859243Sobrien (setq indent-region-function 'csh-indent-region) 73959243Sobrien (make-local-variable 'comment-start) 74059243Sobrien (setq comment-start "# ") 74159243Sobrien (make-local-variable 'comment-end) 74259243Sobrien (setq comment-end "") 74359243Sobrien (make-local-variable 'comment-column) 74459243Sobrien (setq comment-column 32) 74559243Sobrien (make-local-variable 'comment-start-skip) 74659243Sobrien (setq comment-start-skip "#+ *") 74759243Sobrien ;; 74859243Sobrien ;; config font-lock mode 74959243Sobrien (make-local-variable 'font-lock-keywords) 75059243Sobrien (setq font-lock-keywords csh-font-lock-keywords) 75159243Sobrien ;; 75259243Sobrien ;; Let the user customize 75359243Sobrien (run-hooks 'csh-mode-hook) 75459243Sobrien ) ;; defun 75559243Sobrien 75659243Sobrien;; 75759243Sobrien;; Completion code supplied by Haavard Rue <hrue@imf.unit.no>. 75859243Sobrien;; 75959243Sobrien;; 76059243Sobrien;; add a completion with a given type to the list 76159243Sobrien;; 76259243Sobrien(defun csh-addto-alist (completion type) 76359243Sobrien (setq csh-completion-list 76459243Sobrien (append csh-completion-list 76559243Sobrien (list (cons completion type))))) 76659243Sobrien 76759243Sobrien(defun csh-bol-point () 76859243Sobrien (save-excursion 76959243Sobrien (beginning-of-line) 77059243Sobrien (point))) 77159243Sobrien 77259243Sobrien(defun csh-complete-symbol () 77359243Sobrien "Perform completion." 77459243Sobrien (interactive) 77559243Sobrien (let* ((case-fold-search) 77659243Sobrien (end (point)) 77759243Sobrien (beg (unwind-protect 77859243Sobrien (save-excursion 77959243Sobrien (backward-sexp 1) 78059243Sobrien (while (= (char-syntax (following-char)) ?\') 78159243Sobrien (forward-char 1)) 78259243Sobrien (point)))) 78359243Sobrien (pattern (buffer-substring beg end)) 78459243Sobrien (predicate 78559243Sobrien ;; 78659243Sobrien ;; ` or $( mark a function 78759243Sobrien ;; 78859243Sobrien (save-excursion 78959243Sobrien (goto-char beg) 79059243Sobrien (if (or 79159243Sobrien (save-excursion 79259243Sobrien (backward-char 1) 79359243Sobrien (looking-at "`")) 79459243Sobrien (save-excursion 79559243Sobrien (backward-char 2) 79659243Sobrien (looking-at "\\$("))) 79759243Sobrien (function (lambda (sym) 79859243Sobrien (equal (cdr sym) csh-completion-type-function))) 79959243Sobrien ;; 80059243Sobrien ;; a $, ${ or ${# mark a variable 80159243Sobrien ;; 80259243Sobrien (if (or 80359243Sobrien (save-excursion 80459243Sobrien (backward-char 1) 80559243Sobrien (looking-at "\\$")) 80659243Sobrien (save-excursion 80759243Sobrien (backward-char 2) 80859243Sobrien (looking-at "\\${")) 80959243Sobrien (save-excursion 81059243Sobrien (backward-char 3) 81159243Sobrien (looking-at "\\${#"))) 81259243Sobrien (function (lambda (sym) 81359243Sobrien (equal (cdr sym) 81459243Sobrien csh-completion-type-var))) 81559243Sobrien ;; 81659243Sobrien ;; don't know. use 'em all 81759243Sobrien ;; 81859243Sobrien (function (lambda (sym) t)))))) 81959243Sobrien ;; 82059243Sobrien (completion (try-completion pattern csh-completion-list predicate))) 82159243Sobrien ;; 82259243Sobrien (cond ((eq completion t)) 82359243Sobrien ;; 82459243Sobrien ;; oops, what is this ? 82559243Sobrien ;; 82659243Sobrien ((null completion) 82759243Sobrien (message "Can't find completion for \"%s\"" pattern)) 82859243Sobrien ;; 82959243Sobrien ;; insert 83059243Sobrien ;; 83159243Sobrien ((not (string= pattern completion)) 83259243Sobrien (delete-region beg end) 83359243Sobrien (insert completion)) 83459243Sobrien ;; 83559243Sobrien ;; write possible completion in the minibuffer, 83659243Sobrien ;; use this instead of a seperate buffer (usual) 83759243Sobrien ;; 83859243Sobrien (t 83959243Sobrien (let ((list (all-completions pattern csh-completion-list predicate)) 84059243Sobrien (string "")) 84159243Sobrien (while list 84259243Sobrien (progn 84359243Sobrien (setq string (concat string (format "%s " (car list)))) 84459243Sobrien (setq list (cdr list)))) 84559243Sobrien (message string)))))) 84659243Sobrien 84759243Sobrien;; 84859243Sobrien;; init the list and pickup all 84959243Sobrien;; 85059243Sobrien(defun csh-completion-init-and-pickup () 85159243Sobrien (interactive) 85259243Sobrien (let (case-fold-search) 85359243Sobrien (csh-completion-list-init) 85459243Sobrien (csh-pickup-all))) 85559243Sobrien 85659243Sobrien;; 85759243Sobrien;; init the list 85859243Sobrien;; 85959243Sobrien(defun csh-completion-list-init () 86059243Sobrien (interactive) 86159243Sobrien (setq csh-completion-list 86259243Sobrien (list 86359243Sobrien (cons "break" csh-completion-type-misc) 86459243Sobrien (cons "breaksw" csh-completion-type-misc) 86559243Sobrien (cons "case" csh-completion-type-misc) 86659243Sobrien (cons "continue" csh-completion-type-misc) 86759243Sobrien (cons "endif" csh-completion-type-misc) 86859243Sobrien (cons "exit" csh-completion-type-misc) 86959243Sobrien (cons "foreach" csh-completion-type-misc) 87059243Sobrien (cons "if" csh-completion-type-misc) 87159243Sobrien (cons "while" csh-completion-type-misc)))) 87259243Sobrien 87359243Sobrien(defun csh-eol-point () 87459243Sobrien (save-excursion 87559243Sobrien (end-of-line) 87659243Sobrien (point))) 87759243Sobrien 87859243Sobrien(defun csh-pickup-all () 87959243Sobrien "Pickup all completions in buffer." 88059243Sobrien (interactive) 88159243Sobrien (csh-pickup-completion-driver (point-min) (point-max) t)) 88259243Sobrien 88359243Sobrien(defun csh-pickup-completion (regexp type match pmin pmax) 88459243Sobrien "Pickup completion in region and addit to the list, if not already 88559243Sobrienthere." 88659243Sobrien (let ((i 0) kw obj) 88759243Sobrien (save-excursion 88859243Sobrien (goto-char pmin) 88959243Sobrien (while (and 89059243Sobrien (re-search-forward regexp pmax t) 89159243Sobrien (match-beginning match) 89259243Sobrien (setq kw (buffer-substring 89359243Sobrien (match-beginning match) 89459243Sobrien (match-end match)))) 89559243Sobrien (progn 89659243Sobrien (setq obj (assoc kw csh-completion-list)) 89759243Sobrien (if (or (equal nil obj) 89859243Sobrien (and (not (equal nil obj)) 89959243Sobrien (not (= type (cdr obj))))) 90059243Sobrien (progn 90159243Sobrien (setq i (1+ i)) 90259243Sobrien (csh-addto-alist kw type)))))) 90359243Sobrien i)) 90459243Sobrien 90559243Sobrien(defun csh-pickup-completion-driver (pmin pmax message) 90659243Sobrien "Driver routine for csh-pickup-completion." 90759243Sobrien (if message 90859243Sobrien (message "pickup completion...")) 90959243Sobrien (let* ( 91059243Sobrien (i1 91159243Sobrien (csh-pickup-completion csh-completion-regexp-var 91259243Sobrien csh-completion-type-var 91359243Sobrien csh-completion-match-var 91459243Sobrien pmin pmax)) 91559243Sobrien (i2 91659243Sobrien (csh-pickup-completion csh-completion-regexp-var2 91759243Sobrien csh-completion-type-var 91859243Sobrien csh-completion-match-var2 91959243Sobrien pmin pmax)) 92059243Sobrien (i3 92159243Sobrien (csh-pickup-completion csh-completion-regexp-function 92259243Sobrien csh-completion-type-function 92359243Sobrien csh-completion-match-function 92459243Sobrien pmin pmax))) 92559243Sobrien (if message 92659243Sobrien (message "pickup %d variables and %d functions." (+ i1 i2) i3)))) 92759243Sobrien 92859243Sobrien(defun csh-pickup-this-line () 92959243Sobrien "Pickup all completions in current line." 93059243Sobrien (interactive) 93159243Sobrien (csh-pickup-completion-driver (csh-bol-point) (csh-eol-point) nil)) 93259243Sobrien 93359243Sobrien 93459243Sobrien(provide 'csh-mode) 93559243Sobrien;;; csh-mode.el ends here 936