diff options
| author | Stefan Monnier | 2000-10-21 18:06:17 +0000 |
|---|---|---|
| committer | Stefan Monnier | 2000-10-21 18:06:17 +0000 |
| commit | 34939e2c4a9633da96e8d2e5bf17a8db516afa76 (patch) | |
| tree | 2c3b535fa7ec692a335f462df69a9c6381d64e1c /lisp/progmodes | |
| parent | f1eed8ff5b6b7b64976b3e27566efa0cd462d934 (diff) | |
| download | emacs-34939e2c4a9633da96e8d2e5bf17a8db516afa76.tar.gz emacs-34939e2c4a9633da96e8d2e5bf17a8db516afa76.zip | |
(sh-mode-map): Remove bindings for
sh-electric-rparen, sh-electric-less and sh-electric-hash.
(sh-st-punc, sh-here-doc-syntax): Use string-to-syntax.
(sh-font-lock-heredoc, sh-font-lock-paren): New funs.
(sh-font-lock-syntactic-keywords): Use them.
(sh-heredoc-face, sh-st-face, sh-special-syntax): Remove.
(sh-mkword-regexp, sh-electric-rparen-needed-here): Remove.
(sh-mode): Don't override font-lock-unfontify-region-function.
Use a copy of sh-font-lock-syntactic-keywords.
(sh-set-shell): Don't set sh-electric-rparen-needed-here.
Don't call sh-scan-buffer since font-lock does it on the fly.
(sh-get-indent-info): Use `face' rather than `syntax-table'
text-property to detect here-documents.
Replace sh-special-syntax with sh-st-punc.
(sh-prev-line): Use `face' rather than `syntax-table'
text-property to skip over here-documents.
(sh-font-lock-unfontify-region-function, sh-check-paren-in-case)
(sh-set-char-syntax, sh-electric-rparen, sh-electric-hash)
(sh-electric-less, sh-set-here-doc-region)
(sh-remove-our-text-properties, sh-search-word, sh-scan-case)
(sh-scan-buffer, sh-rescan-buffer): Remove.
Diffstat (limited to 'lisp/progmodes')
| -rw-r--r-- | lisp/progmodes/sh-script.el | 487 |
1 files changed, 79 insertions, 408 deletions
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index b0c1bd97afc..4c032ec7cd5 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el | |||
| @@ -177,22 +177,6 @@ | |||
| 177 | ;; | 177 | ;; |
| 178 | ;; Bugs | 178 | ;; Bugs |
| 179 | ;; ---- | 179 | ;; ---- |
| 180 | ;; - Here-documents are marked with text properties face and syntax | ||
| 181 | ;; table. This serves 2 purposes: stopping indentation while inside | ||
| 182 | ;; them, and moving over them when finding the previous line to | ||
| 183 | ;; indent to. However, if font-lock mode is active when there is | ||
| 184 | ;; any change inside the here-document font-lock clears that | ||
| 185 | ;; property. This causes several problems: lines after the here-doc | ||
| 186 | ;; will not be re-indented properly, words in the here-doc region | ||
| 187 | ;; may be fontified, and indentation may occur within the | ||
| 188 | ;; here-document. | ||
| 189 | ;; I'm not sure how to fix this, perhaps using the point-entered | ||
| 190 | ;; property. Anyway, if you use font lock and change a | ||
| 191 | ;; here-document, I recommend using M-x sh-rescan-buffer after the | ||
| 192 | ;; changes are made. Similarly, when using highlight-changes-mode, | ||
| 193 | ;; changes inside a here-document may confuse shell indenting, but again | ||
| 194 | ;; using `sh-rescan-buffer' should fix them. | ||
| 195 | ;; | ||
| 196 | ;; - Indenting many lines is slow. It currently does each line | 180 | ;; - Indenting many lines is slow. It currently does each line |
| 197 | ;; independently, rather than saving state information. | 181 | ;; independently, rather than saving state information. |
| 198 | ;; | 182 | ;; |
| @@ -455,9 +439,6 @@ the car and cdr are the same symbol.") | |||
| 455 | (define-key map "'" 'skeleton-pair-insert-maybe) | 439 | (define-key map "'" 'skeleton-pair-insert-maybe) |
| 456 | (define-key map "`" 'skeleton-pair-insert-maybe) | 440 | (define-key map "`" 'skeleton-pair-insert-maybe) |
| 457 | (define-key map "\"" 'skeleton-pair-insert-maybe) | 441 | (define-key map "\"" 'skeleton-pair-insert-maybe) |
| 458 | (define-key map ")" 'sh-electric-rparen) | ||
| 459 | (define-key map "<" 'sh-electric-less) | ||
| 460 | (define-key map "#" 'sh-electric-hash) | ||
| 461 | 442 | ||
| 462 | (substitute-key-definition 'complete-tag 'comint-dynamic-complete | 443 | (substitute-key-definition 'complete-tag 'comint-dynamic-complete |
| 463 | map (current-global-map)) | 444 | map (current-global-map)) |
| @@ -815,6 +796,61 @@ See `sh-feature'.") | |||
| 815 | (defvar sh-font-lock-keywords-2 () | 796 | (defvar sh-font-lock-keywords-2 () |
| 816 | "Gaudy level highlighting for Shell Script modes.") | 797 | "Gaudy level highlighting for Shell Script modes.") |
| 817 | 798 | ||
| 799 | ;; These are used for the syntax table stuff (derived from cperl-mode). | ||
| 800 | ;; Note: parse-sexp-lookup-properties must be set to t for it to work. | ||
| 801 | (defconst sh-st-punc (string-to-syntax ".")) | ||
| 802 | (defconst sh-here-doc-syntax (string-to-syntax "|")) ;; generic string | ||
| 803 | |||
| 804 | (defun sh-font-lock-heredoc (start string quoted) | ||
| 805 | "Determine the syntax of the \\n after a <<HEREDOC." | ||
| 806 | (unless (sh-in-comment-or-string start) | ||
| 807 | ;; We're looking at <<STRING, so we add "^STRING$" to the syntactic | ||
| 808 | ;; font-lock keywords to detect the end of this here document. | ||
| 809 | (let ((ere (concat | ||
| 810 | "^" (if quoted "[ \t]*") | ||
| 811 | (regexp-quote (replace-regexp-in-string "['\"]" "" string)) | ||
| 812 | "\\(\n\\)"))) | ||
| 813 | (unless (assoc ere font-lock-syntactic-keywords) | ||
| 814 | (let* ( ;; A rough regexp that should find us back. | ||
| 815 | (sre (concat "<<\\(-\\)?\\s-*['\"]?" | ||
| 816 | (regexp-quote string) "['\"]?[ \t\n]")) | ||
| 817 | (code `(cond | ||
| 818 | ((save-excursion (re-search-backward ,sre nil t)) | ||
| 819 | ;; This ^STRING$ is indeed following a <<STRING | ||
| 820 | sh-here-doc-syntax) | ||
| 821 | ((not (save-excursion (re-search-forward ,sre nil t))) | ||
| 822 | ;; There's no <<STRING either before or after us, | ||
| 823 | ;; so we should remove this now obsolete entry. | ||
| 824 | (setq font-lock-syntactic-keywords | ||
| 825 | (delq (assoc ,ere font-lock-syntactic-keywords) | ||
| 826 | font-lock-syntactic-keywords)) | ||
| 827 | nil)))) | ||
| 828 | ;; Use destructive update so the new keyword gets used right away. | ||
| 829 | (setq font-lock-syntactic-keywords | ||
| 830 | (nconc font-lock-syntactic-keywords | ||
| 831 | (list (font-lock-compile-keyword `(,ere 1 ,code)))))))) | ||
| 832 | sh-here-doc-syntax)) | ||
| 833 | |||
| 834 | (defun sh-font-lock-paren (start) | ||
| 835 | (save-excursion | ||
| 836 | (goto-char start) | ||
| 837 | ;; Skip through all patterns | ||
| 838 | (while | ||
| 839 | (progn | ||
| 840 | (forward-comment (- (point-max))) | ||
| 841 | ;; Skip through one pattern | ||
| 842 | (while | ||
| 843 | (or (/= 0 (skip-syntax-backward "w_")) | ||
| 844 | (/= 0 (skip-chars-backward "?*/")) | ||
| 845 | (when (memq (char-before) '(?\" ?\')) | ||
| 846 | (condition-case nil (progn (backward-sexp 1) t) | ||
| 847 | (error nil))))) | ||
| 848 | (forward-comment (- (point-max))) | ||
| 849 | (when (eq (char-before) ?|) | ||
| 850 | (backward-char 1) t))) | ||
| 851 | (when (save-excursion (backward-char 2) (looking-at ";;\\|in")) | ||
| 852 | sh-st-punc))) | ||
| 853 | |||
| 818 | (defconst sh-font-lock-syntactic-keywords | 854 | (defconst sh-font-lock-syntactic-keywords |
| 819 | ;; Mark a `#' character as having punctuation syntax in a variable reference. | 855 | ;; Mark a `#' character as having punctuation syntax in a variable reference. |
| 820 | ;; Really we should do this properly. From Chet Ramey and Brian Fox: | 856 | ;; Really we should do this properly. From Chet Ramey and Brian Fox: |
| @@ -824,7 +860,13 @@ See `sh-feature'.") | |||
| 824 | ;; But I can't be bothered to write a function to do it properly and | 860 | ;; But I can't be bothered to write a function to do it properly and |
| 825 | ;; efficiently. So we only do it properly for `#' in variable references and | 861 | ;; efficiently. So we only do it properly for `#' in variable references and |
| 826 | ;; do it efficiently by anchoring the regexp to the left. | 862 | ;; do it efficiently by anchoring the regexp to the left. |
| 827 | '(("\\${?[^}#\n\t ]*\\(##?\\)" 1 (1 . nil)))) | 863 | `(("\\${?[^}#\n\t ]*\\(##?\\)" 1 ,sh-st-punc) |
| 864 | ;; Find HEREDOC starters and add a corresponding rule for the ender. | ||
| 865 | ("[^<>]<<\\(-\\)?\\s-*\\(\\(['\"][^'\"]+['\"]\\|\\sw\\|\\s_\\)+\\).*\\(\n\\)" | ||
| 866 | 4 (sh-font-lock-heredoc | ||
| 867 | (match-beginning 0) (match-string 2) (match-end 1))) | ||
| 868 | ;; Distinguish the special close-paren in `case'. | ||
| 869 | (")" 0 (sh-font-lock-paren (match-beginning 0))))) | ||
| 828 | 870 | ||
| 829 | (defgroup sh-indentation nil | 871 | (defgroup sh-indentation nil |
| 830 | "Variables controlling indentation in shell scripts. | 872 | "Variables controlling indentation in shell scripts. |
| @@ -1051,51 +1093,15 @@ This is for the rc shell." | |||
| 1051 | :type `(choice ,@ sh-number-or-symbol-list) | 1093 | :type `(choice ,@ sh-number-or-symbol-list) |
| 1052 | :group 'sh-indentation) | 1094 | :group 'sh-indentation) |
| 1053 | 1095 | ||
| 1054 | (defface sh-heredoc-face | ||
| 1055 | '((((class color) | ||
| 1056 | (background dark)) | ||
| 1057 | (:foreground "yellow" :bold t)) | ||
| 1058 | (((class color) | ||
| 1059 | (background light)) | ||
| 1060 | (:foreground "tan" )) | ||
| 1061 | (t | ||
| 1062 | (:bold t))) | ||
| 1063 | "Face to show a here-document" | ||
| 1064 | :group 'sh-indentation) | ||
| 1065 | |||
| 1066 | (defface sh-st-face | ||
| 1067 | '((((class color) | ||
| 1068 | (background dark)) | ||
| 1069 | (:foreground "yellow" :bold t)) | ||
| 1070 | (((class color) | ||
| 1071 | (background light)) | ||
| 1072 | (:foreground "tan" )) | ||
| 1073 | (t | ||
| 1074 | (:bold t))) | ||
| 1075 | "Face to show characters with special syntax properties." | ||
| 1076 | :group 'sh-indentation) | ||
| 1077 | |||
| 1078 | 1096 | ||
| 1079 | ;; Internal use - not designed to be changed by the user: | 1097 | ;; Internal use - not designed to be changed by the user: |
| 1080 | 1098 | ||
| 1081 | ;; These are used for the syntax table stuff (derived from cperl-mode). | ||
| 1082 | ;; Note: parse-sexp-lookup-properties must be set to t for it to work. | ||
| 1083 | (defconst sh-here-doc-syntax '(15)) ;; generic string | ||
| 1084 | (defconst sh-st-punc '(1)) | ||
| 1085 | (defconst sh-special-syntax sh-st-punc) | ||
| 1086 | |||
| 1087 | (defun sh-mkword-regexpr (word) | 1099 | (defun sh-mkword-regexpr (word) |
| 1088 | "Make a regexp which matches WORD as a word. | 1100 | "Make a regexp which matches WORD as a word. |
| 1089 | This specifically excludes an occurrence of WORD followed by | 1101 | This specifically excludes an occurrence of WORD followed by |
| 1090 | punctuation characters like '-'." | 1102 | punctuation characters like '-'." |
| 1091 | (concat word "\\([^-a-z0-9_]\\|$\\)")) | 1103 | (concat word "\\([^-a-z0-9_]\\|$\\)")) |
| 1092 | 1104 | ||
| 1093 | (defun sh-mkword-regexp (word) | ||
| 1094 | "Make a regexp which matches WORD as a word. | ||
| 1095 | This specifically excludes an occurrence of WORD followed by | ||
| 1096 | or preceded by punctuation characters like '-'." | ||
| 1097 | (concat "\\(^\\|[^-a-z0-9_]\\)" word "\\([^-a-z0-9_]\\|$\\)")) | ||
| 1098 | |||
| 1099 | (defconst sh-re-done (sh-mkword-regexpr "done")) | 1105 | (defconst sh-re-done (sh-mkword-regexpr "done")) |
| 1100 | 1106 | ||
| 1101 | 1107 | ||
| @@ -1120,9 +1126,6 @@ or preceded by punctuation characters like '-'." | |||
| 1120 | '((sh . t)) | 1126 | '((sh . t)) |
| 1121 | "Non-nil if the shell type needs an electric handling of case alternatives.") | 1127 | "Non-nil if the shell type needs an electric handling of case alternatives.") |
| 1122 | 1128 | ||
| 1123 | (defvar sh-electric-rparen-needed-here nil | ||
| 1124 | "Non-nil if the buffer needs an electric handling of case alternatives.") | ||
| 1125 | |||
| 1126 | (defconst sh-var-list | 1129 | (defconst sh-var-list |
| 1127 | '( | 1130 | '( |
| 1128 | sh-basic-offset sh-first-lines-indent sh-indent-after-case | 1131 | sh-basic-offset sh-first-lines-indent sh-indent-after-case |
| @@ -1257,13 +1260,13 @@ with your script for an edit-interpret-debug cycle." | |||
| 1257 | ;; we can't look if previous line ended with `\' | 1260 | ;; we can't look if previous line ended with `\' |
| 1258 | comint-prompt-regexp "^[ \t]*" | 1261 | comint-prompt-regexp "^[ \t]*" |
| 1259 | font-lock-defaults | 1262 | font-lock-defaults |
| 1260 | '((sh-font-lock-keywords | 1263 | `((sh-font-lock-keywords |
| 1261 | sh-font-lock-keywords-1 sh-font-lock-keywords-2) | 1264 | sh-font-lock-keywords-1 sh-font-lock-keywords-2) |
| 1262 | nil nil | 1265 | nil nil |
| 1263 | ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")) nil | 1266 | ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")) nil |
| 1264 | (font-lock-syntactic-keywords . sh-font-lock-syntactic-keywords)) | 1267 | (font-lock-syntactic-keywords |
| 1265 | font-lock-unfontify-region-function | 1268 | ;; Copy so we can use destructive update in `sh-font-lock-heredoc'. |
| 1266 | 'sh-font-lock-unfontify-region-function | 1269 | . ,(copy-sequence sh-font-lock-syntactic-keywords))) |
| 1267 | skeleton-pair-alist '((?` _ ?`)) | 1270 | skeleton-pair-alist '((?` _ ?`)) |
| 1268 | skeleton-pair-filter 'sh-quoted-p | 1271 | skeleton-pair-filter 'sh-quoted-p |
| 1269 | skeleton-further-elements '((< '(- (min sh-indentation | 1272 | skeleton-further-elements '((< '(- (min sh-indentation |
| @@ -1420,10 +1423,7 @@ Calls the value of `sh-set-shell-hook' if set." | |||
| 1420 | (if (setq sh-indent-supported-here (sh-feature sh-indent-supported)) | 1423 | (if (setq sh-indent-supported-here (sh-feature sh-indent-supported)) |
| 1421 | (progn | 1424 | (progn |
| 1422 | (message "Setting up indent for shell type %s" sh-shell) | 1425 | (message "Setting up indent for shell type %s" sh-shell) |
| 1423 | (set (make-local-variable 'sh-electric-rparen-needed-here) | ||
| 1424 | (sh-feature sh-electric-rparen-needed)) | ||
| 1425 | (set (make-local-variable 'parse-sexp-lookup-properties) t) | 1426 | (set (make-local-variable 'parse-sexp-lookup-properties) t) |
| 1426 | (sh-scan-buffer) | ||
| 1427 | (set (make-local-variable 'sh-kw-alist) (sh-feature sh-kw)) | 1427 | (set (make-local-variable 'sh-kw-alist) (sh-feature sh-kw)) |
| 1428 | (let ((regexp (sh-feature sh-kws-for-done))) | 1428 | (let ((regexp (sh-feature sh-kws-for-done))) |
| 1429 | (if regexp | 1429 | (if regexp |
| @@ -1923,7 +1923,8 @@ STRING This is ignored for the purposes of calculating | |||
| 1923 | ;; Note: setting result to t means we are done and will return nil. | 1923 | ;; Note: setting result to t means we are done and will return nil. |
| 1924 | ;;(This function never returns just t.) | 1924 | ;;(This function never returns just t.) |
| 1925 | (cond | 1925 | (cond |
| 1926 | ((equal (get-text-property (point) 'syntax-table) sh-here-doc-syntax) | 1926 | ((and (boundp 'font-lock-string-face) |
| 1927 | (equal (get-text-property (point) 'face) font-lock-string-face)) | ||
| 1927 | (setq result t) | 1928 | (setq result t) |
| 1928 | (setq have-result t)) | 1929 | (setq have-result t)) |
| 1929 | ((looking-at "\\s-*#") ; was (equal this-kw "#") | 1930 | ((looking-at "\\s-*#") ; was (equal this-kw "#") |
| @@ -1982,7 +1983,7 @@ STRING This is ignored for the purposes of calculating | |||
| 1982 | (cond | 1983 | (cond |
| 1983 | ((and (equal x ")") | 1984 | ((and (equal x ")") |
| 1984 | (equal (get-text-property (1- (point)) 'syntax-table) | 1985 | (equal (get-text-property (1- (point)) 'syntax-table) |
| 1985 | sh-special-syntax)) | 1986 | sh-st-punc)) |
| 1986 | (sh-debug "Case label) here") | 1987 | (sh-debug "Case label) here") |
| 1987 | (setq x 'case-label) | 1988 | (setq x 'case-label) |
| 1988 | (if (setq val (sh-check-rule 2 x)) | 1989 | (if (setq val (sh-check-rule 2 x)) |
| @@ -2120,13 +2121,15 @@ we go to the end of the previous line and do not check for continuations." | |||
| 2120 | (forward-comment (- (point-max))) | 2121 | (forward-comment (- (point-max))) |
| 2121 | (unless end (beginning-of-line)) | 2122 | (unless end (beginning-of-line)) |
| 2122 | (when (and (not (bobp)) | 2123 | (when (and (not (bobp)) |
| 2123 | (equal (get-text-property (1- (point)) 'syntax-table) | 2124 | (boundp 'font-lock-string-face) |
| 2124 | sh-here-doc-syntax)) | 2125 | (equal (get-text-property (1- (point)) 'face) |
| 2125 | (let ((p1 (previous-single-property-change (1- (point)) 'syntax-table))) | 2126 | font-lock-string-face)) |
| 2127 | (let ((p1 (previous-single-property-change (1- (point)) 'face))) | ||
| 2126 | (when p1 | 2128 | (when p1 |
| 2127 | (goto-char p1) | 2129 | (goto-char p1) |
| 2128 | (forward-line -1) | 2130 | (if end |
| 2129 | (if end (end-of-line))))) | 2131 | (end-of-line) |
| 2132 | (beginning-of-line))))) | ||
| 2130 | (unless end | 2133 | (unless end |
| 2131 | ;; we must check previous lines to see if they are continuation lines | 2134 | ;; we must check previous lines to see if they are continuation lines |
| 2132 | ;; if so, we must return position of first of them | 2135 | ;; if so, we must return position of first of them |
| @@ -2187,8 +2190,7 @@ we go to the end of the previous line and do not check for continuations." | |||
| 2187 | (setq found nil)) | 2190 | (setq found nil)) |
| 2188 | (or found | 2191 | (or found |
| 2189 | (sh-debug "Did not find prev stmt."))) | 2192 | (sh-debug "Did not find prev stmt."))) |
| 2190 | found | 2193 | found))) |
| 2191 | ))) | ||
| 2192 | 2194 | ||
| 2193 | 2195 | ||
| 2194 | (defun sh-get-word () | 2196 | (defun sh-get-word () |
| @@ -2283,8 +2285,7 @@ If AND-MOVE is non-nil then move to end of word." | |||
| 2283 | (buffer-substring (point) | 2285 | (buffer-substring (point) |
| 2284 | (progn (skip-chars-forward "^ \t\n;")(point))) | 2286 | (progn (skip-chars-forward "^ \t\n;")(point))) |
| 2285 | (unless and-move | 2287 | (unless and-move |
| 2286 | (goto-char start))) | 2288 | (goto-char start))))) |
| 2287 | )) | ||
| 2288 | 2289 | ||
| 2289 | (defun sh-find-prev-matching (open close &optional depth) | 2290 | (defun sh-find-prev-matching (open close &optional depth) |
| 2290 | "Find a matching token for a set of opening and closing keywords. | 2291 | "Find a matching token for a set of opening and closing keywords. |
| @@ -2981,337 +2982,7 @@ Return values: | |||
| 2981 | (car (car x))) | 2982 | (car (car x))) |
| 2982 | ;; result is nil here | 2983 | ;; result is nil here |
| 2983 | )) | 2984 | )) |
| 2984 | result | 2985 | result))) |
| 2985 | ))) | ||
| 2986 | |||
| 2987 | |||
| 2988 | ;; The default font-lock-unfontify-region-function removes | ||
| 2989 | ;; syntax-table properties, and so removes our information. | ||
| 2990 | (defun sh-font-lock-unfontify-region-function (beg end) | ||
| 2991 | (let* ((modified (buffer-modified-p)) (buffer-undo-list t) | ||
| 2992 | (inhibit-read-only t) (inhibit-point-motion-hooks t) | ||
| 2993 | before-change-functions after-change-functions | ||
| 2994 | deactivate-mark buffer-file-name buffer-file-truename) | ||
| 2995 | (remove-text-properties beg end '(face nil)) | ||
| 2996 | (when (and (not modified) (buffer-modified-p)) | ||
| 2997 | (set-buffer-modified-p nil)))) | ||
| 2998 | |||
| 2999 | (defun sh-set-char-syntax (where new-prop) | ||
| 3000 | "Set the character's syntax table property at WHERE to be NEW-PROP." | ||
| 3001 | (or where | ||
| 3002 | (setq where (point))) | ||
| 3003 | (let ((inhibit-modification-hooks t)) | ||
| 3004 | (put-text-property where (1+ where) 'syntax-table new-prop) | ||
| 3005 | (add-text-properties where (1+ where) | ||
| 3006 | '(face sh-st-face rear-nonsticky t)) | ||
| 3007 | )) | ||
| 3008 | |||
| 3009 | |||
| 3010 | (defun sh-check-paren-in-case () | ||
| 3011 | "Make syntax class of case label's right parenthesis not close parenthesis. | ||
| 3012 | If this parenthesis is a case alternative, set its syntax class to a word." | ||
| 3013 | (let ((start (point)) | ||
| 3014 | state prev-line) | ||
| 3015 | ;; First test if this is a possible candidate, the first "(" or ")" | ||
| 3016 | ;; on the line; then, if go, check prev line is ;; or case. | ||
| 3017 | (save-excursion | ||
| 3018 | (beginning-of-line) | ||
| 3019 | ;; stop at comment or when depth becomes -1 | ||
| 3020 | (setq state (parse-partial-sexp (point) start -1 nil nil t)) | ||
| 3021 | (if (and | ||
| 3022 | (= (car state) -1) | ||
| 3023 | (= (point) start) | ||
| 3024 | (setq prev-line (sh-prev-line nil))) | ||
| 3025 | (progn | ||
| 3026 | (goto-char prev-line) | ||
| 3027 | (beginning-of-line) | ||
| 3028 | ;; (setq case-stmt-start (point)) | ||
| 3029 | ;; (if (looking-at "\\(^\\s-*case[^-a-z0-9_]\\|[^#]*;;\\s-*$\\)") | ||
| 3030 | (if (sh-search-word "\\(case\\|;;\\)" start) | ||
| 3031 | (sh-set-char-syntax (1- start) sh-special-syntax) | ||
| 3032 | )))))) | ||
| 3033 | |||
| 3034 | (defun sh-electric-rparen () | ||
| 3035 | "Insert a right parenthesis and check if it is a case alternative. | ||
| 3036 | If so, its syntax class is set to word, and its text property | ||
| 3037 | is set to have face `sh-st-face'." | ||
| 3038 | (interactive) | ||
| 3039 | (insert ")") | ||
| 3040 | (if sh-electric-rparen-needed-here | ||
| 3041 | (sh-check-paren-in-case))) | ||
| 3042 | |||
| 3043 | (defun sh-electric-hash () | ||
| 3044 | "Insert a hash, but check it is preceded by \"$\". | ||
| 3045 | If so, it is given a syntax type of comment. | ||
| 3046 | Its text property has face `sh-st-face'." | ||
| 3047 | (interactive) | ||
| 3048 | (let ((pos (point))) | ||
| 3049 | (insert "#") | ||
| 3050 | (if (eq (char-before pos) ?$) | ||
| 3051 | (sh-set-char-syntax pos sh-st-punc)))) | ||
| 3052 | |||
| 3053 | (defun sh-electric-less (arg) | ||
| 3054 | "Insert a \"<\" and see if this is the start of a here-document. | ||
| 3055 | If so, the syntax class is set so that it will not be automatically | ||
| 3056 | reindented. | ||
| 3057 | Argument ARG if non-nil disables this test." | ||
| 3058 | (interactive "*P") | ||
| 3059 | (let ((p1 (point)) p2 p3) | ||
| 3060 | (sh-maybe-here-document arg) ;; call the original fn in sh-script.el. | ||
| 3061 | (setq p2 (point)) | ||
| 3062 | (if (/= (+ p1 (prefix-numeric-value arg)) p2) | ||
| 3063 | (save-excursion | ||
| 3064 | (forward-line 1) | ||
| 3065 | (end-of-line) | ||
| 3066 | (setq p3 (point)) | ||
| 3067 | (sh-set-here-doc-region p2 p3)) | ||
| 3068 | ))) | ||
| 3069 | |||
| 3070 | (defun sh-set-here-doc-region (start end) | ||
| 3071 | "Mark a here-document from START to END so that it will not be reindented." | ||
| 3072 | (interactive "r") | ||
| 3073 | ;; Make the whole thing have syntax type word... | ||
| 3074 | ;; That way sexp movement doens't worry about any parentheses. | ||
| 3075 | ;; A disadvantage of this is we can't use forward-word within a | ||
| 3076 | ;; here-doc, which is annoying. | ||
| 3077 | (let ((inhibit-modification-hooks t)) | ||
| 3078 | (put-text-property start end 'syntax-table sh-here-doc-syntax) | ||
| 3079 | (put-text-property start end 'face 'sh-heredoc-face) | ||
| 3080 | (put-text-property (1- end) end 'rear-nonsticky t) | ||
| 3081 | (put-text-property start (1+ start) 'front-sticky t) | ||
| 3082 | )) | ||
| 3083 | |||
| 3084 | (defun sh-remove-our-text-properties () | ||
| 3085 | "Remove text properties relating to right parentheses and here documents." | ||
| 3086 | (interactive) | ||
| 3087 | (save-excursion | ||
| 3088 | (goto-char (point-min)) | ||
| 3089 | (while (not (eobp)) | ||
| 3090 | (let ((plist (text-properties-at (point))) | ||
| 3091 | (next-change | ||
| 3092 | (or (next-single-property-change (point) 'syntax-table | ||
| 3093 | (current-buffer) ) | ||
| 3094 | (point-max)))) | ||
| 3095 | ;; Process text from point to NEXT-CHANGE... | ||
| 3096 | (if (get-text-property (point) 'syntax-table) | ||
| 3097 | (progn | ||
| 3098 | (sh-debug "-- removing props from %d to %d --" | ||
| 3099 | (point) next-change) | ||
| 3100 | (remove-text-properties (point) next-change | ||
| 3101 | '(syntax-table nil)) | ||
| 3102 | (remove-text-properties (point) next-change '(face nil)) | ||
| 3103 | )) | ||
| 3104 | (goto-char next-change))) | ||
| 3105 | )) | ||
| 3106 | |||
| 3107 | ;; (defun sh-search-word (word &optional limit) | ||
| 3108 | ;; "Search forward for regexp WORD occurring as a word not in string nor comment. | ||
| 3109 | ;; If found, returns non nil with the match available in \(match-string 2\). | ||
| 3110 | ;; Yes 2, not 1, since we build a regexp to guard against false matches | ||
| 3111 | ;; such as matching \"a-case\" when we are searching for \"case\". | ||
| 3112 | ;; If not found, it returns nil. | ||
| 3113 | ;; The search maybe limited by optional argument LIMIT." | ||
| 3114 | ;; (interactive "sSearch for: ") | ||
| 3115 | ;; (let ((found nil) | ||
| 3116 | ;; ;; Cannot use \\b here since it matches "-" and "_" | ||
| 3117 | ;; (regexp (sh-mkword-regexp word)) | ||
| 3118 | ;; start state where) | ||
| 3119 | ;; (setq start (point)) | ||
| 3120 | ;; (while (and (setq start (point)) | ||
| 3121 | ;; (not found) | ||
| 3122 | ;; (re-search-forward regexp limit t)) | ||
| 3123 | ;; ;; Found str; check it is not in a comment or string. | ||
| 3124 | ;; (setq state | ||
| 3125 | ;; ;; Stop on comment: | ||
| 3126 | ;; (parse-partial-sexp start (point) nil nil nil 'syntax_table)) | ||
| 3127 | ;; (if (setq where (nth 8 state)) | ||
| 3128 | ;; ;; in comment or string | ||
| 3129 | ;; (if (= where -1) | ||
| 3130 | ;; (setq found (point)) | ||
| 3131 | ;; (if (eq (char-after where) ?#) | ||
| 3132 | ;; (end-of-line) | ||
| 3133 | ;; (goto-char where) | ||
| 3134 | ;; (unless (sh-safe-forward-sexp) | ||
| 3135 | ;; ;; If the above fails we must either give up or | ||
| 3136 | ;; ;; move forward and try again. | ||
| 3137 | ;; (forward-line 1)) | ||
| 3138 | ;; )) | ||
| 3139 | ;; ;; not in comment or string, so accept it | ||
| 3140 | ;; (setq found (point)) | ||
| 3141 | ;; )) | ||
| 3142 | ;; found | ||
| 3143 | ;; )) | ||
| 3144 | |||
| 3145 | (defun sh-search-word (word &optional limit) | ||
| 3146 | "Search forward for regexp WORD occurring as a word not in string nor comment. | ||
| 3147 | If found, returns non-nil, with the match available in \(match-string 2\). | ||
| 3148 | Yes, that is 2, not 1. | ||
| 3149 | If not found, it returns nil. | ||
| 3150 | The search may be limited by optional argument LIMIT." | ||
| 3151 | (interactive "sSearch for: ") | ||
| 3152 | (let ((found nil) | ||
| 3153 | start state where match) | ||
| 3154 | (setq start (point)) | ||
| 3155 | (while (and (not found) | ||
| 3156 | (re-search-forward word limit t)) | ||
| 3157 | (setq match (match-data)) | ||
| 3158 | ;; Found the word as a string; check it occurs as a word. | ||
| 3159 | (when (and (or (= (match-beginning 0) (point-min)) | ||
| 3160 | (save-excursion | ||
| 3161 | (goto-char (1- (match-beginning 0))) | ||
| 3162 | (looking-at "[^-a-z0-9_]"))) | ||
| 3163 | (or (= (point) (point-max)) | ||
| 3164 | (looking-at "[^-a-z0-9_]"))) | ||
| 3165 | ;; Check it is not in a comment or string. | ||
| 3166 | (setq state | ||
| 3167 | ;; Stop on comment: | ||
| 3168 | (parse-partial-sexp start (point) nil nil nil 'syntax_table)) | ||
| 3169 | (if (setq where (nth 8 state)) | ||
| 3170 | ;; in comment or string | ||
| 3171 | (if (= where -1) | ||
| 3172 | (setq found (point)) | ||
| 3173 | (if (eq (char-after where) ?#) | ||
| 3174 | (end-of-line) | ||
| 3175 | (goto-char where) | ||
| 3176 | (unless (sh-safe-forward-sexp) | ||
| 3177 | ;; If the above fails we must either give up or | ||
| 3178 | ;; move forward and try again. | ||
| 3179 | (forward-line 1)))) | ||
| 3180 | ;; not in comment or string, so accept it | ||
| 3181 | (setq found (point))) | ||
| 3182 | (setq start (point)))) | ||
| 3183 | (when found | ||
| 3184 | (set-match-data match) | ||
| 3185 | (goto-char (1- (match-beginning 0))) | ||
| 3186 | (looking-at (sh-mkword-regexp word)) | ||
| 3187 | (goto-char found)) | ||
| 3188 | found | ||
| 3189 | )) | ||
| 3190 | |||
| 3191 | |||
| 3192 | (defun sh-scan-case () | ||
| 3193 | "Scan a case statement for right parens belonging to case alternatives. | ||
| 3194 | Mark each as having syntax `sh-special-syntax'. | ||
| 3195 | Called from scan-buff. If ok, return non-nil." | ||
| 3196 | (let (end | ||
| 3197 | state | ||
| 3198 | (depth 1) ;; we are called at a "case" | ||
| 3199 | (start (point)) | ||
| 3200 | (return t)) | ||
| 3201 | ;; We enter here at a case statement | ||
| 3202 | ;; First, find limits of the case. | ||
| 3203 | (while (and (> depth 0) | ||
| 3204 | (sh-search-word "\\(case\\|esac\\)")) | ||
| 3205 | (if (equal (match-string 2) "case") | ||
| 3206 | (setq depth (1+ depth)) | ||
| 3207 | (setq depth (1- depth)))) | ||
| 3208 | ;; (message "end of search for esac at %d depth=%d" (point) depth) | ||
| 3209 | (setq end (point)) | ||
| 3210 | (goto-char start) | ||
| 3211 | ;; if we found the esac, then fix all appropriate ')'s in the region | ||
| 3212 | (if (zerop depth) | ||
| 3213 | (progn | ||
| 3214 | (while (< (point) end) | ||
| 3215 | ;; search for targetdepth of -1 meaning extra right paren | ||
| 3216 | (setq state (parse-partial-sexp (point) end -1 nil nil nil)) | ||
| 3217 | (if (and (= (car state) -1) | ||
| 3218 | (= (char-before) ?\))) | ||
| 3219 | (progn | ||
| 3220 | ;; (message "At %d state is %s" (point) state) | ||
| 3221 | ;; (message "Fixing %d" (point)) | ||
| 3222 | (sh-set-char-syntax (1- (point)) sh-special-syntax) | ||
| 3223 | ;; we could advance to the next ";;" perhaps | ||
| 3224 | ) | ||
| 3225 | ;; (message "? Not found at %d" (point)) ; ok, could be "]" | ||
| 3226 | )) | ||
| 3227 | (goto-char end)) | ||
| 3228 | (message "No matching esac for case at %d" start) | ||
| 3229 | (setq return nil) | ||
| 3230 | ) | ||
| 3231 | return | ||
| 3232 | )) | ||
| 3233 | |||
| 3234 | |||
| 3235 | ;; FIXME: This loses big time on very large files (such as CVS' sanity.sh). | ||
| 3236 | (defun sh-scan-buffer () | ||
| 3237 | "Scan a sh buffer for case statements and here-documents. | ||
| 3238 | |||
| 3239 | For each case alternative found, mark its \")\" with a text property | ||
| 3240 | so that its syntax class is no longer a close parenthesis character. | ||
| 3241 | |||
| 3242 | Each here-document is also marked so that it is effectively immune | ||
| 3243 | from indentation changes." | ||
| 3244 | ;; Do not call this interactively, call `sh-rescan-buffer' instead. | ||
| 3245 | (sh-must-be-shell-mode) | ||
| 3246 | (let ((n 0) | ||
| 3247 | (initial-buffer-modified-p (buffer-modified-p)) | ||
| 3248 | start end where label ws) | ||
| 3249 | (save-excursion | ||
| 3250 | (goto-char (point-min)) | ||
| 3251 | ;; 1. Scan for ")" in case statements. | ||
| 3252 | (while (and ;; (re-search-forward "^[^#]*\\bcase\\b" nil t) | ||
| 3253 | (sh-search-word "\\(case\\|esac\\)") | ||
| 3254 | ;; (progn (message "Found a case at %d" (point)) t) | ||
| 3255 | (sh-scan-case))) | ||
| 3256 | ;; 2. Scan for here docs | ||
| 3257 | (goto-char (point-min)) | ||
| 3258 | ;; while (re-search-forward "<<\\(-?\\)\\(\\s-*\\)\\(.*\\)$" nil t) | ||
| 3259 | (while (re-search-forward "<<\\(-?\\)" nil t) | ||
| 3260 | (unless (sh-in-comment-or-string (match-beginning 0)) | ||
| 3261 | ;; (setq label (match-string 3)) | ||
| 3262 | (setq label (sh-get-word)) | ||
| 3263 | (if (string= (match-string 1) "-") | ||
| 3264 | ;; if <<- then we allow whitespace | ||
| 3265 | (setq ws "\\s-*") | ||
| 3266 | ;; otherwise we don't | ||
| 3267 | (setq ws "")) | ||
| 3268 | (while (string-match "['\"\\]" label) | ||
| 3269 | (setq label (replace-match "" nil nil label))) | ||
| 3270 | (if (setq n (string-match "\\s-+$" label)) | ||
| 3271 | (setq label (substring label 0 n))) | ||
| 3272 | (forward-line 1) | ||
| 3273 | ;; the line containing the << could be continued... | ||
| 3274 | (while (sh-this-is-a-continuation) | ||
| 3275 | (forward-line 1)) | ||
| 3276 | (setq start (point)) | ||
| 3277 | (if (re-search-forward (concat "^" ws (regexp-quote label) | ||
| 3278 | "\\s-*$") | ||
| 3279 | nil t) | ||
| 3280 | (sh-set-here-doc-region start (point)) | ||
| 3281 | (sh-debug "missing here-doc delimiter `%s'" label)))) | ||
| 3282 | ;; 3. Scan for $# -- make the "#" a punctuation not a comment | ||
| 3283 | (goto-char (point-min)) | ||
| 3284 | (let (state) | ||
| 3285 | (while (and (not (eobp)) | ||
| 3286 | (setq state (parse-partial-sexp | ||
| 3287 | (1+ (point))(point-max) nil nil nil t)) | ||
| 3288 | (nth 4 state)) | ||
| 3289 | (goto-char (nth 8 state)) | ||
| 3290 | (sh-debug "At %d %s" (point) (eq (char-before) ?$)) | ||
| 3291 | (if (eq (char-before) ?$) | ||
| 3292 | (sh-set-char-syntax (point) sh-st-punc) ;; not a comment! | ||
| 3293 | (end-of-line) ;; if this *was* a comment, ignore rest of line! | ||
| 3294 | ))) | ||
| 3295 | ;; 4. Hide these changes from making a previously unmodified | ||
| 3296 | ;; buffer into a modified buffer. | ||
| 3297 | (if sh-debug | ||
| 3298 | (if initial-buffer-modified-p | ||
| 3299 | (message "buffer was initially modified") | ||
| 3300 | (message | ||
| 3301 | "buffer not initially modified - so clearing modified flag"))) | ||
| 3302 | (set-buffer-modified-p initial-buffer-modified-p) | ||
| 3303 | ))) | ||
| 3304 | |||
| 3305 | (defun sh-rescan-buffer () | ||
| 3306 | "Rescan the buffer for case alternative parentheses and here documents." | ||
| 3307 | (interactive) | ||
| 3308 | (if (eq major-mode 'sh-mode) | ||
| 3309 | (let ((inhibit-read-only t)) | ||
| 3310 | (sh-remove-our-text-properties) | ||
| 3311 | (message "Re-scanning buffer...") | ||
| 3312 | (sh-scan-buffer) | ||
| 3313 | (message "Re-scanning buffer...done") | ||
| 3314 | ))) | ||
| 3315 | 2986 | ||
| 3316 | ;; ======================================================================== | 2987 | ;; ======================================================================== |
| 3317 | 2988 | ||