aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2011-02-05 14:46:47 -0500
committerStefan Monnier2011-02-05 14:46:47 -0500
commitba70ab1cad8149357bd92e23af80c96c08be57ea (patch)
tree1adc03d3336a754d64541630f6dfbb08812fd399
parent7cb76591b02e831807bff7928b1f5f019761e0de (diff)
downloademacs-ba70ab1cad8149357bd92e23af80c96c08be57ea.tar.gz
emacs-ba70ab1cad8149357bd92e23af80c96c08be57ea.zip
* lisp/progmodes/sh-script.el (sh-here-doc-open-re): Don't rely on the
font-lock-syntax-table remappings. (sh-here-doc-markers, sh-here-doc-re): Remove. (sh-font-lock-close-heredoc): Remove. (sh-syntax-propertize-here-doc): New function. (sh-font-lock-open-heredoc): Set the sh-here-doc-marker property instead of the sh-here-doc-re. (sh-font-lock-paren): Don't do anything in comments or strings. Handle line continuations. Accept a few more chars. Don't rely on the font-lock-syntax-table remappings. `esac' is not a valid pattern. (sh-syntax-propertize-function): Handle here-docs differently, so we don't bother syntax-propertizing the insides. Fixes: debbugs:7947
-rw-r--r--lisp/ChangeLog14
-rw-r--r--lisp/progmodes/sh-script.el145
2 files changed, 67 insertions, 92 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 2bdce356f5d..2c514d535a7 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,19 @@
12011-02-05 Stefan Monnier <monnier@iro.umontreal.ca> 12011-02-05 Stefan Monnier <monnier@iro.umontreal.ca>
2 2
3 * progmodes/sh-script.el (sh-here-doc-open-re): Don't rely on the
4 font-lock-syntax-table remappings.
5 (sh-here-doc-markers, sh-here-doc-re): Remove.
6 (sh-font-lock-close-heredoc): Remove.
7 (sh-syntax-propertize-here-doc): New function.
8 (sh-font-lock-open-heredoc): Set the sh-here-doc-marker property
9 instead of the sh-here-doc-re.
10 (sh-font-lock-paren): Don't do anything in comments or strings.
11 Handle line continuations. Accept a few more chars.
12 Don't rely on the font-lock-syntax-table remappings.
13 `esac' is not a valid pattern.
14 (sh-syntax-propertize-function): Handle here-docs differently, so we
15 don't bother syntax-propertizing the insides.
16
3 * progmodes/sh-script.el (sh-font-lock-paren, sh-kw, sh-prev-thing): 17 * progmodes/sh-script.el (sh-font-lock-paren, sh-kw, sh-prev-thing):
4 Handle new bashisms ";&" and ";;&" (bug#7947). 18 Handle new bashisms ";&" and ";;&" (bug#7947).
5 19
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index 30d768d7743..d80d814156e 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -925,65 +925,16 @@ See `sh-feature'.")
925(defconst sh-st-punc (string-to-syntax ".")) 925(defconst sh-st-punc (string-to-syntax "."))
926(defconst sh-here-doc-syntax (string-to-syntax "|")) ;; generic string 926(defconst sh-here-doc-syntax (string-to-syntax "|")) ;; generic string
927 927
928(defconst sh-escaped-line-re 928(eval-and-compile
929 ;; Should match until the real end-of-continued-line, but if that is not 929 (defconst sh-escaped-line-re
930 ;; possible (because we bump into EOB or the search bound), then we should 930 ;; Should match until the real end-of-continued-line, but if that is not
931 ;; match until the search bound. 931 ;; possible (because we bump into EOB or the search bound), then we should
932 "\\(?:\\(?:.*[^\\\n]\\)?\\(?:\\\\\\\\\\)*\\\\\n\\)*.*") 932 ;; match until the search bound.
933 933 "\\(?:\\(?:.*[^\\\n]\\)?\\(?:\\\\\\\\\\)*\\\\\n\\)*.*")
934(defconst sh-here-doc-open-re 934
935 (concat "<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\)+\\)" 935 (defconst sh-here-doc-open-re
936 sh-escaped-line-re "\\(\n\\)")) 936 (concat "<<-?\\s-*\\\\?\\(\\(?:['\"][^'\"]+['\"]\\|\\sw\\|[-/~._]\\)+\\)"
937 937 sh-escaped-line-re "\\(\n\\)")))
938(defvar sh-here-doc-markers nil)
939(make-variable-buffer-local 'sh-here-doc-markers)
940(defvar sh-here-doc-re sh-here-doc-open-re)
941(make-variable-buffer-local 'sh-here-doc-re)
942
943(defun sh-font-lock-close-heredoc (bol eof indented eol)
944 "Determine the syntax of the \\n after an EOF.
945If non-nil INDENTED indicates that the EOF was indented."
946 (let* ((eof-re (if eof (regexp-quote eof) ""))
947 ;; A rough regexp that should find the opening <<EOF back.
948 (sre (concat "<<\\(-?\\)\\s-*['\"\\]?"
949 ;; Use \s| to cheaply check it's an open-heredoc.
950 eof-re "['\"]?\\([ \t|;&)<>]"
951 sh-escaped-line-re
952 "\\)?\\s|"))
953 ;; A regexp that will find other EOFs.
954 (ere (concat "^" (if indented "[ \t]*") eof-re "\n"))
955 (start (save-excursion
956 (goto-char bol)
957 ;; FIXME: will incorrectly find a <<EOF embedded inside
958 ;; the heredoc.
959 (re-search-backward (concat sre "\\|" ere) nil t))))
960 ;; If subgroup 1 matched, we found an open-heredoc, otherwise we first
961 ;; found a close-heredoc which makes the current close-heredoc inoperant.
962 (cond
963 ((when (and start (match-end 1)
964 (not (and indented (= (match-beginning 1) (match-end 1))))
965 (not (sh-in-comment-or-string (match-beginning 0))))
966 ;; Make sure our `<<' is not the EOF1 of a `cat <<EOF1 <<EOF2'.
967 (save-excursion
968 (goto-char start)
969 (setq start (line-beginning-position 2))
970 (while
971 (progn
972 (re-search-forward "<<") ; Skip ourselves.
973 (and (re-search-forward sh-here-doc-open-re start 'move)
974 (goto-char (match-beginning 0))
975 (sh-in-comment-or-string (point)))))
976 ;; No <<EOF2 found after our <<.
977 (= (point) start)))
978 (put-text-property eol (1+ eol) 'syntax-table sh-here-doc-syntax))
979 ((not (or start (save-excursion (re-search-forward sre nil t))))
980 ;; There's no <<EOF either before or after us,
981 ;; so we should remove ourselves from font-lock's keywords.
982 (setq sh-here-doc-markers (delete eof sh-here-doc-markers))
983 (setq sh-here-doc-re
984 (concat sh-here-doc-open-re "\\|^\\([ \t]*\\)"
985 (regexp-opt sh-here-doc-markers t) "\\(\n\\)"))
986 nil))))
987 938
988(defun sh-font-lock-open-heredoc (start string eol) 939(defun sh-font-lock-open-heredoc (start string eol)
989 "Determine the syntax of the \\n after a <<EOF. 940 "Determine the syntax of the \\n after a <<EOF.
@@ -996,27 +947,35 @@ Point is at the beginning of the next line."
996 (sh-in-comment-or-string start)) 947 (sh-in-comment-or-string start))
997 ;; We're looking at <<STRING, so we add "^STRING$" to the syntactic 948 ;; We're looking at <<STRING, so we add "^STRING$" to the syntactic
998 ;; font-lock keywords to detect the end of this here document. 949 ;; font-lock keywords to detect the end of this here document.
999 (let ((str (replace-regexp-in-string "['\"]" "" string))) 950 (let ((str (replace-regexp-in-string "['\"]" "" string))
1000 (unless (member str sh-here-doc-markers) 951 (ppss (save-excursion (syntax-ppss (1- (point))))))
1001 (push str sh-here-doc-markers)
1002 (setq sh-here-doc-re
1003 (concat sh-here-doc-open-re "\\|^\\([ \t]*\\)"
1004 (regexp-opt sh-here-doc-markers t) "\\(\n\\)"))))
1005 (let ((ppss (save-excursion (syntax-ppss (1- (point))))))
1006 (if (nth 4 ppss) 952 (if (nth 4 ppss)
1007 ;; The \n not only starts the heredoc but also closes a comment. 953 ;; The \n not only starts the heredoc but also closes a comment.
1008 ;; Let's close the comment just before the \n. 954 ;; Let's close the comment just before the \n.
1009 (put-text-property (1- (point)) (point) 'syntax-table '(12))) ;">" 955 (put-text-property (1- (point)) (point) 'syntax-table '(12))) ;">"
1010 (if (or (nth 5 ppss) (> (count-lines start (point)) 1)) 956 (if (or (nth 5 ppss) (> (count-lines start (point)) 1))
1011 ;; If the sh-escaped-line-re part of sh-here-doc-re has matched 957 ;; If the sh-escaped-line-re part of sh-here-doc-open-re has matched
1012 ;; several lines, make sure we refontify them together. 958 ;; several lines, make sure we refontify them together.
1013 ;; Furthermore, if (nth 5 ppss) is non-nil (i.e. the \n is 959 ;; Furthermore, if (nth 5 ppss) is non-nil (i.e. the \n is
1014 ;; escaped), it means the right \n is actually further down. 960 ;; escaped), it means the right \n is actually further down.
1015 ;; Don't bother fixing it now, but place a multiline property so 961 ;; Don't bother fixing it now, but place a multiline property so
1016 ;; that when jit-lock-context-* refontifies the rest of the 962 ;; that when jit-lock-context-* refontifies the rest of the
1017 ;; buffer, it also refontifies the current line with it. 963 ;; buffer, it also refontifies the current line with it.
1018 (put-text-property start (point) 'syntax-multiline t))) 964 (put-text-property start (point) 'syntax-multiline t))
1019 (put-text-property eol (1+ eol) 'syntax-table sh-here-doc-syntax))) 965 (put-text-property eol (1+ eol) 'sh-here-doc-marker str)
966 (prog1 sh-here-doc-syntax
967 (goto-char (+ 2 start))))))
968
969(defun sh-syntax-propertize-here-doc (end)
970 (let ((ppss (syntax-ppss)))
971 (when (eq t (nth 3 ppss))
972 (let ((key (get-text-property (nth 8 ppss) 'sh-here-doc-marker)))
973 (when (re-search-forward
974 (concat "^\\([ \t]*\\)" (regexp-quote key) "\\(\n\\)")
975 end 'move)
976 (let ((eol (match-beginning 2)))
977 (put-text-property eol (1+ eol)
978 'syntax-table sh-here-doc-syntax)))))))
1020 979
1021(defun sh-font-lock-quoted-subshell (limit) 980(defun sh-font-lock-quoted-subshell (limit)
1022 "Search for a subshell embedded in a string. 981 "Search for a subshell embedded in a string.
@@ -1068,19 +1027,25 @@ subshells can nest."
1068 (not (sh-is-quoted-p (1- pos))))) 1027 (not (sh-is-quoted-p (1- pos)))))
1069 1028
1070(defun sh-font-lock-paren (start) 1029(defun sh-font-lock-paren (start)
1030 (unless (nth 8 (syntax-ppss))
1071 (save-excursion 1031 (save-excursion
1072 (goto-char start) 1032 (goto-char start)
1073 ;; Skip through all patterns 1033 ;; Skip through all patterns
1074 (while 1034 (while
1075 (progn 1035 (progn
1036 (while
1037 (progn
1076 (forward-comment (- (point-max))) 1038 (forward-comment (- (point-max)))
1039 (when (and (eolp) (sh-is-quoted-p (point)))
1040 (forward-char -1)
1041 t)))
1077 ;; Skip through one pattern 1042 ;; Skip through one pattern
1078 (while 1043 (while
1079 (or (/= 0 (skip-syntax-backward "w_")) 1044 (or (/= 0 (skip-syntax-backward "w_"))
1080 (/= 0 (skip-chars-backward "?[]*@/\\")) 1045 (/= 0 (skip-chars-backward "-$=?[]*@/\\\\"))
1081 (and (sh-is-quoted-p (1- (point))) 1046 (and (sh-is-quoted-p (1- (point)))
1082 (goto-char (- (point) 2))) 1047 (goto-char (- (point) 2)))
1083 (when (memq (char-before) '(?\" ?\')) 1048 (when (memq (char-before) '(?\" ?\' ?\}))
1084 (condition-case nil (progn (backward-sexp 1) t) 1049 (condition-case nil (progn (backward-sexp 1) t)
1085 (error nil))))) 1050 (error nil)))))
1086 ;; Patterns can be preceded by an open-paren (Bug#1320). 1051 ;; Patterns can be preceded by an open-paren (Bug#1320).
@@ -1093,9 +1058,6 @@ subshells can nest."
1093 (backward-char 1)) 1058 (backward-char 1))
1094 (when (eq (char-before) ?|) 1059 (when (eq (char-before) ?|)
1095 (backward-char 1) t))) 1060 (backward-char 1) t)))
1096 ;; FIXME: ";; esac )" is a case that looks like a case-pattern but it's
1097 ;; really just a close paren after a case statement. I.e. if we skipped
1098 ;; over `esac' just now, we're not looking at a case-pattern.
1099 (when (progn (backward-char 2) 1061 (when (progn (backward-char 2)
1100 (if (> start (line-end-position)) 1062 (if (> start (line-end-position))
1101 (put-text-property (point) (1+ start) 1063 (put-text-property (point) (1+ start)
@@ -1104,8 +1066,13 @@ subshells can nest."
1104 ;; a normal command rather than the real `in' keyword. 1066 ;; a normal command rather than the real `in' keyword.
1105 ;; I.e. we should look back to try and find the 1067 ;; I.e. we should look back to try and find the
1106 ;; corresponding `case'. 1068 ;; corresponding `case'.
1107 (looking-at ";[;&]\\|in")) 1069 (and (looking-at ";[;&]\\|in")
1108 sh-st-punc))) 1070 ;; ";; esac )" is a case that looks like a case-pattern
1071 ;; but it's really just a close paren after a case
1072 ;; statement. I.e. if we skipped over `esac' just now,
1073 ;; we're not looking at a case-pattern.
1074 (not (looking-at "..[ \t\n]+esac[^[:word:]_]"))))
1075 sh-st-punc))))
1109 1076
1110(defun sh-font-lock-backslash-quote () 1077(defun sh-font-lock-backslash-quote ()
1111 (if (eq (save-excursion (nth 3 (syntax-ppss (match-beginning 0)))) ?\') 1078 (if (eq (save-excursion (nth 3 (syntax-ppss (match-beginning 0)))) ?\')
@@ -1115,12 +1082,13 @@ subshells can nest."
1115 1082
1116(defun sh-syntax-propertize-function (start end) 1083(defun sh-syntax-propertize-function (start end)
1117 (goto-char start) 1084 (goto-char start)
1118 (while (prog1 1085 (sh-syntax-propertize-here-doc end)
1119 (re-search-forward sh-here-doc-re end 'move)
1120 (save-excursion
1121 (save-match-data
1122 (funcall 1086 (funcall
1123 (syntax-propertize-rules 1087 (syntax-propertize-rules
1088 (sh-here-doc-open-re
1089 (2 (sh-font-lock-open-heredoc
1090 (match-beginning 0) (match-string 1) (match-beginning 2))))
1091 ("\\s|" (0 (prog1 nil (sh-syntax-propertize-here-doc end))))
1124 ;; A `#' begins a comment when it is unquoted and at the 1092 ;; A `#' begins a comment when it is unquoted and at the
1125 ;; beginning of a word. In the shell, words are separated by 1093 ;; beginning of a word. In the shell, words are separated by
1126 ;; metacharacters. The list of special chars is taken from 1094 ;; metacharacters. The list of special chars is taken from
@@ -1135,22 +1103,15 @@ subshells can nest."
1135 (")" (0 (sh-font-lock-paren (match-beginning 0)))) 1103 (")" (0 (sh-font-lock-paren (match-beginning 0))))
1136 ;; Highlight (possibly nested) subshells inside "" quoted 1104 ;; Highlight (possibly nested) subshells inside "" quoted
1137 ;; regions correctly. 1105 ;; regions correctly.
1138 ("\"\\(?:\\(?:.\\|\n\\)*?[^\\]\\(?:\\\\\\\\\\)*\\)??\\(\\$(\\|`\\)" 1106 ("\"\\(?:\\(?:[^\\\"]\\|\\)*?[^\\]\\(?:\\\\\\\\\\)*\\)??\\(\\$(\\|`\\)"
1139 (1 (ignore 1107 (1 (ignore
1140 ;; Save excursion because we want to also apply other 1108 ;; Save excursion because we want to also apply other
1141 ;; syntax-propertize rules within the affected region. 1109 ;; syntax-propertize rules within the affected region.
1110 (if (nth 8 (syntax-ppss))
1111 (goto-char (1+ (match-beginning 0)))
1142 (save-excursion 1112 (save-excursion
1143 (sh-font-lock-quoted-subshell end)))))) 1113 (sh-font-lock-quoted-subshell end)))))))
1144 (prog1 start (setq start (point))) (point))))) 1114 (point) end))
1145 (if (match-beginning 2)
1146 ;; FIXME: actually, once we see an heredoc opener, we should just
1147 ;; search for its ender without propertizing anything in it.
1148 (sh-font-lock-open-heredoc
1149 (match-beginning 0) (match-string 1) (match-beginning 2))
1150 (sh-font-lock-close-heredoc
1151 (match-beginning 0) (match-string 4)
1152 (and (match-beginning 3) (/= (match-beginning 3) (match-end 3)))
1153 (match-beginning 5)))))
1154 1115
1155(defun sh-font-lock-syntactic-face-function (state) 1116(defun sh-font-lock-syntactic-face-function (state)
1156 (let ((q (nth 3 state))) 1117 (let ((q (nth 3 state)))