From 47e59c666a7599863725a04b6ed3f74ba01824b3 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Sun, 8 Dec 2013 18:08:45 +0200 Subject: [PATCH] Port indentation code from js2-mode to js-mode * lisp/progmodes/js.el (js-auto-indent-flag): Remove, was unused. (js-switch-indent-offset): New option. (js--proper-indentation): Use it. And handle the case when "default" is actually a key in an object literal. (js--same-line): New function. (js--multi-line-declaration-indentation): Use it. (js--indent-in-array-comp, js--array-comp-indentation): New functions. (js--proper-indentation): Use them, to handle array comprehension continuations. --- etc/NEWS | 11 +++++ lisp/ChangeLog | 13 ++++++ lisp/progmodes/js.el | 108 +++++++++++++++++++++++++++++++++---------- test/indent/js.js | 45 ++++++++++++++++++ 4 files changed, 152 insertions(+), 25 deletions(-) create mode 100644 test/indent/js.js diff --git a/etc/NEWS b/etc/NEWS index 89fc958375..8ee102487a 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -661,6 +661,17 @@ whether it is safe to use Bash's --noediting option. These days *** Add more Ruby file types to `auto-mode-alist'. +** JS Mode + +*** Better indentation of multiple-variable declarations. +If declaration spans several lines, variables on the following lines +are lined up to the first one. + +*** We now recognize and better indent continuations in array +comprehensions. + +*** New option `js-switch-indent-offset`. + * New Modes and Packages in Emacs 24.4 diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 4a18af44e2..7cc7f0b05f 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,16 @@ +2013-12-08 Dmitry Gutov + + * progmodes/js.el (js-auto-indent-flag): Remove, was unused. + (js-switch-indent-offset): New option. + (js--proper-indentation): Use it. And handle the case when + "default" is actually a key in an object literal. + (js--same-line): New function. + (js--multi-line-declaration-indentation): Use it. + (js--indent-in-array-comp, js--array-comp-indentation): New + functions. + (js--proper-indentation): Use them, to handle array comprehension + continuations. + 2013-12-08 Leo Liu * progmodes/flymake.el (flymake-highlight-line): Re-write. diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 17c13607d7..572b59ecd9 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -459,12 +459,13 @@ The value must be no less than minus `js-indent-level'." :group 'js :version "24.1") -(defcustom js-auto-indent-flag t - "Whether to automatically indent when typing punctuation characters. -If non-nil, the characters {}();,: also indent the current line -in Javascript mode." - :type 'boolean - :group 'js) +(defcustom js-switch-indent-offset 0 + "Number of additional spaces for indenting the contents of a switch block. +The value must not be negative." + :type 'integer + :safe 'integerp + :group 'js + :version "24.4") (defcustom js-flat-functions nil "Treat nested functions as top-level functions in `js-mode'. @@ -1766,6 +1767,10 @@ nil." (list (cons 'c js-comment-lineup-func)))) (c-get-syntactic-indentation (list (cons symbol anchor))))) +(defun js--same-line (pos) + (and (>= pos (point-at-bol)) + (<= pos (point-at-eol)))) + (defun js--multi-line-declaration-indentation () "Helper function for `js--proper-indentation'. Return the proper indentation of the current line if it belongs to a declaration @@ -1788,8 +1793,7 @@ statement spanning multiple lines; otherwise, return nil." (looking-at js--indent-operator-re) (js--backward-syntactic-ws)) (not (eq (char-before) ?\;))) - (and (>= pos (point-at-bol)) - (<= pos (point-at-eol))))))) + (js--same-line pos))))) (condition-case nil (backward-sexp) (scan-error (setq at-opening-bracket t)))) @@ -1797,23 +1801,68 @@ statement spanning multiple lines; otherwise, return nil." (goto-char (match-end 0)) (1+ (current-column))))))) +(defun js--indent-in-array-comp (bracket) + "Return non-nil if we think we're in an array comprehension. +In particular, return the buffer position of the first `for' kwd." + (let ((end (point))) + (save-excursion + (goto-char bracket) + (when (looking-at "\\[") + (forward-char 1) + (js--forward-syntactic-ws) + (if (looking-at "[[{]") + (let (forward-sexp-function) ; Use Lisp version. + (forward-sexp) ; Skip destructuring form. + (js--forward-syntactic-ws) + (if (and (/= (char-after) ?,) ; Regular array. + (looking-at "for")) + (match-beginning 0))) + ;; To skip arbitrary expressions we need the parser, + ;; so we'll just guess at it. + (if (and (> end (point)) ; Not empty literal. + (re-search-forward "[^,]]* \\(for\\) " end t) + ;; Not inside comment or string literal. + (not (nth 8 (parse-partial-sexp bracket (point))))) + (match-beginning 1))))))) + +(defun js--array-comp-indentation (bracket for-kwd) + (if (js--same-line for-kwd) + ;; First continuation line. + (save-excursion + (goto-char bracket) + (forward-char 1) + (skip-chars-forward " \t") + (current-column)) + (save-excursion + (goto-char for-kwd) + (current-column)))) + (defun js--proper-indentation (parse-status) "Return the proper indentation for the current line." (save-excursion (back-to-indentation) - (cond ((nth 4 parse-status) + (cond ((nth 4 parse-status) ; inside comment (js--get-c-offset 'c (nth 8 parse-status))) - ((nth 8 parse-status) 0) ; inside string - ((js--ctrl-statement-indentation)) - ((js--multi-line-declaration-indentation)) + ((nth 3 parse-status) 0) ; inside string ((eq (char-after) ?#) 0) ((save-excursion (js--beginning-of-macro)) 4) + ;; Indent array comprehension continuation lines specially. + ((let ((bracket (nth 1 parse-status)) + beg) + (and bracket + (not (js--same-line bracket)) + (setq beg (js--indent-in-array-comp bracket)) + ;; At or after the first loop? + (>= (point) beg) + (js--array-comp-indentation bracket beg)))) + ((js--ctrl-statement-indentation)) + ((js--multi-line-declaration-indentation)) ((nth 1 parse-status) ;; A single closing paren/bracket should be indented at the ;; same level as the opening statement. Same goes for ;; "case" and "default". - (let ((same-indent-p (looking-at - "[]})]\\|\\_\\|\\_")) + (let ((same-indent-p (looking-at "[]})]")) + (switch-keyword-p (looking-at "default\\_>\\|case\\_>[^:]")) (continued-expr-p (js--continued-expression-p))) (goto-char (nth 1 parse-status)) ; go to the opening char (if (looking-at "[({[]\\s-*\\(/[/*]\\|$\\)") @@ -1821,17 +1870,26 @@ statement spanning multiple lines; otherwise, return nil." (skip-syntax-backward " ") (when (eq (char-before) ?\)) (backward-list)) (back-to-indentation) - (cond (same-indent-p - (current-column)) - (continued-expr-p - (+ (current-column) (* 2 js-indent-level) - js-expr-indent-offset)) - (t - (+ (current-column) js-indent-level - (pcase (char-after (nth 1 parse-status)) - (?\( js-paren-indent-offset) - (?\[ js-square-indent-offset) - (?\{ js-curly-indent-offset)))))) + (let* ((in-switch-p (unless same-indent-p + (looking-at "\\_"))) + (same-indent-p (or same-indent-p + (and switch-keyword-p + in-switch-p))) + (indent + (cond (same-indent-p + (current-column)) + (continued-expr-p + (+ (current-column) (* 2 js-indent-level) + js-expr-indent-offset)) + (t + (+ (current-column) js-indent-level + (pcase (char-after (nth 1 parse-status)) + (?\( js-paren-indent-offset) + (?\[ js-square-indent-offset) + (?\{ js-curly-indent-offset))))))) + (if in-switch-p + (+ indent js-switch-indent-offset) + indent))) ;; If there is something following the opening ;; paren/bracket, everything else should be indented at ;; the same level. diff --git a/test/indent/js.js b/test/indent/js.js new file mode 100644 index 0000000000..d4167da3e9 --- /dev/null +++ b/test/indent/js.js @@ -0,0 +1,45 @@ +var a = 1; +b = 2; + +let c = 1, + d = 2; + +var e = 100500, + + 1; + +var f = bar('/protocols/') +baz(); + +var h = 100500 +1; + +const i = 1, + j = 2; + +var k = 1, + l = [ + 1, 2, + 3, 4 + ], + m = 5; + +var n = function() { + return 7; +}, + o = 8; + +foo(bar, function() { + return 2; +}); + +switch (b) { +case "a": + 2; +default: + 3; +} + +var ; + +var evens = [e for each (e in range(0, 21)) + if (ed % 2 == 0)]; -- 2.20.1