aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes
diff options
context:
space:
mode:
authorStefan Monnier2000-10-21 18:06:17 +0000
committerStefan Monnier2000-10-21 18:06:17 +0000
commit34939e2c4a9633da96e8d2e5bf17a8db516afa76 (patch)
tree2c3b535fa7ec692a335f462df69a9c6381d64e1c /lisp/progmodes
parentf1eed8ff5b6b7b64976b3e27566efa0cd462d934 (diff)
downloademacs-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.el487
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.
1089This specifically excludes an occurrence of WORD followed by 1101This specifically excludes an occurrence of WORD followed by
1090punctuation characters like '-'." 1102punctuation 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.
1095This specifically excludes an occurrence of WORD followed by
1096or 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.
3012If 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.
3036If so, its syntax class is set to word, and its text property
3037is 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 \"$\".
3045If so, it is given a syntax type of comment.
3046Its 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.
3055If so, the syntax class is set so that it will not be automatically
3056reindented.
3057Argument 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.
3147If found, returns non-nil, with the match available in \(match-string 2\).
3148Yes, that is 2, not 1.
3149If not found, it returns nil.
3150The 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.
3194Mark each as having syntax `sh-special-syntax'.
3195Called 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
3239For each case alternative found, mark its \")\" with a text property
3240so that its syntax class is no longer a close parenthesis character.
3241
3242Each here-document is also marked so that it is effectively immune
3243from 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