diff options
| author | Stefan Monnier | 2011-02-05 14:46:47 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2011-02-05 14:46:47 -0500 |
| commit | ba70ab1cad8149357bd92e23af80c96c08be57ea (patch) | |
| tree | 1adc03d3336a754d64541630f6dfbb08812fd399 | |
| parent | 7cb76591b02e831807bff7928b1f5f019761e0de (diff) | |
| download | emacs-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/ChangeLog | 14 | ||||
| -rw-r--r-- | lisp/progmodes/sh-script.el | 145 |
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 @@ | |||
| 1 | 2011-02-05 Stefan Monnier <monnier@iro.umontreal.ca> | 1 | 2011-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. | ||
| 945 | If 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))) |