Commit 84bfbb44 authored by Karl Heuer's avatar Karl Heuer
Browse files

(sh-shells): Eliminated variable redundant with

`interpreter-mode-alist'.
(sh-beginning-of-command): Take into account \quoted newline.
(sh-builtins, sh-leading-keywords, sh-other-keywords): Now three
distinct sets for font-locking.
(sh-font-lock-keywords-1, sh-font-lock-keywords-2): New variables
and functions to give the user finer control over how much to fontify,
including new distinction between keywords and builtins.
(sh-mode): Use new features of `font-lock-defaults' and adaptation
to skeleton changes.
(sh-while): Fix csh, es & rc skeletons.
parent da6a884f
...@@ -138,12 +138,6 @@ Use this where the name of the executable doesn't correspond to the type of ...@@ -138,12 +138,6 @@ Use this where the name of the executable doesn't correspond to the type of
shell it really is.") shell it really is.")
(defvar sh-shells
'(("ash") ("bash") ("csh") ("dtksh") ("es") ("itcsh") ("jsh") ("ksh")
("oash") ("pdksh") ("rc") ("sh") ("tcsh") ("wksh") ("wsh") ("zsh"))
"*Alist of shells available for completing read in `sh-set-shell'.")
(defvar sh-shell-path (or (getenv "SHELL") "/bin/sh") (defvar sh-shell-path (or (getenv "SHELL") "/bin/sh")
"*The executable of the shell being programmed.") "*The executable of the shell being programmed.")
...@@ -347,13 +341,13 @@ That command is also used for setting this variable.") ...@@ -347,13 +341,13 @@ That command is also used for setting this variable.")
(defvar sh-beginning-of-command (defvar sh-beginning-of-command
"\\([;({`|&]\\|^\\)[ \t]*\\([/~:a-zA-Z0-9]\\)" "\\([;({`|&]\\|\\`\\|[^\\]\n\\)[ \t]*\\([/~a-zA-Z0-9:]\\)"
"*Regexp to determine the beginning of a shell command. "*Regexp to determine the beginning of a shell command.
The actual command starts at the beginning of the second \\(grouping\\).") The actual command starts at the beginning of the second \\(grouping\\).")
(defvar sh-end-of-command (defvar sh-end-of-command
"\\([/~:a-zA-Z0-9]\\)[ \t]*\\([;#)}`|&]\\|$\\)" "\\([/~a-zA-Z0-9:]\\)[ \t]*\\([;#)}`|&]\\|$\\)"
"*Regexp to determine the end of a shell command. "*Regexp to determine the end of a shell command.
The actual command ends at the end of the first \\(grouping\\).") The actual command ends at the end of the first \\(grouping\\).")
...@@ -369,28 +363,24 @@ The actual command ends at the end of the first \\(grouping\\).") ...@@ -369,28 +363,24 @@ The actual command ends at the end of the first \\(grouping\\).")
(defvar sh-builtins (defvar sh-builtins
'((bash eval sh-append sh '((bash eval sh-append posix
"alias" "bg" "bind" "builtin" "bye" "command" "declare" "dirs" "alias" "bg" "bind" "builtin" "declare" "dirs" "enable" "fc" "fg"
"enable" "fc" "fg" "function" "help" "history" "jobs" "kill" "let" "help" "history" "jobs" "kill" "let" "local" "popd" "pushd" "source"
"local" "logout" "popd" "pushd" "source" "suspend" "typeset" "suspend" "typeset" "unalias")
"unalias")
;; The next entry is only used for defining the others ;; The next entry is only used for defining the others
(bourne eval sh-append shell (bourne eval sh-append shell
"do" "done" "elif" "esac" "export" "fi" "for" "getopts" "in" "eval" "export" "getopts" "newgrp" "pwd" "read" "readonly"
"newgrp" "pwd" "read" "readonly" "return" "times" "trap" "ulimit" "times" "ulimit")
"until")
(csh eval sh-append shell (csh eval sh-append shell
"alias" "breaksw" "chdir" "default:" "end" "endif" "endsw" "foreach" "alias" "chdir" "glob" "history" "limit" "nice" "nohup" "rehash"
"glob" "goto" "history" "limit" "logout" "nice" "nohup" "onintr" "setenv" "source" "time" "unalias" "unhash")
"rehash" "repeat" "setenv" "source" "switch" "time" "unalias"
"unhash") (dtksh eval identity wksh)
(es "access" "apids" "break" "catch" "cd" "echo" "eval" "exec" "exit" (es "access" "apids" "cd" "echo" "eval" "false" "let" "limit" "local"
"false" "fn" "for" "forever" "fork" "if" "let" "limit" "local" "newpgrp" "result" "time" "umask" "var" "vars" "wait" "whatis")
"newpgrp" "result" "return" "throw" "time" "true" "umask"
"unwind-protect" "var" "vars" "wait" "whatis" "while")
(jsh eval sh-append sh (jsh eval sh-append sh
"bg" "fg" "jobs" "kill" "stop" "suspend") "bg" "fg" "jobs" "kill" "stop" "suspend")
...@@ -399,8 +389,8 @@ The actual command ends at the end of the first \\(grouping\\).") ...@@ -399,8 +389,8 @@ The actual command ends at the end of the first \\(grouping\\).")
"bg" "fg" "jobs" "kill" "notify" "stop" "suspend") "bg" "fg" "jobs" "kill" "notify" "stop" "suspend")
(ksh88 eval sh-append bourne (ksh88 eval sh-append bourne
"alias" "bg" "false" "fc" "fg" "function" "jobs" "kill" "let" "alias" "bg" "false" "fc" "fg" "jobs" "kill" "let" "print" "time"
"print" "select" "time" "typeset" "unalias" "whence") "typeset" "unalias" "whence")
(oash eval sh-append sh (oash eval sh-append sh
"checkwin" "dateline" "error" "form" "menu" "newwin" "oadeinit" "checkwin" "dateline" "error" "form" "menu" "newwin" "oadeinit"
...@@ -414,55 +404,72 @@ The actual command ends at the end of the first \\(grouping\\).") ...@@ -414,55 +404,72 @@ The actual command ends at the end of the first \\(grouping\\).")
(posix eval sh-append sh (posix eval sh-append sh
"command") "command")
(rc "break" "builtin" "case" "cd" "echo" "else" "eval" "exec" "exit" "fn" (rc "builtin" "cd" "echo" "eval" "limit" "newpgrp" "shift" "umask" "wait"
"for" "if" "in" "limit" "newpgrp" "return" "shift" "switch" "umask" "whatis")
"wait" "whatis" "while")
(sh eval sh-append bourne (sh eval sh-append bourne
"hash" "test" "type") "hash" "test" "type")
;; The next entry is only used for defining the others ;; The next entry is only used for defining the others
(shell "break" "case" "cd" "continue" "echo" "else" "eval" "exec" "exit" (shell "cd" "echo" "eval" "set" "shift" "umask" "unset" "wait")
"if" "set" "shift" "then" "umask" "unset" "wait" "while")
(wksh eval sh-append ksh88
"Xt[A-Z][A-Za-z]*")
(zsh eval sh-append ksh88 (zsh eval sh-append ksh88
"autoload" "bindkey" "builtin" "bye" "chdir" "compctl" "declare" "autoload" "bindkey" "builtin" "chdir" "compctl" "declare" "dirs"
"dirs" "disable" "disown" "echotc" "enable" "functions" "getln" "disable" "disown" "echotc" "enable" "functions" "getln" "hash"
"hash" "history" "integer" "limit" "local" "log" "logout" "popd" "history" "integer" "limit" "local" "log" "popd" "pushd" "r"
"pushd" "r" "readonly" "rehash" "sched" "setopt" "source" "suspend" "readonly" "rehash" "sched" "setopt" "source" "suspend" "true"
"true" "ttyctl" "type" "unfunction" "unhash" "unlimit" "unsetopt" "ttyctl" "type" "unfunction" "unhash" "unlimit" "unsetopt" "vared"
"vared" "which")) "which"))
"*List of all shell builtins for completing read and fontification. "*List of all shell builtins for completing read and fontification.
Note that on some systems not all builtins are available or some are Note that on some systems not all builtins are available or some are
implemented as aliases. See `sh-feature'.") implemented as aliases. See `sh-feature'.")
(defvar sh-leading-keywords (defvar sh-leading-keywords
'((bash eval sh-append sh '((csh "else")
"builtin" "command" "enable")
(es "true" "unwind-protect" "whatis")
(rc "else")
(sh "do" "elif" "else" "if" "then" "trap" "type" "until" "while"))
"*List of keywords that may be immediately followed by a builtin or keyword.
Given some confusion between keywords and builtins depending on shell and
system, the distinction here has been based on whether they influence the
flow of control or syntax. See `sh-feature'.")
(defvar sh-other-keywords
'((bash eval sh-append bourne
"bye" "logout")
;; The next entry is only used for defining the others ;; The next entry is only used for defining the others
(bourne "do" "elif" "else" "eval" "if" "then" "trap" "until" "while") (bourne eval sh-append shell
"done" "esac" "fi" "for" "function" "in" "return")
(csh "else") (csh eval sh-append shell
"breaksw" "default" "end" "endif" "endsw" "foreach" "goto"
"if" "logout" "onintr" "repeat" "switch" "then" "while")
(es "eval" "time" "true" "umask" (es "break" "catch" "exec" "exit" "fn" "for" "forever" "fork" "if"
"unwind-protect" "whatis") "return" "throw" "while")
(ksh88 eval sh-append bourne (ksh88 eval sh-append bourne
"time" "whence") "select")
(posix eval sh-append sh (rc "break" "case" "exec" "exit" "fn" "for" "if" "in" "return" "switch"
"command") "while")
(rc "builtin" "else" "eval" "whatis")
(sh eval sh-append bourne ;; The next entry is only used for defining the others
"type") (shell "break" "case" "continue" "exec" "exit")
(zsh eval sh-append ksh88 (zsh eval sh-append bash
"builtin" "disable" "enable" "type" "unhash" "which")) "select"))
"*List of keywords that may be immediately followed by a command(-name). "*List of keywords not in `sh-leading-keywords'.
See `sh-feature'.") See `sh-feature'.")
...@@ -540,31 +547,30 @@ See `sh-feature'.") ...@@ -540,31 +547,30 @@ See `sh-feature'.")
'("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1 '("\\${?[#?]?\\([A-Za-z_][A-Za-z0-9_]*\\|0\\)" 1
font-lock-variable-name-face)) font-lock-variable-name-face))
(dtksh eval identity wksh)
(es eval sh-append executable-font-lock-keywords (es eval sh-append executable-font-lock-keywords
'("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1 '("\\$#?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\)" 1
font-lock-variable-name-face)) font-lock-variable-name-face))
(rc eval sh-append es (rc eval identity es)
'("\\(^\\|[ \t]\\)\\(else\\( if\\)?\\)\\>" 2
font-lock-keyword-face t))
(sh eval sh-append shell (sh eval sh-append shell
'("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2 '("\\$\\({#?\\)?\\([A-Za-z_][A-Za-z0-9_]*\\|[-#?@!]\\)" 2
font-lock-variable-name-face) font-lock-variable-name-face))
" in\\([ \t]\\|$\\)")
;; The next entry is only used for defining the others ;; The next entry is only used for defining the others
(shell eval sh-append executable-font-lock-keywords (shell eval sh-append executable-font-lock-keywords
'("\\\\." 0 font-lock-string-face) '("\\\\." 0 font-lock-string-face)
'("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1 '("\\${?\\([A-Za-z_][A-Za-z0-9_]*\\|[0-9]+\\|[$*_]\\)" 1
font-lock-variable-name-face)) font-lock-variable-name-face)))
(wksh eval sh-append ksh88
'("\\(^\\|[^-._A-Za-z0-9]\\)\\(Xt[A-Z][A-Za-z]*\\)\\($\\|[^-._A-Za-z0-9]\\)" 2 font-lock-keyword-face)))
"*Rules for highlighting shell scripts. See `sh-feature'.") "*Rules for highlighting shell scripts. See `sh-feature'.")
(defvar sh-font-lock-keywords-1
'((sh "[ \t]in[ \t]"))
"*Additional rules for highlighting shell scripts. See `sh-feature'.")
(defvar sh-font-lock-keywords-2 ()
"*Yet more rules for highlighting shell scripts. See `sh-feature'.")
;; mode-command and utility functions ;; mode-command and utility functions
...@@ -618,6 +624,7 @@ with your script for an edit-interpret-debug cycle." ...@@ -618,6 +624,7 @@ with your script for an edit-interpret-debug cycle."
(use-local-map sh-mode-map) (use-local-map sh-mode-map)
(make-local-variable 'indent-line-function) (make-local-variable 'indent-line-function)
(make-local-variable 'indent-region-function) (make-local-variable 'indent-region-function)
(make-local-variable 'skeleton-end-hook)
(make-local-variable 'paragraph-start) (make-local-variable 'paragraph-start)
(make-local-variable 'paragraph-separate) (make-local-variable 'paragraph-separate)
(make-local-variable 'comment-start) (make-local-variable 'comment-start)
...@@ -628,10 +635,10 @@ with your script for an edit-interpret-debug cycle." ...@@ -628,10 +635,10 @@ with your script for an edit-interpret-debug cycle."
(make-local-variable 'sh-shell) (make-local-variable 'sh-shell)
(make-local-variable 'skeleton-pair-alist) (make-local-variable 'skeleton-pair-alist)
(make-local-variable 'skeleton-pair-filter) (make-local-variable 'skeleton-pair-filter)
(make-local-variable 'font-lock-keywords)
(make-local-variable 'comint-dynamic-complete-functions) (make-local-variable 'comint-dynamic-complete-functions)
(make-local-variable 'comint-prompt-regexp) (make-local-variable 'comint-prompt-regexp)
(make-local-variable 'font-lock-keywords-case-fold-search) (make-local-variable 'font-lock-keywords)
(make-local-variable 'font-lock-defaults)
(make-local-variable 'skeleton-filter) (make-local-variable 'skeleton-filter)
(make-local-variable 'skeleton-newline-indent-rigidly) (make-local-variable 'skeleton-newline-indent-rigidly)
(make-local-variable 'process-environment) (make-local-variable 'process-environment)
...@@ -640,14 +647,27 @@ with your script for an edit-interpret-debug cycle." ...@@ -640,14 +647,27 @@ with your script for an edit-interpret-debug cycle."
indent-line-function 'sh-indent-line indent-line-function 'sh-indent-line
;; not very clever, but enables wrapping skeletons around regions ;; not very clever, but enables wrapping skeletons around regions
indent-region-function (lambda (b e) indent-region-function (lambda (b e)
(indent-rigidly b e sh-indentation)) (save-excursion
(goto-char b)
(skip-syntax-backward "-")
(setq b (point))
(goto-char e)
(skip-syntax-backward "-")
(indent-rigidly b (point) sh-indentation)))
skeleton-end-hook (lambda ()
(or (eolp) (newline) (indent-relative)))
paragraph-start "^$\\|^ " paragraph-start "^$\\|^ "
paragraph-separate paragraph-start paragraph-separate paragraph-start
comment-start "# " comment-start "# "
font-lock-keywords-case-fold-search nil
comint-dynamic-complete-functions sh-dynamic-complete-functions comint-dynamic-complete-functions sh-dynamic-complete-functions
;; we can't look if previous line ended with `\' ;; we can't look if previous line ended with `\'
comint-prompt-regexp "^[ \t]*" comint-prompt-regexp "^[ \t]*"
font-lock-defaults
'((sh-font-lock-keywords
sh-font-lock-keywords-1
sh-font-lock-keywords-2)
nil nil
((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")))
skeleton-pair-alist '((?` _ ?`)) skeleton-pair-alist '((?` _ ?`))
skeleton-pair-filter 'sh-quoted-p skeleton-pair-filter 'sh-quoted-p
skeleton-further-elements '((< '(- (min sh-indentation skeleton-further-elements '((< '(- (min sh-indentation
...@@ -659,19 +679,61 @@ with your script for an edit-interpret-debug cycle." ...@@ -659,19 +679,61 @@ with your script for an edit-interpret-debug cycle."
(goto-char (point-min)) (goto-char (point-min))
(sh-set-shell (sh-set-shell
(if (looking-at "#![\t ]*\\([^\t\n ]+\\)") (if (looking-at "#![\t ]*\\([^\t\n ]+\\)")
(buffer-substring (match-beginning 1) (match-end 1)) (match-string 1)
sh-shell-path)) sh-shell-path))
(run-hooks 'sh-mode-hook)) (run-hooks 'sh-mode-hook))
;;;###autoload ;;;###autoload
(defalias 'shell-script-mode 'sh-mode) (defalias 'shell-script-mode 'sh-mode)
(defun sh-font-lock-keywords (&optional keywords)
"Function to get simple fontification based on `sh-font-lock-keywords'.
This adds rules for comments and assignments."
(sh-feature sh-font-lock-keywords
(lambda (list)
`((,(concat (sh-feature sh-comment-prefix) "\\(#.*\\)")
2 font-lock-comment-face t)
(,(sh-feature sh-assignment-regexp)
1 font-lock-variable-name-face)
,@keywords
,@list))))
(defun sh-font-lock-keywords-1 (&optional builtins)
"Function to get better fontification including keywords."
(let ((keywords (concat "\\([;(){}`|&]\\|^\\)[ \t]*\\(\\(\\("
(mapconcat 'identity
(sh-feature sh-leading-keywords)
"\\|")
"\\)[ \t]+\\)?\\("
(mapconcat 'identity
(append (sh-feature sh-leading-keywords)
(sh-feature sh-other-keywords))
"\\|")
"\\)")))
(sh-font-lock-keywords
`(,@(if builtins
`((,(concat keywords "[ \t]+\\)?\\("
(mapconcat 'identity (sh-feature sh-builtins) "\\|")
"\\)\\>")
(2 font-lock-keyword-face nil t)
(6 font-lock-function-name-face))
,@(sh-feature sh-font-lock-keywords-2)))
(,(concat keywords "\\)\\>")
2 font-lock-keyword-face)
,@(sh-feature sh-font-lock-keywords-1)))))
(defun sh-font-lock-keywords-2 ()
"Function to get better fontification including keywords and builtins."
(sh-font-lock-keywords-1 t))
(defun sh-set-shell (shell) (defun sh-set-shell (shell)
"Set this buffer's shell to SHELL (a string). "Set this buffer's shell to SHELL (a string).
Makes this script executable via `executable-set-magic'. Makes this script executable via `executable-set-magic'.
Calls the value of `sh-set-shell-hook' if set." Calls the value of `sh-set-shell-hook' if set."
(interactive (list (completing-read "Name or path of shell: " sh-shells))) (interactive (list (completing-read "Name or path of shell: "
interpreter-mode-alist
(lambda (x) (eq (cdr x) 'sh-mode)))))
(if (eq this-command 'sh-set-shell) (if (eq this-command 'sh-set-shell)
;; prevent querying ;; prevent querying
(setq this-command 'executable-set-magic)) (setq this-command 'executable-set-magic))
...@@ -681,32 +743,13 @@ Calls the value of `sh-set-shell-hook' if set." ...@@ -681,32 +743,13 @@ Calls the value of `sh-set-shell-hook' if set."
sh-shell-path (executable-set-magic shell (sh-feature sh-shell-arg)) sh-shell-path (executable-set-magic shell (sh-feature sh-shell-arg))
local-abbrev-table (sh-feature sh-abbrevs) local-abbrev-table (sh-feature sh-abbrevs)
require-final-newline (sh-feature sh-require-final-newline) require-final-newline (sh-feature sh-require-final-newline)
font-lock-keywords font-lock-keywords nil ; force resetting
(sh-feature
sh-font-lock-keywords
(lambda (list)
`((,(concat (sh-feature sh-comment-prefix) "\\(#.*\\)")
2 font-lock-comment-face t)
(,(sh-feature sh-assignment-regexp)
1 font-lock-variable-name-face)
,@(if font-lock-maximum-decoration
`((,(concat "\\(^\\|[|&;()`!]\\)[ \t]*\\(\\(\\("
(mapconcat 'identity
(sh-feature sh-leading-keywords)
"\\|")
"\\)[ \t]+\\)?\\("
(mapconcat 'identity
(sh-feature sh-builtins)
"\\|")
"\\)\\)\\($\\|[ \t|&;()]\\)")
2 font-lock-keyword-face 'keep)
,@list)
list))))
comment-start-skip (concat (sh-feature sh-comment-prefix) "#+[\t ]*") comment-start-skip (concat (sh-feature sh-comment-prefix) "#+[\t ]*")
mode-line-process (format "[%s]" sh-shell) mode-line-process (format "[%s]" sh-shell)
process-environment (default-value 'process-environment) process-environment (default-value 'process-environment)
shell (sh-feature sh-variables)) shell (sh-feature sh-variables))
(set-syntax-table (sh-feature sh-mode-syntax-table)) (set-syntax-table (sh-feature sh-mode-syntax-table))
(setq font-lock-syntax-table)
(save-excursion (save-excursion
(while (search-forward "=" nil t) (while (search-forward "=" nil t)
(sh-assignment 0))) (sh-assignment 0)))
...@@ -941,6 +984,7 @@ region, clear header." ...@@ -941,6 +984,7 @@ region, clear header."
> _ \n > _ \n
resume: resume:
< < "esac")) < < "esac"))
(put 'sh-case 'menu-enable '(sh-feature sh-case))
...@@ -1159,15 +1203,15 @@ region, clear header." ...@@ -1159,15 +1203,15 @@ region, clear header."
(define-skeleton sh-while (define-skeleton sh-while
"Insert a while loop. See `sh-feature'." "Insert a while loop. See `sh-feature'."
(csh eval sh-modify sh (csh eval sh-modify sh
1 "while( " 2 "while( "
3 " )" 4 " )"
9 "end") 10 "end")
(es eval sh-modify rc (es eval sh-modify rc
1 "while { " 2 "while { "
3 " } {") 4 " } {")
(rc eval sh-modify csh (rc eval sh-modify csh
3 " ) {" 4 " ) {"
9 ?}) 10 ?})
(sh "condition: " (sh "condition: "
'(setq input (sh-feature sh-test)) '(setq input (sh-feature sh-test))
"while " str "; do" \n "while " str "; do" \n
...@@ -1252,7 +1296,7 @@ option followed by a colon `:' if the option accepts an argument." ...@@ -1252,7 +1296,7 @@ option followed by a colon `:' if the option accepts an argument."
(prog1 (point) (prog1 (point)
(beginning-of-line 1)) (beginning-of-line 1))
t) t)
(buffer-substring (match-beginning 1) (match-end 1))))))) (match-string 1))))))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment