diff options
| author | Jackson Ray Hamilton | 2019-03-24 09:55:14 -0700 |
|---|---|---|
| committer | Jackson Ray Hamilton | 2019-04-08 22:48:22 -0700 |
| commit | 8b92719b6b31d26299b5feae0ea92bb80f835e3d (patch) | |
| tree | c406d203b06a1832de0b7d299f9ed0acad60e0be | |
| parent | bf37078df2cbea3a44a641ddbe40f11339c135a2 (diff) | |
| download | emacs-8b92719b6b31d26299b5feae0ea92bb80f835e3d.tar.gz emacs-8b92719b6b31d26299b5feae0ea92bb80f835e3d.zip | |
Improve JSX syntax propertization
* lisp/progmodes/js.el (js-jsx--attribute-name-re): New variable.
(js-jsx--syntax-propertize-tag): Allow “-” in JSXAttribute names. Fix
“out of range” error when typing at the end of a buffer. Fix/improve
future propertization of unfinished JSXBoundaryElements.
* test/manual/indent/js-jsx-unclosed-2.js: Add tests for allowed
characters in JSX.
| -rw-r--r-- | lisp/progmodes/js.el | 74 | ||||
| -rw-r--r-- | test/manual/indent/js-jsx-unclosed-2.js | 8 |
2 files changed, 51 insertions, 31 deletions
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 0bba8159c18..5d87489b524 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el | |||
| @@ -2083,11 +2083,15 @@ been propertized." | |||
| 2083 | (throw 'stop nil))) | 2083 | (throw 'stop nil))) |
| 2084 | (setq text-beg (point)))))) | 2084 | (setq text-beg (point)))))) |
| 2085 | 2085 | ||
| 2086 | (defconst js-jsx--attribute-name-re (concat js--name-start-re | ||
| 2087 | "\\(?:\\s_\\|\\sw\\|-\\)*") | ||
| 2088 | "Like `js--name-re', but matches “-” as well.") | ||
| 2089 | |||
| 2086 | (defun js-jsx--syntax-propertize-tag (end) | 2090 | (defun js-jsx--syntax-propertize-tag (end) |
| 2087 | "Determine if a JSXBoundaryElement is before END and propertize it. | 2091 | "Determine if a JSXBoundaryElement is before END and propertize it. |
| 2088 | Disambiguate JSX from inequality operators and arrow functions by | 2092 | Disambiguate JSX from inequality operators and arrow functions by |
| 2089 | testing for syntax only valid as JSX." | 2093 | testing for syntax only valid as JSX." |
| 2090 | (let ((tag-beg (1- (point))) (type 'open) | 2094 | (let ((tag-beg (1- (point))) tag-end (type 'open) |
| 2091 | name-beg name-match-data unambiguous | 2095 | name-beg name-match-data unambiguous |
| 2092 | forward-sexp-function) ; Use Lisp version. | 2096 | forward-sexp-function) ; Use Lisp version. |
| 2093 | (catch 'stop | 2097 | (catch 'stop |
| @@ -2127,46 +2131,54 @@ testing for syntax only valid as JSX." | |||
| 2127 | ;; figure out what type it actually is. | 2131 | ;; figure out what type it actually is. |
| 2128 | (if (eq type 'open) (setq type (if name-beg 'self-closing 'close))) | 2132 | (if (eq type 'open) (setq type (if name-beg 'self-closing 'close))) |
| 2129 | (forward-char)) | 2133 | (forward-char)) |
| 2130 | ((looking-at js--dotted-name-re) | 2134 | ((and (not name-beg) (looking-at js--dotted-name-re)) |
| 2131 | (if (not name-beg) | 2135 | ;; Don’t match code like “if (i < await foo)” |
| 2132 | (progn | 2136 | (if (js--unary-keyword-p (match-string 0)) (throw 'stop nil)) |
| 2133 | ;; Don’t match code like “if (i < await foo)” | 2137 | ;; Save boundaries for later fontification after |
| 2134 | (if (js--unary-keyword-p (match-string 0)) (throw 'stop nil)) | 2138 | ;; unambiguously determining the code is JSX. |
| 2135 | ;; Save boundaries for later fontification after | 2139 | (setq name-beg (match-beginning 0) |
| 2136 | ;; unambiguously determining the code is JSX. | 2140 | name-match-data (match-data)) |
| 2137 | (setq name-beg (match-beginning 0) | 2141 | (goto-char (match-end 0))) |
| 2138 | name-match-data (match-data)) | 2142 | ((and name-beg (looking-at js-jsx--attribute-name-re)) |
| 2139 | (goto-char (match-end 0))) | 2143 | (setq unambiguous t) ; Non-unary name followed by 2nd name ⇒ JSX |
| 2140 | (setq unambiguous t) ; Non-unary name followed by 2nd name ⇒ JSX | 2144 | ;; Save JSXAttribute’s name’s match data for font-locking later. |
| 2141 | ;; Save JSXAttribute’s name’s match data for font-locking later. | 2145 | (put-text-property (match-beginning 0) (1+ (match-beginning 0)) |
| 2142 | (put-text-property (match-beginning 0) (1+ (match-beginning 0)) | 2146 | 'js-jsx-attribute-name (match-data)) |
| 2143 | 'js-jsx-attribute-name (match-data)) | 2147 | (goto-char (match-end 0)) |
| 2144 | (goto-char (match-end 0)) | 2148 | (if (>= (point) end) (throw 'stop nil)) |
| 2149 | (skip-chars-forward " \t\n" end) | ||
| 2150 | (if (>= (point) end) (throw 'stop nil)) | ||
| 2151 | ;; “=” is optional for null-valued JSXAttributes. | ||
| 2152 | (when (= (char-after) ?=) | ||
| 2153 | (forward-char) | ||
| 2145 | (if (>= (point) end) (throw 'stop nil)) | 2154 | (if (>= (point) end) (throw 'stop nil)) |
| 2146 | (skip-chars-forward " \t\n" end) | 2155 | (skip-chars-forward " \t\n" end) |
| 2147 | (if (>= (point) end) (throw 'stop nil)) | 2156 | (if (>= (point) end) (throw 'stop nil)) |
| 2148 | ;; “=” is optional for null-valued JSXAttributes. | 2157 | ;; Skip over strings (if possible). Any |
| 2149 | (when (= (char-after) ?=) | 2158 | ;; JSXExpressionContainer here will be parsed in the |
| 2150 | (forward-char) | 2159 | ;; next iteration of the loop. |
| 2151 | (if (>= (point) end) (throw 'stop nil)) | 2160 | (when (memq (char-after) '(?\" ?\' ?\`)) |
| 2152 | (skip-chars-forward " \t\n" end) | 2161 | (condition-case nil |
| 2153 | (if (>= (point) end) (throw 'stop nil)) | 2162 | (forward-sexp) |
| 2154 | ;; Skip over strings (if possible). Any | 2163 | (scan-error (throw 'stop nil)))))) |
| 2155 | ;; JSXExpressionContainer here will be parsed in the | ||
| 2156 | ;; next iteration of the loop. | ||
| 2157 | (when (memq (char-after) '(?\" ?\' ?\`)) | ||
| 2158 | (condition-case nil | ||
| 2159 | (forward-sexp) | ||
| 2160 | (scan-error (throw 'stop nil))))))) | ||
| 2161 | ;; There is nothing more to check; this either isn’t JSX, or | 2164 | ;; There is nothing more to check; this either isn’t JSX, or |
| 2162 | ;; the tag is incomplete. | 2165 | ;; the tag is incomplete. |
| 2163 | (t (throw 'stop nil))))) | 2166 | (t (throw 'stop nil))))) |
| 2164 | (when unambiguous | 2167 | (when unambiguous |
| 2165 | ;; Save JSXBoundaryElement’s name’s match data for font-locking. | 2168 | ;; Save JSXBoundaryElement’s name’s match data for font-locking. |
| 2166 | (if name-beg (put-text-property name-beg (1+ name-beg) 'js-jsx-tag-name name-match-data)) | 2169 | (if name-beg (put-text-property name-beg (1+ name-beg) 'js-jsx-tag-name name-match-data)) |
| 2170 | ;; Prevent “out of range” errors when typing at the end of a buffer. | ||
| 2171 | (setq tag-end (if (eobp) (1- (point)) (point))) | ||
| 2167 | ;; Mark beginning and end of tag for font-locking. | 2172 | ;; Mark beginning and end of tag for font-locking. |
| 2168 | (put-text-property tag-beg (1+ tag-beg) 'js-jsx-tag-beg (cons type (point))) | 2173 | (put-text-property tag-beg (1+ tag-beg) 'js-jsx-tag-beg (cons type tag-end)) |
| 2169 | (put-text-property (point) (1+ (point)) 'js-jsx-tag-end tag-beg)) | 2174 | (put-text-property tag-end (1+ tag-end) 'js-jsx-tag-end tag-beg) |
| 2175 | ;; Use text properties to extend the syntax-propertize region | ||
| 2176 | ;; backward to the beginning of the JSXBoundaryElement in the | ||
| 2177 | ;; future. Typically the closing angle bracket could suggest | ||
| 2178 | ;; extending backward, but that would also involve more rigorous | ||
| 2179 | ;; parsing, and the closing angle bracket may not even exist yet | ||
| 2180 | ;; if the JSXBoundaryElement is still being typed. | ||
| 2181 | (put-text-property tag-beg (1+ tag-end) 'syntax-multiline t)) | ||
| 2170 | (if (js-jsx--at-enclosing-tag-child-p) (js-jsx--syntax-propertize-tag-text end)))) | 2182 | (if (js-jsx--at-enclosing-tag-child-p) (js-jsx--syntax-propertize-tag-text end)))) |
| 2171 | 2183 | ||
| 2172 | (defconst js-jsx--text-properties | 2184 | (defconst js-jsx--text-properties |
diff --git a/test/manual/indent/js-jsx-unclosed-2.js b/test/manual/indent/js-jsx-unclosed-2.js index 8b6f33325d7..843ef9b6a88 100644 --- a/test/manual/indent/js-jsx-unclosed-2.js +++ b/test/manual/indent/js-jsx-unclosed-2.js | |||
| @@ -29,3 +29,11 @@ while (await foo > bar) void 0 | |||
| 29 | </Baz> | 29 | </Baz> |
| 30 | </Bar> | 30 | </Bar> |
| 31 | </Foo> | 31 | </Foo> |
| 32 | |||
| 33 | // “-” is not allowed in a JSXBoundaryElement’s name. | ||
| 34 | <ABC /> | ||
| 35 | <A-B-C /> // Weirdly-indented “continued expression.” | ||
| 36 | |||
| 37 | // “-” may be used in a JSXAttribute’s name. | ||
| 38 | <Foo a-b-c="" | ||
| 39 | x-y-z="" /> | ||