diff options
| author | Jackson Ray Hamilton | 2019-03-26 20:14:46 -0700 |
|---|---|---|
| committer | Jackson Ray Hamilton | 2019-04-08 22:48:23 -0700 |
| commit | afec4511cf5c336eaf9f8bb1425bf2dd1fc12740 (patch) | |
| tree | 35b760f6eb48099f7f982b65cb733b8a9dabaa92 | |
| parent | 55c80d43a972d3e126c173745c57a0a383bd3ad4 (diff) | |
| download | emacs-afec4511cf5c336eaf9f8bb1425bf2dd1fc12740.tar.gz emacs-afec4511cf5c336eaf9f8bb1425bf2dd1fc12740.zip | |
Split JSX indentation calculation into several functions
* lisp/progmodes/js.el (js-jsx--contextual-indentation)
(js-jsx--expr-attribute-pos, js-jsx--expr-indentation): Extract logic
from js-jsx--indentation, and improve the logic’s documentation.
(js-jsx--indentation): Simplify by splitting into several
functions (see above) and improve the logic’s documentation.
| -rw-r--r-- | lisp/progmodes/js.el | 146 |
1 files changed, 81 insertions, 65 deletions
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 679633fc836..2d29d4e443a 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el | |||
| @@ -2575,12 +2575,86 @@ current line is the \"=>\" token (of an arrow function)." | |||
| 2575 | (list 'tag (nth 0 enclosing-tag-pos) (nth 1 enclosing-tag-pos))) | 2575 | (list 'tag (nth 0 enclosing-tag-pos) (nth 1 enclosing-tag-pos))) |
| 2576 | (list 'text (nth 0 enclosing-tag-pos) (nth 2 enclosing-tag-pos)))))) | 2576 | (list 'text (nth 0 enclosing-tag-pos) (nth 2 enclosing-tag-pos)))))) |
| 2577 | 2577 | ||
| 2578 | (defun js-jsx--contextual-indentation (line context) | ||
| 2579 | "Calculate indentation column for LINE from CONTEXT. | ||
| 2580 | The column calculation is based off of `sgml-calculate-indent'." | ||
| 2581 | (pcase (nth 0 context) | ||
| 2582 | |||
| 2583 | ('string | ||
| 2584 | ;; Go back to previous non-empty line. | ||
| 2585 | (while (and (> (point) (nth 1 context)) | ||
| 2586 | (zerop (forward-line -1)) | ||
| 2587 | (looking-at "[ \t]*$"))) | ||
| 2588 | (if (> (point) (nth 1 context)) | ||
| 2589 | ;; Previous line is inside the string. | ||
| 2590 | (current-indentation) | ||
| 2591 | (goto-char (nth 1 context)) | ||
| 2592 | (1+ (current-column)))) | ||
| 2593 | |||
| 2594 | ('tag | ||
| 2595 | ;; Special JSX indentation rule: a “dangling” closing angle | ||
| 2596 | ;; bracket on its own line is indented at the same level as the | ||
| 2597 | ;; opening angle bracket of the JSXElement. Otherwise, indent | ||
| 2598 | ;; JSXAttribute space like SGML. | ||
| 2599 | (if (progn | ||
| 2600 | (goto-char (nth 2 context)) | ||
| 2601 | (and (= line (line-number-at-pos)) | ||
| 2602 | (looking-back "^\\s-*/?>" (line-beginning-position)))) | ||
| 2603 | (progn | ||
| 2604 | (goto-char (nth 1 context)) | ||
| 2605 | (current-column)) | ||
| 2606 | ;; Indent JSXAttribute space like SGML. | ||
| 2607 | (goto-char (nth 1 context)) | ||
| 2608 | ;; Skip tag name: | ||
| 2609 | (skip-chars-forward " \t") | ||
| 2610 | (skip-chars-forward "^ \t\n") | ||
| 2611 | (skip-chars-forward " \t") | ||
| 2612 | (if (not (eolp)) | ||
| 2613 | (current-column) | ||
| 2614 | ;; This is the first attribute: indent. | ||
| 2615 | (goto-char (+ (nth 1 context) js-jsx-attribute-offset)) | ||
| 2616 | (+ (current-column) js-indent-level)))) | ||
| 2617 | |||
| 2618 | ('text | ||
| 2619 | ;; Indent to reflect nesting. | ||
| 2620 | (goto-char (nth 1 context)) | ||
| 2621 | (+ (current-column) | ||
| 2622 | ;; The last line isn’t nested, but the rest are. | ||
| 2623 | (if (or (not (nth 2 context)) ; Unclosed. | ||
| 2624 | (< line (line-number-at-pos (nth 2 context)))) | ||
| 2625 | js-indent-level | ||
| 2626 | 0))) | ||
| 2627 | |||
| 2628 | )) | ||
| 2629 | |||
| 2630 | (defun js-jsx--expr-attribute-pos (start limit) | ||
| 2631 | "Look back from START to LIMIT for a JSXAttribute." | ||
| 2632 | (save-excursion | ||
| 2633 | (goto-char start) ; Skip the first curly. | ||
| 2634 | ;; Skip any remaining enclosing curlies until the JSXElement’s | ||
| 2635 | ;; beginning position; the last curly ought to be one of a | ||
| 2636 | ;; JSXExpressionContainer, which may refer to its JSXAttribute’s | ||
| 2637 | ;; beginning position (if it has one). | ||
| 2638 | (js-jsx--goto-outermost-enclosing-curly limit) | ||
| 2639 | (get-text-property (point) 'js-jsx-expr-attribute))) | ||
| 2640 | |||
| 2578 | (defvar js-jsx--indent-col nil | 2641 | (defvar js-jsx--indent-col nil |
| 2579 | "Baseline column for JS indentation within JSX.") | 2642 | "Baseline column for JS indentation within JSX.") |
| 2580 | 2643 | ||
| 2581 | (defvar js-jsx--indent-attribute-line nil | 2644 | (defvar js-jsx--indent-attribute-line nil |
| 2582 | "Line relative to which indentation uses JSX as a baseline.") | 2645 | "Line relative to which indentation uses JSX as a baseline.") |
| 2583 | 2646 | ||
| 2647 | (defun js-jsx--expr-indentation (parse-status pos col) | ||
| 2648 | "Indent using PARSE-STATUS; relative to POS, use base COL. | ||
| 2649 | To indent a JSXExpressionContainer’s expression, calculate the JS | ||
| 2650 | indentation, using JSX indentation as the base column when | ||
| 2651 | indenting relative to the beginning line of the | ||
| 2652 | JSXExpressionContainer’s JSXAttribute (if any)." | ||
| 2653 | (let* ((js-jsx--indent-col col) | ||
| 2654 | (js-jsx--indent-attribute-line | ||
| 2655 | (if pos (line-number-at-pos pos)))) | ||
| 2656 | (js--proper-indentation parse-status))) | ||
| 2657 | |||
| 2584 | (defun js-jsx--indentation (parse-status) | 2658 | (defun js-jsx--indentation (parse-status) |
| 2585 | "Helper function for `js--proper-indentation'. | 2659 | "Helper function for `js--proper-indentation'. |
| 2586 | Return the proper indentation of the current line if it is part | 2660 | Return the proper indentation of the current line if it is part |
| @@ -2605,74 +2679,16 @@ return nil." | |||
| 2605 | (and | 2679 | (and |
| 2606 | (= beg-line current-line) | 2680 | (= beg-line current-line) |
| 2607 | (or (not curly-pos) (> (point) curly-pos))))))) | 2681 | (or (not curly-pos) (> (point) curly-pos))))))) |
| 2682 | ;; When on the second or later line of JSX, indent as JSX, | ||
| 2683 | ;; possibly switching back to JS indentation within | ||
| 2684 | ;; JSXExpressionContainers, possibly using the JSX as a base | ||
| 2685 | ;; column while switching back to JS indentation. | ||
| 2608 | (when (and context (> current-line beg-line)) | 2686 | (when (and context (> current-line beg-line)) |
| 2609 | (save-excursion | 2687 | (save-excursion |
| 2610 | ;; The column calculation is based on `sgml-calculate-indent'. | 2688 | (setq col (js-jsx--contextual-indentation current-line context))) |
| 2611 | (setq col (pcase (nth 0 context) | ||
| 2612 | |||
| 2613 | ('string | ||
| 2614 | ;; Go back to previous non-empty line. | ||
| 2615 | (while (and (> (point) (nth 1 context)) | ||
| 2616 | (zerop (forward-line -1)) | ||
| 2617 | (looking-at "[ \t]*$"))) | ||
| 2618 | (if (> (point) (nth 1 context)) | ||
| 2619 | ;; Previous line is inside the string. | ||
| 2620 | (current-indentation) | ||
| 2621 | (goto-char (nth 1 context)) | ||
| 2622 | (1+ (current-column)))) | ||
| 2623 | |||
| 2624 | ('tag | ||
| 2625 | ;; Special JSX indentation rule: a “dangling” | ||
| 2626 | ;; closing angle bracket on its own line is | ||
| 2627 | ;; indented at the same level as the opening | ||
| 2628 | ;; angle bracket of the JSXElement. Otherwise, | ||
| 2629 | ;; indent JSXAttribute space like SGML. | ||
| 2630 | (if (progn | ||
| 2631 | (goto-char (nth 2 context)) | ||
| 2632 | (and (= current-line (line-number-at-pos)) | ||
| 2633 | (looking-back "^\\s-*/?>" (line-beginning-position)))) | ||
| 2634 | (progn | ||
| 2635 | (goto-char (nth 1 context)) | ||
| 2636 | (current-column)) | ||
| 2637 | ;; Indent JSXAttribute space like SGML. | ||
| 2638 | (goto-char (nth 1 context)) | ||
| 2639 | ;; Skip tag name: | ||
| 2640 | (skip-chars-forward " \t") | ||
| 2641 | (skip-chars-forward "^ \t\n") | ||
| 2642 | (skip-chars-forward " \t") | ||
| 2643 | (if (not (eolp)) | ||
| 2644 | (current-column) | ||
| 2645 | ;; This is the first attribute: indent. | ||
| 2646 | (goto-char (+ (nth 1 context) js-jsx-attribute-offset)) | ||
| 2647 | (+ (current-column) js-indent-level)))) | ||
| 2648 | |||
| 2649 | ('text | ||
| 2650 | ;; Indent to reflect nesting. | ||
| 2651 | (goto-char (nth 1 context)) | ||
| 2652 | (+ (current-column) | ||
| 2653 | ;; The last line isn’t nested, but the rest are. | ||
| 2654 | (if (or (not (nth 2 context)) ; Unclosed. | ||
| 2655 | (< current-line (line-number-at-pos (nth 2 context)))) | ||
| 2656 | js-indent-level | ||
| 2657 | 0))) | ||
| 2658 | |||
| 2659 | ))) | ||
| 2660 | ;; To indent a JSXExpressionContainer’s expression, calculate | ||
| 2661 | ;; the JS indentation, possibly using JSX indentation as the | ||
| 2662 | ;; base column. | ||
| 2663 | (if expr-p | 2689 | (if expr-p |
| 2664 | (let* ((js-jsx--indent-col col) | 2690 | (js-jsx--expr-indentation |
| 2665 | (expr-attribute-pos | 2691 | parse-status (js-jsx--expr-attribute-pos curly-pos (nth 1 context)) col) |
| 2666 | (save-excursion | ||
| 2667 | (goto-char curly-pos) ; Skip first curly. | ||
| 2668 | ;; Skip any remaining enclosing curlies up until | ||
| 2669 | ;; the contextual JSXElement’s beginning position. | ||
| 2670 | (js-jsx--goto-outermost-enclosing-curly (nth 1 context)) | ||
| 2671 | (get-text-property (point) 'js-jsx-expr-attribute))) | ||
| 2672 | (js-jsx--indent-attribute-line | ||
| 2673 | (when expr-attribute-pos | ||
| 2674 | (line-number-at-pos expr-attribute-pos)))) | ||
| 2675 | (js--proper-indentation parse-status)) | ||
| 2676 | col)))) | 2692 | col)))) |
| 2677 | 2693 | ||
| 2678 | (defun js--proper-indentation (parse-status) | 2694 | (defun js--proper-indentation (parse-status) |