diff options
| author | Jackson Ray Hamilton | 2019-02-09 20:06:29 -0800 |
|---|---|---|
| committer | Jackson Ray Hamilton | 2019-04-08 22:48:20 -0700 |
| commit | 4b305bb185596dff5d02cf54da7a41c3e082b7d4 (patch) | |
| tree | 4cf0d618e87fb3bb5cd6de9148e40b9c755e9d0c | |
| parent | 58c77f1f3e041be320a05efb818a0e2bb1583e84 (diff) | |
| download | emacs-4b305bb185596dff5d02cf54da7a41c3e082b7d4.tar.gz emacs-4b305bb185596dff5d02cf54da7a41c3e082b7d4.zip | |
Refactor JSX indentation code to improve enclosing JSX discovery
Fix a number of bugs reported for JSX indentation (caused by poor JSX
detection):
- https://github.com/mooz/js2-mode/issues/140#issuecomment-166250016
- https://github.com/mooz/js2-mode/issues/490
- Bug#24896 / https://github.com/mooz/js2-mode/issues/389 (with
respect to comments)
- Bug#26001 /
https://github.com/mooz/js2-mode/issues/389#issuecomment-271869380
- https://github.com/mooz/js2-mode/issues/411 / Bug#27000 /
https://github.com/mooz/js2-mode/issues/451
Potentially manifest some new bugs (due to false positives with ‘<’
and ‘>’ and SGML detection). Slow down indentation a fair bit.
* list/progmodes/js.el (js-jsx-syntax, js--jsx-start-tag-re)
(js--looking-at-jsx-start-tag-p, js--looking-back-at-jsx-end-tag-p):
New variables and functions.
(js--jsx-find-before-tag, js--jsx-after-tag-re): Deleted.
(js--looking-at-operator-p): Don’t mistake a JSXOpeningElement for the
‘<’ operator.
(js--continued-expression-p): Don’t mistake a JSXClosingElement as a
fragment of a continued expression including the ‘>’ operator.
(js--as-sgml): Simplify. Probably needn’t bind forward-sexp-function
to nil (sgml-mode already does) and probably shouldn’t bind
parse-sexp-lookup-properties to nil either (see Bug#24896).
(js--outermost-enclosing-jsx-tag-pos): Find enclosing JSX more
accurately than js--jsx-find-before-tag. Use sgml-mode’s parsing
logic, rather than unreliable heuristics like paren-wrapping. This
implementation is much slower; the previous implementation was fast,
but at the expense of accuracy. To make up for all the grief we’ve
caused users, we will prefer accuracy over speed from now on. That
said, this can still probably be optimized a lot.
(js--jsx-indented-element-p): Rename to js--jsx-indentation, since it
doesn’t just return a boolean.
(js--jsx-indentation): Refactor js--jsx-indented-element-p to simplify
the implementation as the improved accuracy of other code allows (and
to repent for some awful stylistic choices I made earlier).
(js--expression-in-sgml-indent-line): Rename to
js--indent-line-in-jsx-expression, since it’s a private function and
we can give it a name that reads more like English.
(js--indent-line-in-jsx-expression): Restructure point adjustment
logic more like js-indent-line.
(js--indent-n+1th-jsx-line): New function to complement
js--indent-line-in-jsx-expression.
(js-jsx-indent-line): Refactor. Don’t bind js--continued-expression-p
to ignore any more; instead, rely on the improved accuracy of
js--continued-expression-p.
(js-jsx-mode): Set js-jsx-syntax to t. For now, this will be the flag
we use to determine whether ‘JSX is enabled.’ (Maybe later, we will
refactor the code to use this variable instead of requiring
js-jsx-mode to be enabled, thus rendering the mode obsolete.)
| -rw-r--r-- | lisp/progmodes/js.el | 337 |
1 files changed, 141 insertions, 196 deletions
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 4d91da73340..5b992535a8c 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el | |||
| @@ -572,6 +572,15 @@ then the \".\"s will be lined up: | |||
| 572 | :safe 'booleanp | 572 | :safe 'booleanp |
| 573 | :group 'js) | 573 | :group 'js) |
| 574 | 574 | ||
| 575 | (defcustom js-jsx-syntax nil | ||
| 576 | "When non-nil, parse JavaScript with consideration for JSX syntax. | ||
| 577 | This fixes indentation of JSX code in some cases. It is set to | ||
| 578 | be buffer-local when in `js-jsx-mode'." | ||
| 579 | :version "27.1" | ||
| 580 | :type 'boolean | ||
| 581 | :safe 'booleanp | ||
| 582 | :group 'js) | ||
| 583 | |||
| 575 | ;;; KeyMap | 584 | ;;; KeyMap |
| 576 | 585 | ||
| 577 | (defvar js-mode-map | 586 | (defvar js-mode-map |
| @@ -1774,6 +1783,14 @@ This performs fontification according to `js--class-styles'." | |||
| 1774 | (js--regexp-opt-symbol '("in" "instanceof"))) | 1783 | (js--regexp-opt-symbol '("in" "instanceof"))) |
| 1775 | "Regexp matching operators that affect indentation of continued expressions.") | 1784 | "Regexp matching operators that affect indentation of continued expressions.") |
| 1776 | 1785 | ||
| 1786 | (defconst js--jsx-start-tag-re | ||
| 1787 | (concat "<" sgml-name-re) | ||
| 1788 | "Regexp matching code that looks like a JSXOpeningElement.") | ||
| 1789 | |||
| 1790 | (defun js--looking-at-jsx-start-tag-p () | ||
| 1791 | "Non-nil if a JSXOpeningElement immediately follows point." | ||
| 1792 | (looking-at js--jsx-start-tag-re)) | ||
| 1793 | |||
| 1777 | (defun js--looking-at-operator-p () | 1794 | (defun js--looking-at-operator-p () |
| 1778 | "Return non-nil if point is on a JavaScript operator, other than a comma." | 1795 | "Return non-nil if point is on a JavaScript operator, other than a comma." |
| 1779 | (save-match-data | 1796 | (save-match-data |
| @@ -1796,7 +1813,9 @@ This performs fontification according to `js--class-styles'." | |||
| 1796 | (js--backward-syntactic-ws) | 1813 | (js--backward-syntactic-ws) |
| 1797 | ;; We might misindent some expressions that would | 1814 | ;; We might misindent some expressions that would |
| 1798 | ;; return NaN anyway. Shouldn't be a problem. | 1815 | ;; return NaN anyway. Shouldn't be a problem. |
| 1799 | (memq (char-before) '(?, ?} ?{)))))))) | 1816 | (memq (char-before) '(?, ?} ?{))))) |
| 1817 | ;; “<” isn’t necessarily an operator in JSX. | ||
| 1818 | (not (and js-jsx-syntax (js--looking-at-jsx-start-tag-p)))))) | ||
| 1800 | 1819 | ||
| 1801 | (defun js--find-newline-backward () | 1820 | (defun js--find-newline-backward () |
| 1802 | "Move backward to the nearest newline that is not in a block comment." | 1821 | "Move backward to the nearest newline that is not in a block comment." |
| @@ -1816,6 +1835,14 @@ This performs fontification according to `js--class-styles'." | |||
| 1816 | (setq result nil))) | 1835 | (setq result nil))) |
| 1817 | result)) | 1836 | result)) |
| 1818 | 1837 | ||
| 1838 | (defconst js--jsx-end-tag-re | ||
| 1839 | (concat "</" sgml-name-re ">\\|/>") | ||
| 1840 | "Regexp matching a JSXClosingElement.") | ||
| 1841 | |||
| 1842 | (defun js--looking-back-at-jsx-end-tag-p () | ||
| 1843 | "Non-nil if a JSXClosingElement immediately precedes point." | ||
| 1844 | (looking-back js--jsx-end-tag-re (point-at-bol))) | ||
| 1845 | |||
| 1819 | (defun js--continued-expression-p () | 1846 | (defun js--continued-expression-p () |
| 1820 | "Return non-nil if the current line continues an expression." | 1847 | "Return non-nil if the current line continues an expression." |
| 1821 | (save-excursion | 1848 | (save-excursion |
| @@ -1833,12 +1860,19 @@ This performs fontification according to `js--class-styles'." | |||
| 1833 | (and (js--find-newline-backward) | 1860 | (and (js--find-newline-backward) |
| 1834 | (progn | 1861 | (progn |
| 1835 | (skip-chars-backward " \t") | 1862 | (skip-chars-backward " \t") |
| 1836 | (or (bobp) (backward-char)) | 1863 | (and |
| 1837 | (and (> (point) (point-min)) | 1864 | ;; The “>” at the end of any JSXBoundaryElement isn’t |
| 1838 | (save-excursion (backward-char) (not (looking-at "[/*]/\\|=>"))) | 1865 | ;; part of a continued expression. |
| 1839 | (js--looking-at-operator-p) | 1866 | (not (and js-jsx-syntax (js--looking-back-at-jsx-end-tag-p))) |
| 1840 | (and (progn (backward-char) | 1867 | (progn |
| 1841 | (not (looking-at "\\+\\+\\|--\\|/[/*]")))))))))) | 1868 | (or (bobp) (backward-char)) |
| 1869 | (and (> (point) (point-min)) | ||
| 1870 | (save-excursion | ||
| 1871 | (backward-char) | ||
| 1872 | (not (looking-at "[/*]/\\|=>"))) | ||
| 1873 | (js--looking-at-operator-p) | ||
| 1874 | (and (progn (backward-char) | ||
| 1875 | (not (looking-at "\\+\\+\\|--\\|/[/*]")))))))))))) | ||
| 1842 | 1876 | ||
| 1843 | (defun js--skip-term-backward () | 1877 | (defun js--skip-term-backward () |
| 1844 | "Skip a term before point; return t if a term was skipped." | 1878 | "Skip a term before point; return t if a term was skipped." |
| @@ -2153,190 +2187,108 @@ current line is the \"=>\" token." | |||
| 2153 | 2187 | ||
| 2154 | ;;; JSX Indentation | 2188 | ;;; JSX Indentation |
| 2155 | 2189 | ||
| 2156 | (defsubst js--jsx-find-before-tag () | 2190 | (defmacro js--as-sgml (&rest body) |
| 2157 | "Find where JSX starts. | 2191 | "Execute BODY as if in sgml-mode." |
| 2158 | 2192 | `(with-syntax-table sgml-mode-syntax-table | |
| 2159 | Assume JSX appears in the following instances: | 2193 | ,@body)) |
| 2160 | - Inside parentheses, when returned or as the first argument | 2194 | |
| 2161 | to a function, and after a newline | 2195 | (defun js--outermost-enclosing-jsx-tag-pos () |
| 2162 | - When assigned to variables or object properties, but only | 2196 | (let (context tag-pos last-tag-pos parse-status parens paren-pos curly-pos) |
| 2163 | on a single line | 2197 | (js--as-sgml |
| 2164 | - As the N+1th argument to a function | 2198 | ;; Search until we reach the top or encounter the start of a |
| 2165 | 2199 | ;; JSXExpressionContainer (implying nested JSX). | |
| 2166 | This is an optimized version of (re-search-backward \"[(,]\n\" | 2200 | (while (and (setq context (sgml-get-context)) |
| 2167 | nil t), except set point to the end of the match. This logic | 2201 | (progn |
| 2168 | executes up to the number of lines in the file, so it should be | 2202 | (setq tag-pos (sgml-tag-start (car (last context)))) |
| 2169 | really fast to reduce that impact." | 2203 | (or (not curly-pos) |
| 2170 | (let (pos) | 2204 | ;; Stop before curly brackets (start of a |
| 2171 | (while (and (> (point) (point-min)) | 2205 | ;; JSXExpressionContainer). |
| 2172 | (not (progn | 2206 | (> tag-pos curly-pos)))) |
| 2173 | (end-of-line 0) | 2207 | ;; Record this position so it can potentially be returned. |
| 2174 | (when (or (eq (char-before) 40) ; ( | 2208 | (setq last-tag-pos tag-pos) |
| 2175 | (eq (char-before) 44)) ; , | 2209 | ;; Always parse sexps / search for the next context from the |
| 2176 | (setq pos (1- (point)))))))) | 2210 | ;; immediately enclosing tag (sgml-get-context may not leave |
| 2177 | pos)) | 2211 | ;; point there). |
| 2178 | 2212 | (goto-char tag-pos) | |
| 2179 | (defconst js--jsx-end-tag-re | 2213 | (unless parse-status ; Don’t needlessly reparse. |
| 2180 | (concat "</" sgml-name-re ">\\|/>") | 2214 | ;; Search upward for an enclosing starting curly bracket. |
| 2181 | "Find the end of a JSX element.") | 2215 | (setq parse-status (syntax-ppss)) |
| 2182 | 2216 | (setq parens (reverse (nth 9 parse-status))) | |
| 2183 | (defconst js--jsx-after-tag-re "[),]" | 2217 | (while (and (setq paren-pos (car parens)) |
| 2184 | "Find where JSX ends. | 2218 | (not (when (= (char-after paren-pos) ?{) |
| 2185 | This complements the assumption of where JSX appears from | 2219 | (setq curly-pos paren-pos)))) |
| 2186 | `js--jsx-before-tag-re', which see.") | 2220 | (setq parens (cdr parens))) |
| 2187 | 2221 | ;; Always search for the next context from the immediately | |
| 2188 | (defun js--jsx-indented-element-p () | 2222 | ;; enclosing tag (calling syntax-ppss in the above loop |
| 2223 | ;; may move point from there). | ||
| 2224 | (goto-char tag-pos)))) | ||
| 2225 | last-tag-pos)) | ||
| 2226 | |||
| 2227 | (defun js--jsx-indentation () | ||
| 2189 | "Determine if/how the current line should be indented as JSX. | 2228 | "Determine if/how the current line should be indented as JSX. |
| 2190 | 2229 | ||
| 2191 | Return `first' for the first JSXElement on its own line. | 2230 | Return nil for first JSXElement line (indent like JS). |
| 2192 | Return `nth' for subsequent lines of the first JSXElement. | 2231 | Return `n+1th' for second+ JSXElement lines (indent like SGML). |
| 2193 | Return `expression' for an embedded JS expression. | 2232 | Return `expression' for lines within embedded JS expressions |
| 2194 | Return `after' for anything after the last JSXElement. | 2233 | (indent like JS inside SGML). |
| 2195 | Return nil for non-JSX lines. | 2234 | Return nil for non-JSX lines." |
| 2196 | |||
| 2197 | Currently, JSX indentation supports the following styles: | ||
| 2198 | |||
| 2199 | - Single-line elements (indented like normal JS): | ||
| 2200 | |||
| 2201 | var element = <div></div>; | ||
| 2202 | |||
| 2203 | - Multi-line elements (enclosed in parentheses): | ||
| 2204 | |||
| 2205 | function () { | ||
| 2206 | return ( | ||
| 2207 | <div> | ||
| 2208 | <div></div> | ||
| 2209 | </div> | ||
| 2210 | ); | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | - Function arguments: | ||
| 2214 | |||
| 2215 | React.render( | ||
| 2216 | <div></div>, | ||
| 2217 | document.querySelector('.root') | ||
| 2218 | );" | ||
| 2219 | (let ((current-pos (point)) | 2235 | (let ((current-pos (point)) |
| 2220 | (current-line (line-number-at-pos)) | 2236 | (current-line (line-number-at-pos)) |
| 2221 | last-pos | 2237 | tag-start-pos parens paren type) |
| 2222 | before-tag-pos before-tag-line | ||
| 2223 | tag-start-pos tag-start-line | ||
| 2224 | tag-end-pos tag-end-line | ||
| 2225 | after-tag-line | ||
| 2226 | parens paren type) | ||
| 2227 | (save-excursion | 2238 | (save-excursion |
| 2228 | (and | 2239 | ;; Determine if inside a JSXElement. |
| 2229 | ;; Determine if we're inside a jsx element | 2240 | (beginning-of-line) ; For exclusivity |
| 2230 | (progn | 2241 | (when (setq tag-start-pos (js--outermost-enclosing-jsx-tag-pos)) |
| 2231 | (end-of-line) | 2242 | ;; Check if inside an embedded multi-line JS expression. |
| 2232 | (while (and (not tag-start-pos) | 2243 | (goto-char current-pos) |
| 2233 | (setq last-pos (js--jsx-find-before-tag))) | 2244 | (end-of-line) ; For exclusivity |
| 2234 | (while (forward-comment 1)) | 2245 | (setq parens (nth 9 (syntax-ppss))) |
| 2235 | (when (= (char-after) 60) ; < | 2246 | (while |
| 2236 | (setq before-tag-pos last-pos | 2247 | (and |
| 2237 | tag-start-pos (point))) | 2248 | (setq paren (car parens)) |
| 2238 | (goto-char last-pos)) | 2249 | (if (and |
| 2239 | tag-start-pos) | 2250 | (>= paren tag-start-pos) |
| 2240 | (progn | 2251 | ;; A curly bracket indicates the start of an |
| 2241 | (setq before-tag-line (line-number-at-pos before-tag-pos) | 2252 | ;; embedded expression. |
| 2242 | tag-start-line (line-number-at-pos tag-start-pos)) | 2253 | (= (char-after paren) ?{) |
| 2243 | (and | 2254 | ;; The first line of the expression is indented |
| 2244 | ;; A "before" line which also starts an element begins with js, so | 2255 | ;; like SGML. |
| 2245 | ;; indent it like js | ||
| 2246 | (> current-line before-tag-line) | ||
| 2247 | ;; Only indent the jsx lines like jsx | ||
| 2248 | (>= current-line tag-start-line))) | ||
| 2249 | (cond | ||
| 2250 | ;; Analyze bounds if there are any | ||
| 2251 | ((progn | ||
| 2252 | (while (and (not tag-end-pos) | ||
| 2253 | (setq last-pos (re-search-forward js--jsx-end-tag-re nil t))) | ||
| 2254 | (while (forward-comment 1)) | ||
| 2255 | (when (looking-at js--jsx-after-tag-re) | ||
| 2256 | (setq tag-end-pos last-pos))) | ||
| 2257 | tag-end-pos) | ||
| 2258 | (setq tag-end-line (line-number-at-pos tag-end-pos) | ||
| 2259 | after-tag-line (line-number-at-pos after-tag-line)) | ||
| 2260 | (or (and | ||
| 2261 | ;; Ensure we're actually within the bounds of the jsx | ||
| 2262 | (<= current-line tag-end-line) | ||
| 2263 | ;; An "after" line which does not end an element begins with | ||
| 2264 | ;; js, so indent it like js | ||
| 2265 | (<= current-line after-tag-line)) | ||
| 2266 | (and | ||
| 2267 | ;; Handle another case where there could be e.g. comments after | ||
| 2268 | ;; the element | ||
| 2269 | (> current-line tag-end-line) | ||
| 2270 | (< current-line after-tag-line) | ||
| 2271 | (setq type 'after)))) | ||
| 2272 | ;; They may not be any bounds (yet) | ||
| 2273 | (t)) | ||
| 2274 | ;; Check if we're inside an embedded multi-line js expression | ||
| 2275 | (cond | ||
| 2276 | ((not type) | ||
| 2277 | (goto-char current-pos) | ||
| 2278 | (end-of-line) | ||
| 2279 | (setq parens (nth 9 (syntax-ppss))) | ||
| 2280 | (while (and parens (not type)) | ||
| 2281 | (setq paren (car parens)) | ||
| 2282 | (cond | ||
| 2283 | ((and (>= paren tag-start-pos) | ||
| 2284 | ;; Curly bracket indicates the start of an embedded expression | ||
| 2285 | (= (char-after paren) 123) ; { | ||
| 2286 | ;; The first line of the expression is indented like sgml | ||
| 2287 | (> current-line (line-number-at-pos paren)) | 2256 | (> current-line (line-number-at-pos paren)) |
| 2288 | ;; Check if within a closing curly bracket (if any) | 2257 | ;; Check if within a closing curly bracket (if any) |
| 2289 | ;; (exclusive, as the closing bracket is indented like sgml) | 2258 | ;; (exclusive, as the closing bracket is indented |
| 2290 | (cond | 2259 | ;; like SGML). |
| 2291 | ((progn | 2260 | (if (progn |
| 2292 | (goto-char paren) | 2261 | (goto-char paren) |
| 2293 | (ignore-errors (let (forward-sexp-function) | 2262 | (ignore-errors (let (forward-sexp-function) |
| 2294 | (forward-sexp)))) | 2263 | (forward-sexp)))) |
| 2295 | (< current-line (line-number-at-pos))) | 2264 | (< current-line (line-number-at-pos)) |
| 2296 | (t))) | 2265 | ;; No matching bracket implies we’re inside! |
| 2297 | ;; Indicate this guy will be indented specially | 2266 | t)) |
| 2298 | (setq type 'expression)) | 2267 | ;; Indicate this will be indented specially. Return |
| 2299 | (t (setq parens (cdr parens))))) | 2268 | ;; nil to stop iterating too. |
| 2300 | t) | 2269 | (progn (setq type 'expression) nil) |
| 2301 | (t)) | 2270 | ;; Stop iterating when parens = nil. |
| 2302 | (cond | 2271 | (setq parens (cdr parens))))) |
| 2303 | (type) | 2272 | (or type 'n+1th))))) |
| 2304 | ;; Indent the first jsx thing like js so we can indent future jsx things | 2273 | |
| 2305 | ;; like sgml relative to the first thing | 2274 | (defun js--indent-line-in-jsx-expression () |
| 2306 | ((= current-line tag-start-line) 'first) | 2275 | "Indent the current line as JavaScript within JSX." |
| 2307 | ('nth)))))) | 2276 | (let ((parse-status (save-excursion (syntax-ppss (point-at-bol)))) |
| 2308 | 2277 | offset indent-col) | |
| 2309 | (defmacro js--as-sgml (&rest body) | ||
| 2310 | "Execute BODY as if in sgml-mode." | ||
| 2311 | `(with-syntax-table sgml-mode-syntax-table | ||
| 2312 | (let (forward-sexp-function | ||
| 2313 | parse-sexp-lookup-properties) | ||
| 2314 | ,@body))) | ||
| 2315 | |||
| 2316 | (defun js--expression-in-sgml-indent-line () | ||
| 2317 | "Indent the current line as JavaScript or SGML (whichever is farther)." | ||
| 2318 | (let* (indent-col | ||
| 2319 | (savep (point)) | ||
| 2320 | ;; Don't whine about errors/warnings when we're indenting. | ||
| 2321 | ;; This has to be set before calling parse-partial-sexp below. | ||
| 2322 | (inhibit-point-motion-hooks t) | ||
| 2323 | (parse-status (save-excursion | ||
| 2324 | (syntax-ppss (point-at-bol))))) | ||
| 2325 | ;; Don't touch multiline strings. | ||
| 2326 | (unless (nth 3 parse-status) | 2278 | (unless (nth 3 parse-status) |
| 2327 | (setq indent-col (save-excursion | 2279 | (save-excursion |
| 2328 | (back-to-indentation) | 2280 | (setq offset (- (point) (progn (back-to-indentation) (point))) |
| 2329 | (if (>= (point) savep) (setq savep nil)) | 2281 | indent-col (js--as-sgml (sgml-calculate-indent)))) |
| 2330 | (js--as-sgml (sgml-calculate-indent)))) | 2282 | (if (null indent-col) 'noindent ; Like in sgml-mode |
| 2331 | (if (null indent-col) | 2283 | ;; Use whichever indentation column is greater, such that the |
| 2332 | 'noindent | 2284 | ;; SGML column is effectively a minimum. |
| 2333 | ;; Use whichever indentation column is greater, such that the sgml | 2285 | (indent-line-to (max (js--proper-indentation parse-status) |
| 2334 | ;; column is effectively a minimum | 2286 | (+ indent-col js-indent-level))) |
| 2335 | (setq indent-col (max (js--proper-indentation parse-status) | 2287 | (when (> offset 0) (forward-char offset)))))) |
| 2336 | (+ indent-col js-indent-level))) | 2288 | |
| 2337 | (if savep | 2289 | (defun js--indent-n+1th-jsx-line () |
| 2338 | (save-excursion (indent-line-to indent-col)) | 2290 | "Indent the current line as JSX within JavaScript." |
| 2339 | (indent-line-to indent-col)))))) | 2291 | (js--as-sgml (sgml-indent-line))) |
| 2340 | 2292 | ||
| 2341 | (defun js-indent-line () | 2293 | (defun js-indent-line () |
| 2342 | "Indent the current line as JavaScript." | 2294 | "Indent the current line as JavaScript." |
| @@ -2353,19 +2305,11 @@ Currently, JSX indentation supports the following styles: | |||
| 2353 | i.e., customize JSX element indentation with `sgml-basic-offset', | 2305 | i.e., customize JSX element indentation with `sgml-basic-offset', |
| 2354 | `sgml-attribute-offset' et al." | 2306 | `sgml-attribute-offset' et al." |
| 2355 | (interactive) | 2307 | (interactive) |
| 2356 | (let ((indentation-type (js--jsx-indented-element-p))) | 2308 | (let ((type (js--jsx-indentation))) |
| 2357 | (cond | 2309 | (if type |
| 2358 | ((eq indentation-type 'expression) | 2310 | (if (eq type 'n+1th) (js--indent-n+1th-jsx-line) |
| 2359 | (js--expression-in-sgml-indent-line)) | 2311 | (js--indent-line-in-jsx-expression)) |
| 2360 | ((or (eq indentation-type 'first) | 2312 | (js-indent-line)))) |
| 2361 | (eq indentation-type 'after)) | ||
| 2362 | ;; Don't treat this first thing as a continued expression (often a "<" or | ||
| 2363 | ;; ">" causes this misinterpretation) | ||
| 2364 | (cl-letf (((symbol-function #'js--continued-expression-p) 'ignore)) | ||
| 2365 | (js-indent-line))) | ||
| 2366 | ((eq indentation-type 'nth) | ||
| 2367 | (js--as-sgml (sgml-indent-line))) | ||
| 2368 | (t (js-indent-line))))) | ||
| 2369 | 2313 | ||
| 2370 | ;;; Filling | 2314 | ;;; Filling |
| 2371 | 2315 | ||
| @@ -3944,6 +3888,7 @@ locally, like so: | |||
| 3944 | (setq-local sgml-basic-offset js-indent-level)) | 3888 | (setq-local sgml-basic-offset js-indent-level)) |
| 3945 | (add-hook \\='js-jsx-mode-hook #\\='set-jsx-indentation)" | 3889 | (add-hook \\='js-jsx-mode-hook #\\='set-jsx-indentation)" |
| 3946 | :group 'js | 3890 | :group 'js |
| 3891 | (setq-local js-jsx-syntax t) | ||
| 3947 | (setq-local indent-line-function #'js-jsx-indent-line)) | 3892 | (setq-local indent-line-function #'js-jsx-indent-line)) |
| 3948 | 3893 | ||
| 3949 | ;;;###autoload (defalias 'javascript-mode 'js-mode) | 3894 | ;;;###autoload (defalias 'javascript-mode 'js-mode) |