1;; csh-mode.el --- csh (and tcsh) script editing mode for Emacs. 2;; 3;; Version: 1.2 4;; Date: April 2, 1999 5;; Maintainer: Dan Harkless <software@harkless.org> 6;; 7;; Description: 8;; csh and tcsh script editing mode for Emacs. 9;; 10;; Installation: 11;; Put csh-mode.el in some directory in your load-path and load it. 12;; 13;; Usage: 14;; This major mode assists shell script writers with indentation 15;; control and control structure construct matching in much the same 16;; fashion as other programming language modes. Invoke describe-mode 17;; for more information. 18;; 19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 20;; 21;; Author key: 22;; DH - Dan Harkless <software@harkless.org> 23;; CM - Carlo Migliorini <migliorini@sodalia.it> 24;; JR - Jack Repenning <jackr@sgi.com> 25;; GE - Gary Ellison <Gary.F.Ellison@att.com> 26;; 27;; *** REVISION HISTORY *** 28;; 29;; DATE MOD. BY REASON FOR MODIFICATION 30;; --------- -- -------------------------------------------------------------- 31;; 2 Apr 99 DH 1.2: Noticed an out-of-date comment referencing .bashrc etc. 32;; 11 Dec 96 DH 1.1: ksh-mode just indented continuation lines by 1 space. 33;; csh-mode looks at the first line and indents properly to line 34;; up under the open-paren, quote, or command. 35;; 11 Dec 96 DH Added fontification for history substitutions. 36;; 10 Dec 96 DH Added indentation and fontification for labels. Added 37;; fontification for variables and backquoted strings. 38;; 9 Dec 96 DH 1.0: Brought csh-mode up to the level of functionality of 39;; the original ksh-mode. 40;; 7 Oct 96 CM 0.1: Hacked ksh-mode.el into minimally functional csh-mode.el 41;; by doing search-and-replace and some keyword changes. 42;; 8 Aug 96 JR (Last modification to ksh-mode 2.6.) 43;; [...] 44;; 19 Jun 92 GE (Conception of ksh-mode.) 45;; 46;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 47 48 49(defconst csh-mode-version "1.2" 50 "*Version number of this version of csh-mode") 51 52(defvar csh-mode-hook 53 '(lambda () 54 (auto-fill-mode 1)) 55 "Hook to run each time csh-mode is entered.") 56 57 58;; 59;; -------------------------------------------> Variables controlling completion 60;; 61(defvar csh-completion-list '()) 62(make-variable-buffer-local 'csh-completion-list) 63(set-default 'csh-completion-list '()) 64;; 65;; -type- : type number, 0:misc, 1:variable, 2:function 66;; -regexp-: regexp used to parse the script 67;; -match- : used by match-beginning/end to pickup target 68;; 69(defvar csh-completion-type-misc 0) 70(defvar csh-completion-regexp-var "\\([A-Za-z_0-9]+\\)=") 71(defvar csh-completion-type-var 1) 72(defvar csh-completion-match-var 1) 73(defvar csh-completion-regexp-var2 "\\$\\({\\|{#\\)?\\([A-Za-z_0-9]+\\)[#%:}]?") 74(defvar csh-completion-match-var2 2) 75(defvar csh-completion-regexp-function 76 "\\(function\\)?[ \t]*\\([A-Za-z_0-9]+\\)[ \t]*([ \t]*)") 77(defvar csh-completion-type-function 2) 78(defvar csh-completion-match-function 2) 79 80 81;; 82;; ------------------------------------> Variables controlling indentation style 83;; 84(defvar csh-indent 4 85 "*Indentation of csh statements with respect to containing block. A value 86of nil indicates compound list keyword \(\"do\" and \"then\"\) alignment.") 87 88(defvar csh-case-item-offset csh-indent 89 "*Additional indentation for case items within a case statement.") 90(defvar csh-case-indent nil 91 "*Additional indentation for statements under case items.") 92(defvar csh-comment-regexp "^\\s *#" 93 "*Regular expression used to recognize comments. Customize to support 94csh-like languages.") 95(defvar csh-match-and-tell t 96 "*If non-nil echo in the minibuffer the matching compound command 97for the \"breaksw\", \"end\", or \"endif\".") 98(defvar csh-tab-always-indent t 99 "*Controls the operation of the TAB key. If t (the default), always 100reindent the current line. If nil, indent the current line only if 101point is at the left margin or in the line's indentation; otherwise 102insert a tab.") 103 104 105;; 106;; ----------------------------------------> Constants containing syntax regexps 107;; 108(defconst csh-case-default-re 109 "^\\s *\\(case\\|default\\)\\b" 110 "Regexp used to locate grouping keywords case and default" ) 111 112(defconst csh-case-item-re "^\\s *\\(case .*\\|default\\):" 113 "Regexp used to match case-items") 114 115(defconst csh-end-re "^\\s *end\\b" 116 "Regexp used to match keyword: end") 117 118(defconst csh-endif-re "^\\s *endif\\b" 119 "Regexp used to match keyword: endif") 120 121(defconst csh-endsw-re "^\\s *endsw\\b" 122 "Regexp used to match keyword: endsw") 123 124(defconst csh-else-re "^\\s *\\belse\\(\\b\\|$\\)" 125 "Regexp used to match keyword: else") 126 127(defconst csh-else-if-re "^\\s *\\belse if\\(\\b\\|$\\)" 128 "Regexp used to match keyword pair: else if") 129 130(defconst csh-if-re "^\\s *if\\b.+\\(\\\\\\|\\bthen\\b\\)" 131 "Regexp used to match non-one-line if statements") 132 133(defconst csh-iteration-keywords-re "^[^#\n]*\\s\"*\\b\\(while\\|foreach\\)\\b" 134 "Match one of the keywords: while, foreach") 135 136(defconst csh-keywords-re 137 "^\\s *\\(else\\b\\|foreach\\b\\|if\\b.+\\(\\\\\\|\\bthen\\b\\)\\|switch\\b\\|while\\b\\)" 138 "Regexp used to detect compound command keywords: else, if, foreach, while") 139 140(defconst csh-label-re "^\\s *[^!#$\n ]+:" 141 "Regexp used to match flow-control labels") 142 143(defconst csh-multiline-re "^.*\\\\$" 144 "Regexp used to match a line with a statement using more lines.") 145 146(defconst csh-switch-re "^\\s *switch\\b" 147 "Regexp used to match keyword: switch") 148 149 150;; 151;; ----------------------------------------> Variables controlling fontification 152;; 153(defvar csh-keywords '("@" "alias" "bg" "break" "breaksw" "case" "cd" "chdir" 154 "continue" "default" "dirs" "echo" "else" "end" "endif" 155 "endsw" "eval" "exec" "exit" "fg" "foreach" "glob" "goto" 156 "hashstat" "history" "if" "jobs" "kill" "limit" "login" 157 "logout" "limit" "notify" "onintr" "popd" "printenv" 158 "pushd" "rehash" "repeat" "set" "setenv" "shift" "source" 159 "stop" "suspend" "switch" "then" "time" "umask" "unalias" 160 "unhash" "unlimit" "unset" "unsetenv" "wait" "while" 161 ;; tcsh-keywords 162 "alloc" "bindkey" "builtins" "complete" "echotc" 163 "filetest" "hup" "log" "ls-F" "nice" "nohup" "sched" 164 "settc" "setty" "telltc" "uncomplete" "where" "which")) 165 166(require 'font-lock) ; need to do this before referring to font-lock-* below 167 168(defconst csh-font-lock-keywords 169 ;; NOTE: The order of some of the items in this list is significant. Do not 170 ;; alphabetize or otherwise blindly rearrange. 171 (list 172 ;; Comments on line 1, which are missed by syntactic fontification. 173 '("^#.*" 0 font-lock-comment-face) 174 175 ;; Label definitions (1 means first parenthesized exp in regexp). 176 '("^\\s *\\([^!#$\n ]+\\):" 1 font-lock-function-name-face) 177 178 ;; Label references. 179 '("\\b\\(goto\\|onintr\\)\\b\\s +\\([^!#$ \n\t]+\\)" 180 2 font-lock-function-name-face) 181 182 ;; Variable settings. 183 '("\\(@\\|set\\|setenv\\)\\s +\\([0-9A-Za-z_]+\\b\\)" 184 2 font-lock-variable-name-face) 185 186 ;; Variable references not inside of strings. 187 '("\\$[][0-9A-Za-z_#:?]+" 0 font-lock-variable-name-face) 188 189 ;; Backquoted strings. 'keep' means to just fontify non-fontified text. 190 '("`\\(.*\\)`" 1 font-lock-reference-face keep) 191 192 ;; NOTE: The following variables need to be anchored to the beginning of 193 ;; line to prevent re-fontifying text in comments. Due to this, we 194 ;; can only catch a finite number of occurrences. More can be added. 195 ;; The 't' means to override previous fontification. 196 ;; 197 ;; Variable references inside of " strings. 198 '("^[^#\n]*\".*\\(\\$[][0-9A-Za-z_#:?]+\\).*\"" 199 1 font-lock-variable-name-face t) ; 1 200 '("^[^#\n]*\".*\\(\\$[][0-9A-Za-z_#:?]+\\).*\\$[][0-9A-Za-z_#:?]+.*\"" 201 1 font-lock-variable-name-face t) ; 2 202 (cons (concat "^[^#\n]*\".*\\(\\$[][0-9A-Za-z_#:?]+\\).*" 203 "\\$[][0-9A-Za-z_#:?]+.*\\$[][0-9A-Za-z_#:?]+.*\"") 204 (list 1 font-lock-variable-name-face t)) ; 3 205 ;; 206 ;; History substitutions. 207 '("^![^~= \n\t]+" 0 font-lock-reference-face t) ; BOL 208 '("^[^#\n]*[^#\\\n]\\(![^~= \n\t]+\\)" 1 font-lock-reference-face t) ; 1 209 '("^[^#\n]*[^#\\\n]\\(![^~= \n\t]+\\).*![^~= \n\t]+" 210 1 font-lock-reference-face t) ; 2 211 212 ;; Keywords. 213 (cons (concat 214 "\\(\\<" 215 (mapconcat 'identity csh-keywords "\\>\\|\\<") 216 "\\>\\)") 217 1) 218 )) 219 220(put 'csh-mode 'font-lock-keywords 'csh-font-lock-keywords) 221 222 223;; 224;; -------------------------------------------------------> Mode-specific tables 225;; 226(defvar csh-mode-abbrev-table nil 227 "Abbrev table used while in csh mode.") 228(define-abbrev-table 'csh-mode-abbrev-table ()) 229 230(defvar csh-mode-map nil 231 "Keymap used in csh mode") 232(if csh-mode-map 233 () 234 (setq csh-mode-map (make-sparse-keymap)) 235;;(define-key csh-mode-map "\177" 'backward-delete-char-untabify) 236 (define-key csh-mode-map "\C-c\t" 'csh-completion-init-and-pickup) 237 (define-key csh-mode-map "\C-j" 'reindent-then-newline-and-indent) 238 (define-key csh-mode-map "\e\t" 'csh-complete-symbol) 239 (define-key csh-mode-map "\n" 'reindent-then-newline-and-indent) 240 (define-key csh-mode-map '[return] 'reindent-then-newline-and-indent) 241 (define-key csh-mode-map "\t" 'csh-indent-command) 242;;(define-key csh-mode-map "\t" 'csh-indent-line) 243 ) 244 245(defvar csh-mode-syntax-table nil 246 "Syntax table used while in csh mode.") 247(if csh-mode-syntax-table 248 ;; If it's already set up, don't change it. 249 () 250 ;; Else, create it from the standard table and modify entries that need to be. 251 (setq csh-mode-syntax-table (make-syntax-table)) 252 (modify-syntax-entry ?& "." csh-mode-syntax-table) ; & -punctuation 253 (modify-syntax-entry ?* "." csh-mode-syntax-table) ; * -punctuation 254 (modify-syntax-entry ?- "." csh-mode-syntax-table) ; - -punctuation 255 (modify-syntax-entry ?= "." csh-mode-syntax-table) ; = -punctuation 256 (modify-syntax-entry ?+ "." csh-mode-syntax-table) ; + -punctuation 257 (modify-syntax-entry ?| "." csh-mode-syntax-table) ; | -punctuation 258 (modify-syntax-entry ?< "." csh-mode-syntax-table) ; < -punctuation 259 (modify-syntax-entry ?> "." csh-mode-syntax-table) ; > -punctuation 260 (modify-syntax-entry ?/ "." csh-mode-syntax-table) ; / -punctuation 261 (modify-syntax-entry ?\' "\"" csh-mode-syntax-table) ; ' -string quote 262 (modify-syntax-entry ?. "w" csh-mode-syntax-table) ; . -word constituent 263 (modify-syntax-entry ?? "w" csh-mode-syntax-table) ; ? -word constituent 264 265 ;; \n - comment ender, first character of 2-char comment sequence 266 (modify-syntax-entry ?\n "> 1" csh-mode-syntax-table) ; # -word constituent 267 268 ;; - whitespace, first character of 2-char comment sequence 269 (modify-syntax-entry ? " 1" csh-mode-syntax-table) ; 270 271 ;; \t - whitespace, first character of 2-char comment sequence 272 (modify-syntax-entry ?\t " 1" csh-mode-syntax-table) ; # -word constituent 273 274 ;; # - word constituent, second character of 2-char comment sequence 275 (modify-syntax-entry ?# "w 2" csh-mode-syntax-table) ; # -word constituent 276 ) 277 278 279;; 280;; ------------------------------------------------------------------> Functions 281;; 282(defun csh-current-line () 283 "Return the vertical position of point in the buffer. 284Top line is 1." 285 (+ (count-lines (point-min) (point)) 286 (if (= (current-column) 0) 1 0)) 287 ) 288 289(defun csh-get-compound-level 290 (begin-re end-re anchor-point &optional balance-list) 291 "Determine how much to indent this structure. Return a list (level line) 292of the matching compound command or nil if no match found." 293 (let* 294 (;; Locate the next compound begin keyword bounded by point-min 295 (match-point (if (re-search-backward begin-re (point-min) t) 296 (match-beginning 0) 0)) 297 (nest-column (if (zerop match-point) 298 1 299 (progn 300 (goto-char match-point) 301 (current-indentation)))) 302 (nest-list (cons 0 0)) ;; sentinel cons since cdr is >= 1 303 ) 304 (if (zerop match-point) 305 nil ;; graceful exit from recursion 306 (progn 307 (if (nlistp balance-list) 308 (setq balance-list (list))) 309 ;; Now search forward from matching start keyword for end keyword 310 (while (and (consp nest-list) (zerop (cdr nest-list)) 311 (re-search-forward end-re anchor-point t)) 312 (if (not (memq (point) balance-list)) 313 (progn 314 (setq balance-list (cons (point) balance-list)) 315 (goto-char match-point) ;; beginning of compound cmd 316 (setq nest-list 317 (csh-get-compound-level begin-re end-re 318 anchor-point balance-list)) 319 ))) 320 321 (cond ((consp nest-list) 322 (if (zerop (cdr nest-list)) 323 (progn 324 (goto-char match-point) 325 (cons nest-column (csh-current-line))) 326 nest-list)) 327 (t nil) 328 ) 329 ) 330 ) 331 ) 332 ) 333 334(defun csh-get-nest-level () 335 "Return a 2 element list (nest-level nest-line) describing where the 336current line should nest." 337 (let ((case-fold-search) 338 (level)) 339 (save-excursion 340 (forward-line -1) 341 (while (and (not (bobp)) 342 (null level)) 343 (if (and (not (looking-at "^\\s *$")) 344 (not (save-excursion 345 (forward-line -1) 346 (beginning-of-line) 347 (looking-at csh-multiline-re))) 348 (not (looking-at csh-comment-regexp))) 349 (setq level (cons (current-indentation) 350 (csh-current-line))) 351 (forward-line -1) 352 );; if 353 );; while 354 (if (null level) 355 (cons (current-indentation) (csh-current-line)) 356 level) 357 ) 358 ) 359 ) 360 361(defun csh-get-nester-column (nest-line) 362 "Return the column to indent to with respect to nest-line taking 363into consideration keywords and other nesting constructs." 364 (save-excursion 365 (let ((fence-post) 366 (case-fold-search) 367 (start-line (csh-current-line))) 368 ;; 369 ;; Handle case item indentation constructs for this line 370 (cond ((looking-at csh-case-item-re) 371 ;; This line is a case item... 372 (save-excursion 373 (goto-line nest-line) 374 (let ((fence-post (save-excursion (end-of-line) (point)))) 375 (cond ((re-search-forward csh-switch-re fence-post t) 376 ;; If this is the first case under the switch, indent. 377 (goto-char (match-beginning 0)) 378 (+ (current-indentation) csh-case-item-offset)) 379 380 ((re-search-forward csh-case-item-re fence-post t) 381 ;; If this is another case right under a previous case 382 ;; without intervening code, stay at the same 383 ;; indentation. 384 (goto-char (match-beginning 0)) 385 (current-indentation)) 386 387 (t 388 ;; Else, this is a new case. Outdent. 389 (- (current-indentation) csh-case-item-offset)) 390 ) 391 ))) 392 (t;; Not a case-item. What to do relative to the nest-line? 393 (save-excursion 394 (goto-line nest-line) 395 (setq fence-post (save-excursion (end-of-line) (point))) 396 (save-excursion 397 (cond 398 ;; 399 ;; Check if we are in a continued statement 400 ((and (looking-at csh-multiline-re) 401 (save-excursion 402 (goto-line (1- start-line)) 403 (looking-at csh-multiline-re))) 404 (if (looking-at ".*[\'\"]\\\\") 405 ;; If this is a continued string, indent under 406 ;; opening quote. 407 (progn 408 (re-search-forward "[\'\"]") 409 (forward-char -1)) 410 (if (looking-at ".*([^\)\n]*\\\\") 411 ;; Else if this is a continued parenthesized 412 ;; list, indent after paren. 413 (re-search-forward "(" fence-post t) 414 ;; Else, indent after whitespace after first word. 415 (re-search-forward "[^ \t]+[ \t]+" fence-post t))) 416 (current-column)) 417 418 ;; In order to locate the column of the keyword, 419 ;; which might be embedded within a case-item, 420 ;; it is necessary to use re-search-forward. 421 ;; Search by literal case, since shell is 422 ;; case-sensitive. 423 ((re-search-forward csh-keywords-re fence-post t) 424 (goto-char (match-beginning 1)) 425 (if (looking-at csh-switch-re) 426 (+ (current-indentation) csh-case-item-offset) 427 (+ (current-indentation) 428 (if (null csh-indent) 429 2 csh-indent) 430 ))) 431 432 ((re-search-forward csh-case-default-re fence-post t) 433 (if (null csh-indent) 434 (progn 435 (goto-char (match-end 1)) 436 (+ (current-indentation) 1)) 437 (progn 438 (goto-char (match-beginning 1)) 439 (+ (current-indentation) csh-indent)) 440 )) 441 442 ;; 443 ;; Now detect first statement under a case item 444 ((looking-at csh-case-item-re) 445 (if (null csh-case-indent) 446 (progn 447 (re-search-forward csh-case-item-re fence-post t) 448 (goto-char (match-end 1)) 449 (+ (current-column) 1)) 450 (+ (current-indentation) csh-case-indent))) 451 452 ;; 453 ;; If this is the first statement under a control-flow 454 ;; label, indent one level. 455 ((csh-looking-at-label) 456 (+ (current-indentation) csh-indent)) 457 458 ;; This is hosed when using current-column 459 ;; and there is a multi-command expression as the 460 ;; nester. 461 (t (current-indentation))) 462 ) 463 ));; excursion over 464 );; Not a case-item 465 );;let 466 );; excursion 467 );; defun 468 469(defun csh-indent-command () 470 "Indent current line relative to containing block and allow for 471csh-tab-always-indent customization" 472 (interactive) 473 (let (case-fold-search) 474 (cond ((save-excursion 475 (skip-chars-backward " \t") 476 (bolp)) 477 (csh-indent-line)) 478 (csh-tab-always-indent 479 (save-excursion 480 (csh-indent-line))) 481 (t (insert-tab)) 482 )) 483 ) 484 485(defun csh-indent-line () 486 "Indent current line as far as it should go according 487to the syntax/context" 488 (interactive) 489 (let (case-fold-search) 490 (save-excursion 491 (beginning-of-line) 492 (if (bobp) 493 nil 494 ;; 495 ;; Align this line to current nesting level 496 (let* 497 ( 498 (level-list (csh-get-nest-level)) ; Where to nest against 499 ;; (last-line-level (car level-list)) 500 (this-line-level (current-indentation)) 501 (nester-column (csh-get-nester-column (cdr level-list))) 502 (struct-match (csh-match-structure-and-reindent)) 503 ) 504 (if struct-match 505 (setq nester-column struct-match)) 506 (if (eq nester-column this-line-level) 507 nil 508 (beginning-of-line) 509 (let ((beg (point))) 510 (back-to-indentation) 511 (delete-region beg (point))) 512 (indent-to nester-column)) 513 );; let* 514 );; if 515 );; excursion 516 ;; 517 ;; Position point on this line 518 (let* 519 ( 520 (this-line-level (current-indentation)) 521 (this-bol (save-excursion 522 (beginning-of-line) 523 (point))) 524 (this-point (- (point) this-bol)) 525 ) 526 (cond ((> this-line-level this-point);; point in initial white space 527 (back-to-indentation)) 528 (t nil) 529 );; cond 530 );; let* 531 );; let 532 );; defun 533 534(defun csh-indent-region (start end) 535 "From start to end, indent each line." 536 ;; The algorithm is just moving through the region line by line with 537 ;; the match noise turned off. Only modifies nonempty lines. 538 (save-excursion 539 (let (csh-match-and-tell 540 (endmark (copy-marker end))) 541 542 (goto-char start) 543 (beginning-of-line) 544 (setq start (point)) 545 (while (> (marker-position endmark) start) 546 (if (not (and (bolp) (eolp))) 547 (csh-indent-line)) 548 (forward-line 1) 549 (setq start (point))) 550 551 (set-marker endmark nil) 552 ) 553 ) 554 ) 555 556(defun csh-line-to-string () 557 "From point, construct a string from all characters on 558current line" 559 (skip-chars-forward " \t") ;; skip tabs as well as spaces 560 (buffer-substring (point) 561 (progn 562 (end-of-line 1) 563 (point)))) 564 565(defun csh-looking-at-label () 566 "Return true if current line is a label (not the default: case label)." 567 (and 568 (looking-at csh-label-re) 569 (not (looking-at "^\\s *default:")))) 570 571(defun csh-match-indent-level (begin-re end-re) 572 "Match the compound command and indent. Return nil on no match, 573indentation to use for this line otherwise." 574 (interactive) 575 (let* ((case-fold-search) 576 (nest-list 577 (save-excursion 578 (csh-get-compound-level begin-re end-re (point)) 579 )) 580 ) ;; bindings 581 (if (null nest-list) 582 (progn 583 (if csh-match-and-tell 584 (message "No matching compound command")) 585 nil) ;; Propagate a miss. 586 (let* ( 587 (nest-level (car nest-list)) 588 (match-line (cdr nest-list)) 589 ) ;; bindings 590 (if csh-match-and-tell 591 (save-excursion 592 (goto-line match-line) 593 (message "Matched ... %s" (csh-line-to-string)) 594 ) ;; excursion 595 ) ;; if csh-match-and-tell 596 nest-level ;;Propagate a hit. 597 ) ;; let* 598 ) ;; if 599 ) ;; let* 600 ) ;; defun csh-match-indent-level 601 602(defun csh-match-structure-and-reindent () 603 "If the current line matches one of the indenting keywords 604or one of the control structure ending keywords then reindent. Also 605if csh-match-and-tell is non-nil the matching structure will echo in 606the minibuffer" 607 (interactive) 608 (let (case-fold-search) 609 (save-excursion 610 (beginning-of-line) 611 (cond ((looking-at csh-else-re) 612 (csh-match-indent-level csh-if-re csh-endif-re)) 613 ((looking-at csh-else-if-re) 614 (csh-match-indent-level csh-if-re csh-endif-re)) 615 ((looking-at csh-endif-re) 616 (csh-match-indent-level csh-if-re csh-endif-re)) 617 ((looking-at csh-end-re) 618 (csh-match-indent-level csh-iteration-keywords-re csh-end-re)) 619 ((looking-at csh-endsw-re) 620 (csh-match-indent-level csh-switch-re csh-endsw-re)) 621 ((csh-looking-at-label) 622 ;; Flush control-flow labels left since they don't nest. 623 0) 624 ;; 625 (t nil) 626 );; cond 627 ) 628 )) 629 630;;;###autoload 631(defun csh-mode () 632 "csh-mode 2.0 - Major mode for editing csh and tcsh scripts. 633Special key bindings and commands: 634\\{csh-mode-map} 635Variables controlling indentation style: 636csh-indent 637 Indentation of csh statements with respect to containing block. 638 Default value is 4. 639csh-case-indent 640 Additional indentation for statements under case items. 641 Default value is nil which will align the statements one position 642 past the \")\" of the pattern. 643csh-case-item-offset 644 Additional indentation for case items within a case statement. 645 Default value is 2. 646csh-tab-always-indent 647 Controls the operation of the TAB key. If t (the default), always 648 reindent the current line. If nil, indent the current line only if 649 point is at the left margin or in the line's indentation; otherwise 650 insert a tab. 651csh-match-and-tell 652 If non-nil echo in the minibuffer the matching compound command 653 for the \"done\", \"}\", \"fi\", or \"endsw\". Default value is t. 654 655csh-comment-regexp 656 Regular expression used to recognize comments. Customize to support 657 csh-like languages. Default value is \"\^\\\\s *#\". 658 659Style Guide. 660 By setting 661 (setq csh-indent default-tab-width) 662 663 The following style is obtained: 664 665 if [ -z $foo ] 666 then 667 bar # <-- csh-group-offset is additive to csh-indent 668 foo 669 fi 670 671 By setting 672 (setq csh-indent default-tab-width) 673 (setq csh-group-offset (- 0 csh-indent)) 674 675 The following style is obtained: 676 677 if [ -z $foo ] 678 then 679 bar 680 foo 681 fi 682 683 By setting 684 (setq csh-case-item-offset 1) 685 (setq csh-case-indent nil) 686 687 The following style is obtained: 688 689 case x in * 690 foo) bar # <-- csh-case-item-offset 691 baz;; # <-- csh-case-indent aligns with \")\" 692 foobar) foo 693 bar;; 694 endsw 695 696 By setting 697 (setq csh-case-item-offset 1) 698 (setq csh-case-indent 6) 699 700 The following style is obtained: 701 702 case x in * 703 foo) bar # <-- csh-case-item-offset 704 baz;; # <-- csh-case-indent 705 foobar) foo 706 bar;; 707 endsw 708 709 710Installation: 711 Put csh-mode.el in some directory in your load-path. 712 Put the following forms in your .emacs file. 713 714 (setq auto-mode-alist 715 (append auto-mode-alist 716 (list 717 '(\"\\\\.csh$\" . csh-mode) 718 '(\"\\\\.login\" . csh-mode)))) 719 720 (setq csh-mode-hook 721 (function (lambda () 722 (font-lock-mode 1) ;; font-lock the buffer 723 (setq csh-indent 8) 724 (setq csh-tab-always-indent t) 725 (setq csh-match-and-tell t) 726 (setq csh-align-to-keyword t) ;; Turn on keyword alignment 727 )))" 728 (interactive) 729 (kill-all-local-variables) 730 (use-local-map csh-mode-map) 731 (setq major-mode 'csh-mode) 732 (setq mode-name "Csh") 733 (setq local-abbrev-table csh-mode-abbrev-table) 734 (set-syntax-table csh-mode-syntax-table) 735 (make-local-variable 'indent-line-function) 736 (setq indent-line-function 'csh-indent-line) 737 (make-local-variable 'indent-region-function) 738 (setq indent-region-function 'csh-indent-region) 739 (make-local-variable 'comment-start) 740 (setq comment-start "# ") 741 (make-local-variable 'comment-end) 742 (setq comment-end "") 743 (make-local-variable 'comment-column) 744 (setq comment-column 32) 745 (make-local-variable 'comment-start-skip) 746 (setq comment-start-skip "#+ *") 747 ;; 748 ;; config font-lock mode 749 (make-local-variable 'font-lock-keywords) 750 (setq font-lock-keywords csh-font-lock-keywords) 751 ;; 752 ;; Let the user customize 753 (run-hooks 'csh-mode-hook) 754 ) ;; defun 755 756;; 757;; Completion code supplied by Haavard Rue <hrue@imf.unit.no>. 758;; 759;; 760;; add a completion with a given type to the list 761;; 762(defun csh-addto-alist (completion type) 763 (setq csh-completion-list 764 (append csh-completion-list 765 (list (cons completion type))))) 766 767(defun csh-bol-point () 768 (save-excursion 769 (beginning-of-line) 770 (point))) 771 772(defun csh-complete-symbol () 773 "Perform completion." 774 (interactive) 775 (let* ((case-fold-search) 776 (end (point)) 777 (beg (unwind-protect 778 (save-excursion 779 (backward-sexp 1) 780 (while (= (char-syntax (following-char)) ?\') 781 (forward-char 1)) 782 (point)))) 783 (pattern (buffer-substring beg end)) 784 (predicate 785 ;; 786 ;; ` or $( mark a function 787 ;; 788 (save-excursion 789 (goto-char beg) 790 (if (or 791 (save-excursion 792 (backward-char 1) 793 (looking-at "`")) 794 (save-excursion 795 (backward-char 2) 796 (looking-at "\\$("))) 797 (function (lambda (sym) 798 (equal (cdr sym) csh-completion-type-function))) 799 ;; 800 ;; a $, ${ or ${# mark a variable 801 ;; 802 (if (or 803 (save-excursion 804 (backward-char 1) 805 (looking-at "\\$")) 806 (save-excursion 807 (backward-char 2) 808 (looking-at "\\${")) 809 (save-excursion 810 (backward-char 3) 811 (looking-at "\\${#"))) 812 (function (lambda (sym) 813 (equal (cdr sym) 814 csh-completion-type-var))) 815 ;; 816 ;; don't know. use 'em all 817 ;; 818 (function (lambda (sym) t)))))) 819 ;; 820 (completion (try-completion pattern csh-completion-list predicate))) 821 ;; 822 (cond ((eq completion t)) 823 ;; 824 ;; oops, what is this ? 825 ;; 826 ((null completion) 827 (message "Can't find completion for \"%s\"" pattern)) 828 ;; 829 ;; insert 830 ;; 831 ((not (string= pattern completion)) 832 (delete-region beg end) 833 (insert completion)) 834 ;; 835 ;; write possible completion in the minibuffer, 836 ;; use this instead of a seperate buffer (usual) 837 ;; 838 (t 839 (let ((list (all-completions pattern csh-completion-list predicate)) 840 (string "")) 841 (while list 842 (progn 843 (setq string (concat string (format "%s " (car list)))) 844 (setq list (cdr list)))) 845 (message string)))))) 846 847;; 848;; init the list and pickup all 849;; 850(defun csh-completion-init-and-pickup () 851 (interactive) 852 (let (case-fold-search) 853 (csh-completion-list-init) 854 (csh-pickup-all))) 855 856;; 857;; init the list 858;; 859(defun csh-completion-list-init () 860 (interactive) 861 (setq csh-completion-list 862 (list 863 (cons "break" csh-completion-type-misc) 864 (cons "breaksw" csh-completion-type-misc) 865 (cons "case" csh-completion-type-misc) 866 (cons "continue" csh-completion-type-misc) 867 (cons "endif" csh-completion-type-misc) 868 (cons "exit" csh-completion-type-misc) 869 (cons "foreach" csh-completion-type-misc) 870 (cons "if" csh-completion-type-misc) 871 (cons "while" csh-completion-type-misc)))) 872 873(defun csh-eol-point () 874 (save-excursion 875 (end-of-line) 876 (point))) 877 878(defun csh-pickup-all () 879 "Pickup all completions in buffer." 880 (interactive) 881 (csh-pickup-completion-driver (point-min) (point-max) t)) 882 883(defun csh-pickup-completion (regexp type match pmin pmax) 884 "Pickup completion in region and addit to the list, if not already 885there." 886 (let ((i 0) kw obj) 887 (save-excursion 888 (goto-char pmin) 889 (while (and 890 (re-search-forward regexp pmax t) 891 (match-beginning match) 892 (setq kw (buffer-substring 893 (match-beginning match) 894 (match-end match)))) 895 (progn 896 (setq obj (assoc kw csh-completion-list)) 897 (if (or (equal nil obj) 898 (and (not (equal nil obj)) 899 (not (= type (cdr obj))))) 900 (progn 901 (setq i (1+ i)) 902 (csh-addto-alist kw type)))))) 903 i)) 904 905(defun csh-pickup-completion-driver (pmin pmax message) 906 "Driver routine for csh-pickup-completion." 907 (if message 908 (message "pickup completion...")) 909 (let* ( 910 (i1 911 (csh-pickup-completion csh-completion-regexp-var 912 csh-completion-type-var 913 csh-completion-match-var 914 pmin pmax)) 915 (i2 916 (csh-pickup-completion csh-completion-regexp-var2 917 csh-completion-type-var 918 csh-completion-match-var2 919 pmin pmax)) 920 (i3 921 (csh-pickup-completion csh-completion-regexp-function 922 csh-completion-type-function 923 csh-completion-match-function 924 pmin pmax))) 925 (if message 926 (message "pickup %d variables and %d functions." (+ i1 i2) i3)))) 927 928(defun csh-pickup-this-line () 929 "Pickup all completions in current line." 930 (interactive) 931 (csh-pickup-completion-driver (csh-bol-point) (csh-eol-point) nil)) 932 933 934(provide 'csh-mode) 935;;; csh-mode.el ends here 936