aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJackson Ray Hamilton2019-03-24 09:55:14 -0700
committerJackson Ray Hamilton2019-04-08 22:48:22 -0700
commit8b92719b6b31d26299b5feae0ea92bb80f835e3d (patch)
treec406d203b06a1832de0b7d299f9ed0acad60e0be
parentbf37078df2cbea3a44a641ddbe40f11339c135a2 (diff)
downloademacs-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.el74
-rw-r--r--test/manual/indent/js-jsx-unclosed-2.js8
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.
2088Disambiguate JSX from inequality operators and arrow functions by 2092Disambiguate JSX from inequality operators and arrow functions by
2089testing for syntax only valid as JSX." 2093testing 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="" />