aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisp/progmodes/sh-script.el242
1 files changed, 143 insertions, 99 deletions
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index 5927a8069fe..b4db1b903c3 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -138,12 +138,6 @@ Use this where the name of the executable doesn't correspond to the type of
138shell it really is.") 138shell it really is.")
139 139
140 140
141(defvar sh-shells
142 '(("ash") ("bash") ("csh") ("dtksh") ("es") ("itcsh") ("jsh") ("ksh")
143 ("oash") ("pdksh") ("rc") ("sh") ("tcsh") ("wksh") ("wsh") ("zsh"))
144 "*Alist of shells available for completing read in `sh-set-shell'.")
145
146
147(defvar sh-shell-path (or (getenv "SHELL") "/bin/sh") 141(defvar sh-shell-path (or (getenv "SHELL") "/bin/sh")
148 "*The executable of the shell being programmed.") 142 "*The executable of the shell being programmed.")
149 143
@@ -347,13 +341,13 @@ That command is also used for setting this variable.")
347 341
348 342
349(defvar sh-beginning-of-command 343(defvar sh-beginning-of-command
350 "\\([;({`|&]\\|^\\)[ \t]*\\([/~:a-zA-Z0-9]\\)" 344 "\\([;({`|&]\\|\\`\\|[^\\]\n\\)[ \t]*\\([/~a-zA-Z0-9:]\\)"
351 "*Regexp to determine the beginning of a shell command. 345 "*Regexp to determine the beginning of a shell command.
352The actual command starts at the beginning of the second \\(grouping\\).") 346The actual command starts at the beginning of the second \\(grouping\\).")
353 347
354 348
355(defvar sh-end-of-command 349(defvar sh-end-of-command
356 "\\([/~:a-zA-Z0-9]\\)[ \t]*\\([;#)}`|&]\\|$\\)" 350 "\\([/~a-zA-Z0-9:]\\)[ \t]*\\([;#)}`|&]\\|$\\)"
357 "*Regexp to determine the end of a shell command. 351 "*Regexp to determine the end of a shell command.
358The actual command ends at the end of the first \\(grouping\\).") 352The actual command ends at the end of the first \\(grouping\\).")
359 353
@@ -369,28 +363,24 @@ The actual command ends at the end of the first \\(grouping\\).")
369 363
370 364
371(defvar sh-builtins 365(defvar sh-builtins
372 '((bash eval sh-append sh 366 '((bash eval sh-append posix
373 "alias" "bg" "bind" "builtin" "bye" "command" "declare" "dirs" 367 "alias" "bg" "bind" "builtin" "declare" "dirs" "enable" "fc" "fg"
374 "enable" "fc" "fg" "function" "help" "history" "jobs" "kill" "let" 368 "help" "history" "jobs" "kill" "let" "local" "popd" "pushd" "source"
375 "local" "logout" "popd" "pushd" "source" "suspend" "typeset" 369 "suspend" "typeset" "unalias")
376 "unalias")
377 370
378 ;; The next entry is only used for defining the others 371 ;; The next entry is only used for defining the others
379 (bourne eval sh-append shell 372 (bourne eval sh-append shell
380 "do" "done" "elif" "esac" "export" "fi" "for" "getopts" "in" 373 "eval" "export" "getopts" "newgrp" "pwd" "read" "readonly"
381 "newgrp" "pwd" "read" "readonly" "return" "times" "trap" "ulimit" 374 "times" "ulimit")
382 "until")
383 375
384 (csh eval sh-append shell 376 (csh eval sh-append shell
385 "alias" "breaksw" "chdir" "default:" "end" "endif" "endsw" "foreach" 377 "alias" "chdir" "glob" "history" "limit" "nice" "nohup" "rehash"
386 "glob" "goto" "history" "limit" "logout" "nice" "nohup" "onintr" 378 "setenv" "source" "time" "unalias" "unhash")
387 "rehash" "repeat" "setenv" "source" "switch" "time" "unalias" 379
388 "unhash") 380 (dtksh eval identity wksh)
389 381
390 (es "access" "apids" "break" "catch" "cd" "echo" "eval" "exec" "exit" 382 (es "access" "apids" "cd" "echo" "eval" "false" "let" "limit" "local"
391 "false" "fn" "for" "forever" "fork" "if" "let" "limit" "local" 383 "newpgrp" "result" "time" "umask" "var" "vars" "wait" "whatis")
392 "newpgrp" "result" "return" "throw" "time" "true" "umask"
393 "unwind-protect" "var" "vars" "wait" "whatis" "while")
394 384
395 (jsh eval sh-append sh 385 (jsh eval sh-append sh
396 "bg" "fg" "jobs" "kill" "stop" "suspend") 386 "bg" "fg" "jobs" "kill" "stop" "suspend")
@@ -399,8 +389,8 @@ The actual command ends at the end of the first \\(grouping\\).")
399 "bg" "fg" "jobs" "kill" "notify" "stop" "suspend") 389 "bg" "fg" "jobs" "kill" "notify" "stop" "suspend")
400 390
401 (ksh88 eval sh-append bourne 391 (ksh88 eval sh-append bourne
402 "alias" "bg" "false" "fc" "fg" "function" "jobs" "kill" "let" 392 "alias" "bg" "false" "fc" "fg" "jobs" "kill" "let" "print" "time"
403 "print" "select" "time" "typeset" "unalias" "whence") 393 "typeset" "unalias" "whence")
404 394
405 (oash eval sh-append sh 395 (oash eval sh-append sh
406 "checkwin" "dateline" "error" "form" "menu" "newwin" "oadeinit" 396 "checkwin" "dateline" "error" "form" "menu" "newwin" "oadeinit"
@@ -414,55 +404,72 @@ The actual command ends at the end of the first \\(grouping\\).")
414 (posix eval sh-append sh 404 (posix eval sh-append sh
415 "command") 405 "command")
416 406
417 (rc "break" "builtin" "case" "cd" "echo" "else" "eval" "exec" "exit" "fn" 407 (rc "builtin" "cd" "echo" "eval" "limit" "newpgrp" "shift" "umask" "wait"
418 "for" "if" "in" "limit" "newpgrp" "return" "shift" "switch" "umask" 408 "whatis")
419 "wait" "whatis" "while")
420 409
421 (sh eval sh-append bourne 410 (sh eval sh-append bourne
422 "hash" "test" "type") 411 "hash" "test" "type")
423 412
424 ;; The next entry is only used for defining the others 413 ;; The next entry is only used for defining the others
425 (shell "break" "case" "cd" "continue" "echo" "else" "eval" "exec" "exit" 414 (shell "cd" "echo" "eval" "set" "shift" "umask" "unset" "wait")
426 "if" "set" "shift" "then" "umask" "unset" "wait" "while") 415
416 (wksh eval sh-append ksh88
417 "Xt[A-Z][A-Za-z]*")
427 418
428 (zsh eval sh-append ksh88 419 (zsh eval sh-append ksh88
429 "autoload" "bindkey" "builtin" "bye" "chdir" "compctl" "declare" 420 "autoload" "bindkey" "builtin" "chdir" "compctl" "declare" "dirs"
430 "dirs" "disable" "disown" "echotc" "enable" "functions" "getln" 421 "disable" "disown" "echotc" "enable" "functions" "getln" "hash"
431 "hash" "history" "integer" "limit" "local" "log" "logout" "popd" 422 "history" "integer" "limit" "local" "log" "popd" "pushd" "r"
432 "pushd" "r" "readonly" "rehash" "sched" "setopt" "source" "suspend" 423 "readonly" "rehash" "sched" "setopt" "source" "suspend" "true"
433 "true" "ttyctl" "type" "unfunction" "unhash" "unlimit" "unsetopt" 424 "ttyctl" "type" "unfunction" "unhash" "unlimit" "unsetopt" "vared"
434 "vared" "which")) 425 "which"))
435 "*List of all shell builtins for completing read and fontification. 426 "*List of all shell builtins for completing read and fontification.
436Note that on some systems not all builtins are available or some are 427Note that on some systems not all builtins are available or some are
437implemented as aliases. See `sh-feature'.") 428implemented as aliases. See `sh-feature'.")
438 429
439 430
431
440(defvar sh-leading-keywords 432(defvar sh-leading-keywords
441 '((bash eval sh-append sh 433 '((csh "else")
442 "builtin" "command" "enable") 434
435 (es "true" "unwind-protect" "whatis")
436
437 (rc "else")
438
439 (sh "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
440 "*List of keywords that may be immediately followed by a builtin or keyword.
441Given some confusion between keywords and builtins depending on shell and
442system, the distinction here has been based on whether they influence the
443flow of control or syntax. See `sh-feature'.")
444
445
446(defvar sh-other-keywords
447 '((bash eval sh-append bourne
448 "bye" "logout")
443 449
444 ;; The next entry is only used for defining the others 450 ;; The next entry is only used for defining the others
445 (bourne "do" "elif" "else" "eval" "if" "then" "trap" "until" "while") 451 (bourne eval sh-append shell
452 "done" "esac" "fi" "for" "function" "in" "return")
446 453
447 (csh "else") 454 (csh eval sh-append shell
455 "breaksw" "default" "end" "endif" "endsw" "foreach" "goto"
456 "if" "logout" "onintr" "repeat" "switch" "then" "while")
448 457
449 (es "eval" "time" "true" "umask" 458 (es "break" "catch" "exec" "exit" "fn" "for" "forever" "fork" "if"
450 "unwind-protect" "whatis") 459 "return" "throw" "while")
451 460
452 (ksh88 eval sh-append bourne 461 (ksh88 eval sh-append bourne
453 "time" "whence") 462 "select")
454 463
455 (posix eval sh-append sh 464 (rc "break" "case" "exec" "exit" "fn" "for" "if" "in" "return" "switch"
456 "command") 465 "while")
457
458 (rc "builtin" "else" "eval" "whatis")
459 466
460 (sh eval sh-append bourne 467 ;; The next entry is only used for defining the others
461 "type") 468 (shell "break" "case" "continue" "exec" "exit")
462 469
463 (zsh eval sh-append ksh88 470 (zsh eval sh-append bash
464 "builtin" "disable" "enable" "type" "unhash" "which")) 471 "select"))
465 "*List of keywords that may be immediately followed by a command(-name). 472 "*List of keywords not in `sh-leading-keywords'.
466See `sh-feature'.") 473See `sh-feature'.")
467 474
468 475
@@ -540,31 +547,30 @@ See `sh-feature'.")
540 '("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1 547 '("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
541 font-lock-variable-name-face)) 548 font-lock-variable-name-face))
542 549
543 (dtksh eval identity wksh)
544
545 (es eval sh-append executable-font-lock-keywords 550 (es eval sh-append executable-font-lock-keywords
546 '("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1 551 '("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
547 font-lock-variable-name-face)) 552 font-lock-variable-name-face))
548 553
549 (rc eval sh-append es 554 (rc eval identity es)
550 '("\\(^\\|[ \t]\\)\\(else\\( if\\)?\\)\\>" 2
551 font-lock-keyword-face t))
552 555
553 (sh eval sh-append shell 556 (sh eval sh-append shell
554 '("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2 557 '("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
555 font-lock-variable-name-face) 558 font-lock-variable-name-face))
556 " in\\([ \t]\\|$\\)")
557 559
558 ;; The next entry is only used for defining the others 560 ;; The next entry is only used for defining the others
559 (shell eval sh-append executable-font-lock-keywords 561 (shell eval sh-append executable-font-lock-keywords
560 '("\\\\." 0 font-lock-string-face) 562 '("\\\\." 0 font-lock-string-face)
561 '("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1 563 '("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
562 font-lock-variable-name-face)) 564 font-lock-variable-name-face)))
563
564 (wksh eval sh-append ksh88
565 '("\\(^\\|[^-._A-Za-z0-9]\\)\\(Xt[A-Z][A-Za-z]*\\)\\($\\|[^-._A-Za-z0-9]\\)" 2 font-lock-keyword-face)))
566 "*Rules for highlighting shell scripts. See `sh-feature'.") 565 "*Rules for highlighting shell scripts. See `sh-feature'.")
567 566
567(defvar sh-font-lock-keywords-1
568 '((sh "[ \t]in[ \t]"))
569 "*Additional rules for highlighting shell scripts. See `sh-feature'.")
570
571(defvar sh-font-lock-keywords-2 ()
572 "*Yet more rules for highlighting shell scripts. See `sh-feature'.")
573
568 574
569;; mode-command and utility functions 575;; mode-command and utility functions
570 576
@@ -618,6 +624,7 @@ with your script for an edit-interpret-debug cycle."
618 (use-local-map sh-mode-map) 624 (use-local-map sh-mode-map)
619 (make-local-variable 'indent-line-function) 625 (make-local-variable 'indent-line-function)
620 (make-local-variable 'indent-region-function) 626 (make-local-variable 'indent-region-function)
627 (make-local-variable 'skeleton-end-hook)
621 (make-local-variable 'paragraph-start) 628 (make-local-variable 'paragraph-start)
622 (make-local-variable 'paragraph-separate) 629 (make-local-variable 'paragraph-separate)
623 (make-local-variable 'comment-start) 630 (make-local-variable 'comment-start)
@@ -628,10 +635,10 @@ with your script for an edit-interpret-debug cycle."
628 (make-local-variable 'sh-shell) 635 (make-local-variable 'sh-shell)
629 (make-local-variable 'skeleton-pair-alist) 636 (make-local-variable 'skeleton-pair-alist)
630 (make-local-variable 'skeleton-pair-filter) 637 (make-local-variable 'skeleton-pair-filter)
631 (make-local-variable 'font-lock-keywords)
632 (make-local-variable 'comint-dynamic-complete-functions) 638 (make-local-variable 'comint-dynamic-complete-functions)
633 (make-local-variable 'comint-prompt-regexp) 639 (make-local-variable 'comint-prompt-regexp)
634 (make-local-variable 'font-lock-keywords-case-fold-search) 640 (make-local-variable 'font-lock-keywords)
641 (make-local-variable 'font-lock-defaults)
635 (make-local-variable 'skeleton-filter) 642 (make-local-variable 'skeleton-filter)
636 (make-local-variable 'skeleton-newline-indent-rigidly) 643 (make-local-variable 'skeleton-newline-indent-rigidly)
637 (make-local-variable 'process-environment) 644 (make-local-variable 'process-environment)
@@ -640,14 +647,27 @@ with your script for an edit-interpret-debug cycle."
640 indent-line-function 'sh-indent-line 647 indent-line-function 'sh-indent-line
641 ;; not very clever, but enables wrapping skeletons around regions 648 ;; not very clever, but enables wrapping skeletons around regions
642 indent-region-function (lambda (b e) 649 indent-region-function (lambda (b e)
643 (indent-rigidly b e sh-indentation)) 650 (save-excursion
651 (goto-char b)
652 (skip-syntax-backward "-")
653 (setq b (point))
654 (goto-char e)
655 (skip-syntax-backward "-")
656 (indent-rigidly b (point) sh-indentation)))
657 skeleton-end-hook (lambda ()
658 (or (eolp) (newline) (indent-relative)))
644 paragraph-start "^$\\|^ " 659 paragraph-start "^$\\|^ "
645 paragraph-separate paragraph-start 660 paragraph-separate paragraph-start
646 comment-start "# " 661 comment-start "# "
647 font-lock-keywords-case-fold-search nil
648 comint-dynamic-complete-functions sh-dynamic-complete-functions 662 comint-dynamic-complete-functions sh-dynamic-complete-functions
649 ;; we can't look if previous line ended with `\' 663 ;; we can't look if previous line ended with `\'
650 comint-prompt-regexp "^[ \t]*" 664 comint-prompt-regexp "^[ \t]*"
665 font-lock-defaults
666 '((sh-font-lock-keywords
667 sh-font-lock-keywords-1
668 sh-font-lock-keywords-2)
669 nil nil
670 ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")))
651 skeleton-pair-alist '((?` _ ?`)) 671 skeleton-pair-alist '((?` _ ?`))
652 skeleton-pair-filter 'sh-quoted-p 672 skeleton-pair-filter 'sh-quoted-p
653 skeleton-further-elements '((< '(- (min sh-indentation 673 skeleton-further-elements '((< '(- (min sh-indentation
@@ -659,19 +679,61 @@ with your script for an edit-interpret-debug cycle."
659 (goto-char (point-min)) 679 (goto-char (point-min))
660 (sh-set-shell 680 (sh-set-shell
661 (if (looking-at "#![\t ]*\\([^\t\n ]+\\)") 681 (if (looking-at "#![\t ]*\\([^\t\n ]+\\)")
662 (buffer-substring (match-beginning 1) (match-end 1)) 682 (match-string 1)
663 sh-shell-path)) 683 sh-shell-path))
664 (run-hooks 'sh-mode-hook)) 684 (run-hooks 'sh-mode-hook))
665;;;###autoload 685;;;###autoload
666(defalias 'shell-script-mode 'sh-mode) 686(defalias 'shell-script-mode 'sh-mode)
667 687
668 688
689(defun sh-font-lock-keywords (&optional keywords)
690 "Function to get simple fontification based on `sh-font-lock-keywords'.
691This adds rules for comments and assignments."
692 (sh-feature sh-font-lock-keywords
693 (lambda (list)
694 `((,(concat (sh-feature sh-comment-prefix) "\\(#.*\\)")
695 2 font-lock-comment-face t)
696 (,(sh-feature sh-assignment-regexp)
697 1 font-lock-variable-name-face)
698 ,@keywords
699 ,@list))))
700
701(defun sh-font-lock-keywords-1 (&optional builtins)
702 "Function to get better fontification including keywords."
703 (let ((keywords (concat "\\([;(){}`|&]\\|^\\)[ \t]*\\(\\(\\("
704 (mapconcat 'identity
705 (sh-feature sh-leading-keywords)
706 "\\|")
707 "\\)[ \t]+\\)?\\("
708 (mapconcat 'identity
709 (append (sh-feature sh-leading-keywords)
710 (sh-feature sh-other-keywords))
711 "\\|")
712 "\\)")))
713 (sh-font-lock-keywords
714 `(,@(if builtins
715 `((,(concat keywords "[ \t]+\\)?\\("
716 (mapconcat 'identity (sh-feature sh-builtins) "\\|")
717 "\\)\\>")
718 (2 font-lock-keyword-face nil t)
719 (6 font-lock-function-name-face))
720 ,@(sh-feature sh-font-lock-keywords-2)))
721 (,(concat keywords "\\)\\>")
722 2 font-lock-keyword-face)
723 ,@(sh-feature sh-font-lock-keywords-1)))))
724
725(defun sh-font-lock-keywords-2 ()
726 "Function to get better fontification including keywords and builtins."
727 (sh-font-lock-keywords-1 t))
728
669 729
670(defun sh-set-shell (shell) 730(defun sh-set-shell (shell)
671 "Set this buffer's shell to SHELL (a string). 731 "Set this buffer's shell to SHELL (a string).
672Makes this script executable via `executable-set-magic'. 732Makes this script executable via `executable-set-magic'.
673Calls the value of `sh-set-shell-hook' if set." 733Calls the value of `sh-set-shell-hook' if set."
674 (interactive (list (completing-read "Name or path of shell: " sh-shells))) 734 (interactive (list (completing-read "Name or path of shell: "
735 interpreter-mode-alist
736 (lambda (x) (eq (cdr x) 'sh-mode)))))
675 (if (eq this-command 'sh-set-shell) 737 (if (eq this-command 'sh-set-shell)
676 ;; prevent querying 738 ;; prevent querying
677 (setq this-command 'executable-set-magic)) 739 (setq this-command 'executable-set-magic))
@@ -681,32 +743,13 @@ Calls the value of `sh-set-shell-hook' if set."
681 sh-shell-path (executable-set-magic shell (sh-feature sh-shell-arg)) 743 sh-shell-path (executable-set-magic shell (sh-feature sh-shell-arg))
682 local-abbrev-table (sh-feature sh-abbrevs) 744 local-abbrev-table (sh-feature sh-abbrevs)
683 require-final-newline (sh-feature sh-require-final-newline) 745 require-final-newline (sh-feature sh-require-final-newline)
684 font-lock-keywords 746 font-lock-keywords nil ; force resetting
685 (sh-feature
686 sh-font-lock-keywords
687 (lambda (list)
688 `((,(concat (sh-feature sh-comment-prefix) "\\(#.*\\)")
689 2 font-lock-comment-face t)
690 (,(sh-feature sh-assignment-regexp)
691 1 font-lock-variable-name-face)
692 ,@(if font-lock-maximum-decoration
693 `((,(concat "\\(^\\|[|&;()`!]\\)[ \t]*\\(\\(\\("
694 (mapconcat 'identity
695 (sh-feature sh-leading-keywords)
696 "\\|")
697 "\\)[ \t]+\\)?\\("
698 (mapconcat 'identity
699 (sh-feature sh-builtins)
700 "\\|")
701 "\\)\\)\\($\\|[ \t|&;()]\\)")
702 2 font-lock-keyword-face 'keep)
703 ,@list)
704 list))))
705 comment-start-skip (concat (sh-feature sh-comment-prefix) "#+[\t ]*") 747 comment-start-skip (concat (sh-feature sh-comment-prefix) "#+[\t ]*")
706 mode-line-process (format "[%s]" sh-shell) 748 mode-line-process (format "[%s]" sh-shell)
707 process-environment (default-value 'process-environment) 749 process-environment (default-value 'process-environment)
708 shell (sh-feature sh-variables)) 750 shell (sh-feature sh-variables))
709 (set-syntax-table (sh-feature sh-mode-syntax-table)) 751 (set-syntax-table (sh-feature sh-mode-syntax-table))
752 (setq font-lock-syntax-table)
710 (save-excursion 753 (save-excursion
711 (while (search-forward "=" nil t) 754 (while (search-forward "=" nil t)
712 (sh-assignment 0))) 755 (sh-assignment 0)))
@@ -941,6 +984,7 @@ region, clear header."
941 > _ \n 984 > _ \n
942 resume: 985 resume:
943 < < "esac")) 986 < < "esac"))
987(put 'sh-case 'menu-enable '(sh-feature sh-case))
944 988
945 989
946 990
@@ -1159,15 +1203,15 @@ region, clear header."
1159(define-skeleton sh-while 1203(define-skeleton sh-while
1160 "Insert a while loop. See `sh-feature'." 1204 "Insert a while loop. See `sh-feature'."
1161 (csh eval sh-modify sh 1205 (csh eval sh-modify sh
1162 1 "while( " 1206 2 "while( "
1163 3 " )" 1207 4 " )"
1164 9 "end") 1208 10 "end")
1165 (es eval sh-modify rc 1209 (es eval sh-modify rc
1166 1 "while { " 1210 2 "while { "
1167 3 " } {") 1211 4 " } {")
1168 (rc eval sh-modify csh 1212 (rc eval sh-modify csh
1169 3 " ) {" 1213 4 " ) {"
1170 9 ?}) 1214 10 ?})
1171 (sh "condition: " 1215 (sh "condition: "
1172 '(setq input (sh-feature sh-test)) 1216 '(setq input (sh-feature sh-test))
1173 "while " str "; do" \n 1217 "while " str "; do" \n
@@ -1252,7 +1296,7 @@ option followed by a colon `:' if the option accepts an argument."
1252 (prog1 (point) 1296 (prog1 (point)
1253 (beginning-of-line 1)) 1297 (beginning-of-line 1))
1254 t) 1298 t)
1255 (buffer-substring (match-beginning 1) (match-end 1))))))) 1299 (match-string 1))))))
1256 1300
1257 1301
1258 1302