]> code.delx.au - gnu-emacs-elpa/blob - js2-old-indent.el
Add NEWS entry for js2-jsx-mode
[gnu-emacs-elpa] / js2-old-indent.el
1 ;;; js2-old-indent.el --- Indentation code kept for compatibility
2
3 ;; Copyright (C) 2015 Free Software Foundation, Inc.
4
5 ;; This file is part of GNU Emacs.
6
7 ;; GNU Emacs is free software: you can redistribute it and/or modify
8 ;; it under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation, either version 3 of the License, or
10 ;; (at your option) any later version.
11
12 ;; GNU Emacs is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details.
16
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
19
20 ;;; Commentary:
21
22 ;; All features of this indentation code have been ported to Emacs's
23 ;; built-in `js-mode' by now, so we derive from it. An older
24 ;; commentary follows.
25
26 ;; This code is kept for Emacs 24.5 and ealier.
27
28 ;; This indenter is based on Karl Landström's "javascript.el" indenter.
29 ;; Karl cleverly deduces that the desired indentation level is often a
30 ;; function of paren/bracket/brace nesting depth, which can be determined
31 ;; quickly via the built-in `parse-partial-sexp' function. His indenter
32 ;; then does some equally clever checks to see if we're in the context of a
33 ;; substatement of a possibly braceless statement keyword such as if, while,
34 ;; or finally. This approach yields pretty good results.
35
36 ;; The indenter is often "wrong", however, and needs to be overridden.
37 ;; The right long-term solution is probably to emulate (or integrate
38 ;; with) cc-engine, but it's a nontrivial amount of coding. Even when a
39 ;; parse tree from `js2-parse' is present, which is not true at the
40 ;; moment the user is typing, computing indentation is still thousands
41 ;; of lines of code to handle every possible syntactic edge case.
42
43 ;; In the meantime, the compromise solution is that we offer a "bounce
44 ;; indenter", configured with `js2-bounce-indent-p', which cycles the
45 ;; current line indent among various likely guess points. This approach
46 ;; is far from perfect, but should at least make it slightly easier to
47 ;; move the line towards its desired indentation when manually
48 ;; overriding Karl's heuristic nesting guesser.
49
50 ;; I've made miscellaneous tweaks to Karl's code to handle some Ecma
51 ;; extensions such as `let' and Array comprehensions. Major kudos to
52 ;; Karl for coming up with the initial approach, which packs a lot of
53 ;; punch for so little code. -- Steve
54
55 ;;; Code:
56
57 (require 'sgml-mode)
58
59 (defvar js2-language-version)
60
61 (declare-function js2-mark-safe-local "js2-mode")
62 (declare-function js2-backward-sws "js2-mode")
63 (declare-function js2-forward-sws "js2-mode")
64 (declare-function js2-same-line "js2-mode")
65
66 (defcustom js2-basic-offset (if (and (boundp 'c-basic-offset)
67 (numberp c-basic-offset))
68 c-basic-offset
69 4)
70 "Number of spaces to indent nested statements.
71 Similar to `c-basic-offset'."
72 :group 'js2-mode
73 :safe 'integerp
74 :type 'integer)
75
76 (defcustom js2-pretty-multiline-declarations t
77 "Non-nil to line up multiline declarations vertically:
78
79 var a = 10,
80 b = 20,
81 c = 30;
82
83 If the value is t, and the first assigned value in the
84 declaration is a function/array/object literal spanning several
85 lines, it won't be indented additionally:
86
87 var o = { var bar = 2,
88 foo: 3 vs. o = {
89 }, foo: 3
90 bar = 2; };
91
92 If the value is `all', it will always be indented additionally:
93
94 var o = {
95 foo: 3
96 };
97
98 var o = {
99 foo: 3
100 },
101 bar = 2;
102
103 If the value is `dynamic', it will be indented additionally only
104 if the declaration contains more than one variable:
105
106 var o = {
107 foo: 3
108 };
109
110 var o = {
111 foo: 3
112 },
113 bar = 2;"
114 :group 'js2-mode
115 :safe 'symbolp
116 :type 'symbol)
117
118 (defcustom js2-indent-switch-body nil
119 "When nil, case labels are indented on the same level as the
120 containing switch statement. Otherwise, all lines inside
121 switch statement body are indented one additional level."
122 :type 'boolean
123 :safe 'booleanp
124 :group 'js2-mode)
125
126 (defconst js2-possibly-braceless-keywords-re
127 (concat "else[ \t]+if\\|for[ \t]+each\\|"
128 (regexp-opt '("catch" "do" "else" "finally" "for" "if"
129 "try" "while" "with" "let")))
130 "Regular expression matching keywords that are optionally
131 followed by an opening brace.")
132
133 (defconst js2-indent-operator-re
134 (concat "[-+*/%<>&^|?:.]\\([^-+*/]\\|$\\)\\|!?=\\|"
135 (regexp-opt '("in" "instanceof") 'words))
136 "Regular expression matching operators that affect indentation
137 of continued expressions.")
138
139 (defconst js2-declaration-keyword-re
140 (regexp-opt '("var" "let" "const") 'words)
141 "Regular expression matching variable declaration keywords.")
142
143 (defun js2-re-search-forward-inner (regexp &optional bound count)
144 "Auxiliary function for `js2-re-search-forward'."
145 (let (parse saved-point)
146 (while (> count 0)
147 (re-search-forward regexp bound)
148 (setq parse (if saved-point
149 (parse-partial-sexp saved-point (point))
150 (syntax-ppss (point))))
151 (cond ((nth 3 parse)
152 (re-search-forward
153 (concat "\\(\\=\\|[^\\]\\|^\\)" (string (nth 3 parse)))
154 (save-excursion (end-of-line) (point)) t))
155 ((nth 7 parse)
156 (forward-line))
157 ((or (nth 4 parse)
158 (and (eq (char-before) ?\/) (eq (char-after) ?\*)))
159 (re-search-forward "\\*/"))
160 (t
161 (setq count (1- count))))
162 (setq saved-point (point))))
163 (point))
164
165 (defun js2-re-search-forward (regexp &optional bound noerror count)
166 "Search forward but ignore strings and comments.
167 Invokes `re-search-forward' but treats the buffer as if strings
168 and comments have been removed."
169 (let ((saved-point (point)))
170 (condition-case err
171 (cond ((null count)
172 (js2-re-search-forward-inner regexp bound 1))
173 ((< count 0)
174 (js2-re-search-backward-inner regexp bound (- count)))
175 ((> count 0)
176 (js2-re-search-forward-inner regexp bound count)))
177 (search-failed
178 (goto-char saved-point)
179 (unless noerror
180 (error (error-message-string err)))))))
181
182 (defun js2-re-search-backward-inner (regexp &optional bound count)
183 "Auxiliary function for `js2-re-search-backward'."
184 (let (parse)
185 (while (> count 0)
186 (re-search-backward regexp bound)
187 (setq parse (syntax-ppss (point)))
188 (cond ((nth 3 parse)
189 (re-search-backward
190 (concat "\\([^\\]\\|^\\)" (string (nth 3 parse)))
191 (line-beginning-position) t))
192 ((nth 7 parse)
193 (goto-char (nth 8 parse)))
194 ((or (nth 4 parse)
195 (and (eq (char-before) ?/) (eq (char-after) ?*)))
196 (re-search-backward "/\\*"))
197 (t
198 (setq count (1- count))))))
199 (point))
200
201 (defun js2-re-search-backward (regexp &optional bound noerror count)
202 "Search backward but ignore strings and comments.
203 Invokes `re-search-backward' but treats the buffer as if strings
204 and comments have been removed."
205 (let ((saved-point (point)))
206 (condition-case err
207 (cond ((null count)
208 (js2-re-search-backward-inner regexp bound 1))
209 ((< count 0)
210 (js2-re-search-forward-inner regexp bound (- count)))
211 ((> count 0)
212 (js2-re-search-backward-inner regexp bound count)))
213 (search-failed
214 (goto-char saved-point)
215 (unless noerror
216 (error (error-message-string err)))))))
217
218 (defun js2-looking-at-operator-p ()
219 "Return non-nil if text after point is a non-comma operator."
220 (defvar js2-mode-identifier-re)
221 (and (looking-at js2-indent-operator-re)
222 (or (not (eq (char-after) ?:))
223 (save-excursion
224 (and (js2-re-search-backward "[?:{]\\|\\_<case\\_>" nil t)
225 (eq (char-after) ??))))
226 (not (and
227 (eq (char-after) ?*)
228 (looking-at (concat "\\* *" js2-mode-identifier-re " *("))
229 (save-excursion
230 (goto-char (1- (match-end 0)))
231 (let (forward-sexp-function) (forward-sexp))
232 (js2-forward-sws)
233 (eq (char-after) ?{))))))
234
235 (defun js2-continued-expression-p ()
236 "Return non-nil if the current line continues an expression."
237 (save-excursion
238 (back-to-indentation)
239 (or (js2-looking-at-operator-p)
240 (when (catch 'found
241 (while (and (re-search-backward "\n" nil t)
242 (let ((state (syntax-ppss)))
243 (when (nth 4 state)
244 (goto-char (nth 8 state))) ;; skip comments
245 (skip-chars-backward " \t")
246 (if (bolp)
247 t
248 (throw 'found t))))))
249 (backward-char)
250 (when (js2-looking-at-operator-p)
251 (backward-char)
252 (not (looking-at "\\*\\|\\+\\+\\|--\\|/[/*]")))))))
253
254 (defun js2-end-of-do-while-loop-p ()
255 "Return non-nil if word after point is `while' of a do-while
256 statement, else returns nil. A braceless do-while statement
257 spanning several lines requires that the start of the loop is
258 indented to the same column as the current line."
259 (interactive)
260 (save-excursion
261 (when (looking-at "\\s-*\\_<while\\_>")
262 (if (save-excursion
263 (skip-chars-backward "[ \t\n]*}")
264 (looking-at "[ \t\n]*}"))
265 (save-excursion
266 (backward-list) (backward-word 1) (looking-at "\\_<do\\_>"))
267 (js2-re-search-backward "\\_<do\\_>" (point-at-bol) t)
268 (or (looking-at "\\_<do\\_>")
269 (let ((saved-indent (current-indentation)))
270 (while (and (js2-re-search-backward "^[ \t]*\\_<" nil t)
271 (/= (current-indentation) saved-indent)))
272 (and (looking-at "[ \t]*\\_<do\\_>")
273 (not (js2-re-search-forward
274 "\\_<while\\_>" (point-at-eol) t))
275 (= (current-indentation) saved-indent))))))))
276
277 (defun js2-multiline-decl-indentation ()
278 "Return the declaration indentation column if the current line belongs
279 to a multiline declaration statement. See `js2-pretty-multiline-declarations'."
280 (let (forward-sexp-function ; use Lisp version
281 at-opening-bracket)
282 (save-excursion
283 (back-to-indentation)
284 (when (not (looking-at js2-declaration-keyword-re))
285 (when (looking-at js2-indent-operator-re)
286 (goto-char (match-end 0))) ; continued expressions are ok
287 (while (and (not at-opening-bracket)
288 (not (bobp))
289 (let ((pos (point)))
290 (save-excursion
291 (js2-backward-sws)
292 (or (eq (char-before) ?,)
293 (and (not (eq (char-before) ?\;))
294 (prog2 (skip-syntax-backward ".")
295 (looking-at js2-indent-operator-re)
296 (js2-backward-sws))
297 (not (eq (char-before) ?\;)))
298 (js2-same-line pos)))))
299 (condition-case _
300 (backward-sexp)
301 (scan-error (setq at-opening-bracket t))))
302 (when (looking-at js2-declaration-keyword-re)
303 (goto-char (match-end 0))
304 (1+ (current-column)))))))
305
306 (defun js2-ctrl-statement-indentation ()
307 "Return the proper indentation of current line if it is a control statement.
308 Returns an indentation if this line starts the body of a control
309 statement without braces, else returns nil."
310 (let (forward-sexp-function)
311 (save-excursion
312 (back-to-indentation)
313 (when (and (not (js2-same-line (point-min)))
314 (not (looking-at "{"))
315 (js2-re-search-backward "[[:graph:]]" nil t)
316 (not (looking-at "[{([]"))
317 (progn
318 (forward-char)
319 (when (= (char-before) ?\))
320 ;; scan-sexps sometimes throws an error
321 (ignore-errors (backward-sexp))
322 (skip-chars-backward " \t" (point-at-bol)))
323 (let ((pt (point)))
324 (back-to-indentation)
325 (when (looking-at "}[ \t]*")
326 (goto-char (match-end 0)))
327 (and (looking-at js2-possibly-braceless-keywords-re)
328 (= (match-end 0) pt)
329 (not (js2-end-of-do-while-loop-p))))))
330 (+ (current-indentation) js2-basic-offset)))))
331
332 (defun js2-indent-in-array-comp (parse-status)
333 "Return non-nil if we think we're in an array comprehension.
334 In particular, return the buffer position of the first `for' kwd."
335 (let ((bracket (nth 1 parse-status))
336 (end (point)))
337 (when bracket
338 (save-excursion
339 (goto-char bracket)
340 (when (looking-at "\\[")
341 (forward-char 1)
342 (js2-forward-sws)
343 (if (looking-at "[[{]")
344 (let (forward-sexp-function) ; use Lisp version
345 (forward-sexp) ; skip destructuring form
346 (js2-forward-sws)
347 (if (and (/= (char-after) ?,) ; regular array
348 (looking-at "for"))
349 (match-beginning 0)))
350 ;; to skip arbitrary expressions we need the parser,
351 ;; so we'll just guess at it.
352 (if (and (> end (point)) ; not empty literal
353 (re-search-forward "[^,]]* \\(for\\) " end t)
354 ;; not inside comment or string literal
355 (let ((state (parse-partial-sexp bracket (point))))
356 (not (or (nth 3 state) (nth 4 state)))))
357 (match-beginning 1))))))))
358
359 (defun js2-array-comp-indentation (parse-status for-kwd)
360 (if (js2-same-line for-kwd)
361 ;; first continuation line
362 (save-excursion
363 (goto-char (nth 1 parse-status))
364 (forward-char 1)
365 (skip-chars-forward " \t")
366 (current-column))
367 (save-excursion
368 (goto-char for-kwd)
369 (current-column))))
370
371 (defun js2-maybe-goto-declaration-keyword-end (bracket)
372 "Helper function for `js2-proper-indentation'.
373 Depending on the value of `js2-pretty-multiline-declarations',
374 move point to the end of a variable declaration keyword so that
375 indentation is aligned to that column."
376 (cond
377 ((eq js2-pretty-multiline-declarations 'all)
378 (when (looking-at js2-declaration-keyword-re)
379 (goto-char (1+ (match-end 0)))))
380 ((eq js2-pretty-multiline-declarations 'dynamic)
381 (let (declaration-keyword-end
382 at-closing-bracket-p
383 comma-p)
384 (when (looking-at js2-declaration-keyword-re)
385 ;; Preserve the match data lest it somehow be overridden.
386 (setq declaration-keyword-end (match-end 0))
387 (save-excursion
388 (goto-char bracket)
389 (setq at-closing-bracket-p
390 ;; Handle scan errors gracefully.
391 (condition-case nil
392 (progn
393 ;; Use the regular `forward-sexp-function' because the
394 ;; normal one for this mode uses the AST.
395 (let (forward-sexp-function)
396 (forward-sexp))
397 t)
398 (error nil)))
399 (when at-closing-bracket-p
400 (js2-forward-sws)
401 (setq comma-p (looking-at-p ","))))
402 (when comma-p
403 (goto-char (1+ declaration-keyword-end))))))))
404
405 (cl-defun js2-proper-indentation (parse-status)
406 "Return the proper indentation for the current line."
407 (save-excursion
408 (back-to-indentation)
409 (when (nth 4 parse-status)
410 (cl-return-from js2-proper-indentation (js2--comment-indent parse-status)))
411 (let* ((at-closing-bracket (looking-at "[]})]"))
412 (same-indent-p (or at-closing-bracket
413 (looking-at "\\_<case\\_>[^:]")
414 (and (looking-at "\\_<default:")
415 (save-excursion
416 (js2-backward-sws)
417 (not (memq (char-before) '(?, ?{)))))))
418 (continued-expr-p (js2-continued-expression-p))
419 (declaration-indent (and js2-pretty-multiline-declarations
420 (js2-multiline-decl-indentation)))
421 (bracket (nth 1 parse-status))
422 beg indent)
423 (cond
424 ;; indent array comprehension continuation lines specially
425 ((and bracket
426 (>= js2-language-version 170)
427 (not (js2-same-line bracket))
428 (setq beg (js2-indent-in-array-comp parse-status))
429 (>= (point) (save-excursion
430 (goto-char beg)
431 (point-at-bol)))) ; at or after first loop?
432 (js2-array-comp-indentation parse-status beg))
433
434 ((js2-ctrl-statement-indentation))
435
436 ((and declaration-indent continued-expr-p)
437 (+ declaration-indent js2-basic-offset))
438
439 (declaration-indent)
440
441 (bracket
442 (goto-char bracket)
443 (cond
444 ((looking-at "[({[][ \t]*\\(/[/*]\\|$\\)")
445 (when (save-excursion (skip-chars-backward " \t\n)")
446 (looking-at ")"))
447 (backward-list))
448 (back-to-indentation)
449 (js2-maybe-goto-declaration-keyword-end bracket)
450 (setq indent
451 (cond (same-indent-p
452 (current-column))
453 (continued-expr-p
454 (+ (current-column) (* 2 js2-basic-offset)))
455 (t
456 (+ (current-column) js2-basic-offset))))
457 (if (and js2-indent-switch-body
458 (not at-closing-bracket)
459 (looking-at "\\_<switch\\_>"))
460 (+ indent js2-basic-offset)
461 indent))
462 (t
463 (unless same-indent-p
464 (forward-char)
465 (skip-chars-forward " \t"))
466 (current-column))))
467
468 (continued-expr-p js2-basic-offset)
469
470 (t 0)))))
471
472 (defun js2--comment-indent (parse-status)
473 "Indentation inside a multi-line block comment continuation line."
474 (save-excursion
475 (goto-char (nth 8 parse-status))
476 (if (looking-at "/\\*")
477 (+ 1 (current-column))
478 0)))
479
480 (defun js2-indent-line (&optional bounce-backwards)
481 "Indent the current line as JavaScript source text."
482 (interactive)
483 (let (parse-status offset
484 ;; Don't whine about errors/warnings when we're indenting.
485 ;; This has to be set before calling parse-partial-sexp below.
486 (inhibit-point-motion-hooks t))
487 (setq parse-status (save-excursion
488 (syntax-ppss (point-at-bol)))
489 offset (- (point) (save-excursion
490 (back-to-indentation)
491 (point))))
492 ;; Don't touch multiline strings.
493 (unless (nth 3 parse-status)
494 (indent-line-to (js2-proper-indentation parse-status))
495 (when (cl-plusp offset)
496 (forward-char offset)))))
497
498 ;;; JSX Indentation
499
500 ;; The following JSX indentation code is copied basically verbatim from js.el at
501 ;; 958da7f, except that the prefixes on the functions/variables are changed.
502
503 (defsubst js2--jsx-find-before-tag ()
504 "Find where JSX starts.
505
506 Assume JSX appears in the following instances:
507 - Inside parentheses, when returned or as the first argument
508 to a function, and after a newline
509 - When assigned to variables or object properties, but only
510 on a single line
511 - As the N+1th argument to a function
512
513 This is an optimized version of (re-search-backward \"[(,]\n\"
514 nil t), except set point to the end of the match. This logic
515 executes up to the number of lines in the file, so it should be
516 really fast to reduce that impact."
517 (let (pos)
518 (while (and (> (point) (point-min))
519 (not (progn
520 (end-of-line 0)
521 (when (or (eq (char-before) 40) ; (
522 (eq (char-before) 44)) ; ,
523 (setq pos (1- (point))))))))
524 pos))
525
526 (defconst js2--jsx-end-tag-re
527 (concat "</" sgml-name-re ">\\|/>")
528 "Find the end of a JSX element.")
529
530 (defconst js2--jsx-after-tag-re "[),]"
531 "Find where JSX ends.
532 This complements the assumption of where JSX appears from
533 `js--jsx-before-tag-re', which see.")
534
535 (defun js2--jsx-indented-element-p ()
536 "Determine if/how the current line should be indented as JSX.
537
538 Return `first' for the first JSXElement on its own line.
539 Return `nth' for subsequent lines of the first JSXElement.
540 Return `expression' for an embedded JS expression.
541 Return `after' for anything after the last JSXElement.
542 Return nil for non-JSX lines.
543
544 Currently, JSX indentation supports the following styles:
545
546 - Single-line elements (indented like normal JS):
547
548 var element = <div></div>;
549
550 - Multi-line elements (enclosed in parentheses):
551
552 function () {
553 return (
554 <div>
555 <div></div>
556 </div>
557 );
558 }
559
560 - Function arguments:
561
562 React.render(
563 <div></div>,
564 document.querySelector('.root')
565 );"
566 (let ((current-pos (point))
567 (current-line (line-number-at-pos))
568 last-pos
569 before-tag-pos before-tag-line
570 tag-start-pos tag-start-line
571 tag-end-pos tag-end-line
572 after-tag-line
573 parens paren type)
574 (save-excursion
575 (and
576 ;; Determine if we're inside a jsx element
577 (progn
578 (end-of-line)
579 (while (and (not tag-start-pos)
580 (setq last-pos (js2--jsx-find-before-tag)))
581 (while (forward-comment 1))
582 (when (= (char-after) 60) ; <
583 (setq before-tag-pos last-pos
584 tag-start-pos (point)))
585 (goto-char last-pos))
586 tag-start-pos)
587 (progn
588 (setq before-tag-line (line-number-at-pos before-tag-pos)
589 tag-start-line (line-number-at-pos tag-start-pos))
590 (and
591 ;; A "before" line which also starts an element begins with js, so
592 ;; indent it like js
593 (> current-line before-tag-line)
594 ;; Only indent the jsx lines like jsx
595 (>= current-line tag-start-line)))
596 (cond
597 ;; Analyze bounds if there are any
598 ((progn
599 (while (and (not tag-end-pos)
600 (setq last-pos (re-search-forward js2--jsx-end-tag-re nil t)))
601 (while (forward-comment 1))
602 (when (looking-at js2--jsx-after-tag-re)
603 (setq tag-end-pos last-pos)))
604 tag-end-pos)
605 (setq tag-end-line (line-number-at-pos tag-end-pos)
606 after-tag-line (line-number-at-pos after-tag-line))
607 (or (and
608 ;; Ensure we're actually within the bounds of the jsx
609 (<= current-line tag-end-line)
610 ;; An "after" line which does not end an element begins with
611 ;; js, so indent it like js
612 (<= current-line after-tag-line))
613 (and
614 ;; Handle another case where there could be e.g. comments after
615 ;; the element
616 (> current-line tag-end-line)
617 (< current-line after-tag-line)
618 (setq type 'after))))
619 ;; They may not be any bounds (yet)
620 (t))
621 ;; Check if we're inside an embedded multi-line js expression
622 (cond
623 ((not type)
624 (goto-char current-pos)
625 (end-of-line)
626 (setq parens (nth 9 (syntax-ppss)))
627 (while (and parens (not type))
628 (setq paren (car parens))
629 (cond
630 ((and (>= paren tag-start-pos)
631 ;; Curly bracket indicates the start of an embedded expression
632 (= (char-after paren) 123) ; {
633 ;; The first line of the expression is indented like sgml
634 (> current-line (line-number-at-pos paren))
635 ;; Check if within a closing curly bracket (if any)
636 ;; (exclusive, as the closing bracket is indented like sgml)
637 (cond
638 ((progn
639 (goto-char paren)
640 (ignore-errors (let (forward-sexp-function)
641 (forward-sexp))))
642 (< current-line (line-number-at-pos)))
643 (t)))
644 ;; Indicate this guy will be indented specially
645 (setq type 'expression))
646 (t (setq parens (cdr parens)))))
647 t)
648 (t))
649 (cond
650 (type)
651 ;; Indent the first jsx thing like js so we can indent future jsx things
652 ;; like sgml relative to the first thing
653 ((= current-line tag-start-line) 'first)
654 ('nth))))))
655
656 (defmacro js2--as-sgml (&rest body)
657 "Execute BODY as if in sgml-mode."
658 `(with-syntax-table sgml-mode-syntax-table
659 (let (forward-sexp-function
660 parse-sexp-lookup-properties)
661 ,@body)))
662
663 (defun js2--expression-in-sgml-indent-line ()
664 "Indent the current line as JavaScript or SGML (whichever is farther)."
665 (let* (indent-col
666 (savep (point))
667 ;; Don't whine about errors/warnings when we're indenting.
668 ;; This has to be set before calling parse-partial-sexp below.
669 (inhibit-point-motion-hooks t)
670 (parse-status (save-excursion
671 (syntax-ppss (point-at-bol)))))
672 ;; Don't touch multiline strings.
673 (unless (nth 3 parse-status)
674 (setq indent-col (save-excursion
675 (back-to-indentation)
676 (if (>= (point) savep) (setq savep nil))
677 (js2--as-sgml (sgml-calculate-indent))))
678 (if (null indent-col)
679 'noindent
680 ;; Use whichever indentation column is greater, such that the sgml
681 ;; column is effectively a minimum
682 (setq indent-col (max (js2-proper-indentation parse-status)
683 (+ indent-col js2-basic-offset)))
684 (if savep
685 (save-excursion (indent-line-to indent-col))
686 (indent-line-to indent-col))))))
687
688 (defun js2-jsx-indent-line ()
689 "Indent the current line as JSX (with SGML offsets).
690 i.e., customize JSX element indentation with `sgml-basic-offset'
691 et al."
692 (interactive)
693 (let ((indentation-type (js2--jsx-indented-element-p)))
694 (cond
695 ((eq indentation-type 'expression)
696 (js2--expression-in-sgml-indent-line))
697 ((or (eq indentation-type 'first)
698 (eq indentation-type 'after))
699 ;; Don't treat this first thing as a continued expression (often a "<" or
700 ;; ">" causes this misinterpretation)
701 (cl-letf (((symbol-function #'js2-continued-expression-p) 'ignore))
702 (js2-indent-line)))
703 ((eq indentation-type 'nth)
704 (js2--as-sgml (sgml-indent-line)))
705 (t (js2-indent-line)))))
706
707 (provide 'js2-old-indent)
708
709 ;;; js2-old-indent.el ends here