aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Tromey2017-01-12 23:15:00 -0700
committerTom Tromey2017-01-13 12:38:36 -0700
commit502390822f9c0068898ae41285b37568bf0e4d1c (patch)
tree08d55c4c60eeb803935779d6541db7ef171b02bd
parentb47f97218efb8d9966e084bdfd8a86e8c47cf81d (diff)
downloademacs-502390822f9c0068898ae41285b37568bf0e4d1c.tar.gz
emacs-502390822f9c0068898ae41285b37568bf0e4d1c.zip
Add chained indentation to js-mode
Bug#20896 * lisp/progmodes/js.el (js-chain-indent): New variable. (js--skip-term-backward, js--skip-terms-backward) (js--chained-expression-p): New functions. (js--proper-indentation): Call js--chained-expression-p. * test/manual/indent/js-chain.js: New file. * test/manual/indent/js.js: Add (non-)chained indentation test.
-rw-r--r--lisp/progmodes/js.el72
-rw-r--r--test/manual/indent/js-chain.js29
-rw-r--r--test/manual/indent/js.js4
3 files changed, 105 insertions, 0 deletions
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index e84215d4301..54df3913fc6 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -552,6 +552,20 @@ don't indent the first one's initializer; otherwise, indent it.
552 :safe 'symbolp 552 :safe 'symbolp
553 :group 'js) 553 :group 'js)
554 554
555(defcustom js-chain-indent nil
556 "Use \"chained\" indentation.
557Chained indentation applies when the current line starts with \".\".
558If the previous expression also contains a \".\" at the same level,
559then the \".\"s will be lined up:
560
561 let x = svg.mumble()
562 .chained;
563"
564 :version "26.1"
565 :type 'boolean
566 :safe 'booleanp
567 :group 'js)
568
555;;; KeyMap 569;;; KeyMap
556 570
557(defvar js-mode-map 571(defvar js-mode-map
@@ -1808,6 +1822,63 @@ This performs fontification according to `js--class-styles'."
1808 (and (progn (backward-char) 1822 (and (progn (backward-char)
1809 (not (looking-at "+\\+\\|--\\|/[/*]")))))))))) 1823 (not (looking-at "+\\+\\|--\\|/[/*]"))))))))))
1810 1824
1825(defun js--skip-term-backward ()
1826 "Skip a term before point; return t if a term was skipped."
1827 (let ((term-skipped nil))
1828 ;; Skip backward over balanced parens.
1829 (let ((progress t))
1830 (while progress
1831 (setq progress nil)
1832 ;; First skip whitespace.
1833 (skip-syntax-backward " ")
1834 ;; Now if we're looking at closing paren, skip to the opener.
1835 ;; This doesn't strictly follow JS syntax, in that we might
1836 ;; skip something nonsensical like "()[]{}", but it is enough
1837 ;; if it works ok for valid input.
1838 (when (memq (char-before) '(?\] ?\) ?\}))
1839 (setq progress t term-skipped t)
1840 (backward-list))))
1841 ;; Maybe skip over a symbol.
1842 (let ((save-point (point)))
1843 (if (and (< (skip-syntax-backward "w_") 0)
1844 (looking-at js--name-re))
1845 ;; Skipped.
1846 (progn
1847 (setq term-skipped t)
1848 (skip-syntax-backward " "))
1849 ;; Did not skip, so restore point.
1850 (goto-char save-point)))
1851 (when (and term-skipped (> (point) (point-min)))
1852 (backward-char)
1853 (eq (char-after) ?.))))
1854
1855(defun js--skip-terms-backward ()
1856 "Skip any number of terms backward.
1857Move point to the earliest \".\" without changing paren levels.
1858Returns t if successful, nil if no term was found."
1859 (when (js--skip-term-backward)
1860 ;; Found at least one.
1861 (let ((last-point (point)))
1862 (while (js--skip-term-backward)
1863 (setq last-point (point)))
1864 (goto-char last-point)
1865 t)))
1866
1867(defun js--chained-expression-p ()
1868 "A helper for js--proper-indentation that handles chained expressions.
1869A chained expression is when the current line starts with '.' and the
1870previous line also has a '.' expression.
1871This function returns the indentation for the current line if it is
1872a chained expression line; otherwise nil.
1873This should only be called while point is at the start of the line's content,
1874as determined by `back-to-indentation'."
1875 (when js-chain-indent
1876 (save-excursion
1877 (when (and (eq (char-after) ?.)
1878 (js--continued-expression-p)
1879 (js--find-newline-backward)
1880 (js--skip-terms-backward))
1881 (current-column)))))
1811 1882
1812(defun js--end-of-do-while-loop-p () 1883(defun js--end-of-do-while-loop-p ()
1813 "Return non-nil if point is on the \"while\" of a do-while statement. 1884 "Return non-nil if point is on the \"while\" of a do-while statement.
@@ -1984,6 +2055,7 @@ indentation is aligned to that column."
1984 ;; At or after the first loop? 2055 ;; At or after the first loop?
1985 (>= (point) beg) 2056 (>= (point) beg)
1986 (js--array-comp-indentation bracket beg)))) 2057 (js--array-comp-indentation bracket beg))))
2058 ((js--chained-expression-p))
1987 ((js--ctrl-statement-indentation)) 2059 ((js--ctrl-statement-indentation))
1988 ((js--multi-line-declaration-indentation)) 2060 ((js--multi-line-declaration-indentation))
1989 ((nth 1 parse-status) 2061 ((nth 1 parse-status)
diff --git a/test/manual/indent/js-chain.js b/test/manual/indent/js-chain.js
new file mode 100644
index 00000000000..2a290294026
--- /dev/null
+++ b/test/manual/indent/js-chain.js
@@ -0,0 +1,29 @@
1// Normal chaining.
2let x = svg.mumble()
3 .zzz;
4
5// Chaining with an intervening line comment.
6let x = svg.mumble() // line comment
7 .zzz;
8
9// Chaining with multiple dots.
10let x = svg.selectAll().something()
11 .zzz;
12
13// Nested chaining.
14let x = svg.selectAll(d3.svg.something()
15 .zzz);
16
17// Nothing to chain to.
18let x = svg()
19 .zzz;
20
21// Nothing to chain to.
22let x = svg().mumble.x() + 73
23 .zzz;
24
25// Local Variables:
26// indent-tabs-mode: nil
27// js-chain-indent: t
28// js-indent-level: 2
29// End:
diff --git a/test/manual/indent/js.js b/test/manual/indent/js.js
index d004b82f8bc..846c3a1a5c2 100644
--- a/test/manual/indent/js.js
+++ b/test/manual/indent/js.js
@@ -124,6 +124,10 @@ if (x > 72 &&
124 do_something(); 124 do_something();
125} 125}
126 126
127// Test that chaining doesn't happen when js-chain-indent is nil.
128let x = svg.mumble()
129 .zzz;
130
127// Local Variables: 131// Local Variables:
128// indent-tabs-mode: nil 132// indent-tabs-mode: nil
129// js-indent-level: 2 133// js-indent-level: 2