emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 5023908 5/5: Add chained indentation to js-mode


From: Tom Tromey
Subject: [Emacs-diffs] master 5023908 5/5: Add chained indentation to js-mode
Date: Fri, 13 Jan 2017 19:51:00 +0000 (UTC)

branch: master
commit 502390822f9c0068898ae41285b37568bf0e4d1c
Author: Tom Tromey <address@hidden>
Commit: Tom Tromey <address@hidden>

    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.
---
 lisp/progmodes/js.el           |   72 ++++++++++++++++++++++++++++++++++++++++
 test/manual/indent/js-chain.js |   29 ++++++++++++++++
 test/manual/indent/js.js       |    4 +++
 3 files changed, 105 insertions(+)

diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index e84215d..54df391 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.
   :safe 'symbolp
   :group 'js)
 
+(defcustom js-chain-indent nil
+  "Use \"chained\" indentation.
+Chained indentation applies when the current line starts with \".\".
+If the previous expression also contains a \".\" at the same level,
+then the \".\"s will be lined up:
+
+  let x = svg.mumble()
+             .chained;
+"
+  :version "26.1"
+  :type 'boolean
+  :safe 'booleanp
+  :group 'js)
+
 ;;; KeyMap
 
 (defvar js-mode-map
@@ -1808,6 +1822,63 @@ This performs fontification according to 
`js--class-styles'."
                   (and (progn (backward-char)
                               (not (looking-at "+\\+\\|--\\|/[/*]"))))))))))
 
+(defun js--skip-term-backward ()
+  "Skip a term before point; return t if a term was skipped."
+  (let ((term-skipped nil))
+    ;; Skip backward over balanced parens.
+    (let ((progress t))
+      (while progress
+        (setq progress nil)
+        ;; First skip whitespace.
+        (skip-syntax-backward " ")
+        ;; Now if we're looking at closing paren, skip to the opener.
+        ;; This doesn't strictly follow JS syntax, in that we might
+        ;; skip something nonsensical like "()[]{}", but it is enough
+        ;; if it works ok for valid input.
+        (when (memq (char-before) '(?\] ?\) ?\}))
+          (setq progress t term-skipped t)
+          (backward-list))))
+    ;; Maybe skip over a symbol.
+    (let ((save-point (point)))
+      (if (and (< (skip-syntax-backward "w_") 0)
+                 (looking-at js--name-re))
+          ;; Skipped.
+          (progn
+            (setq term-skipped t)
+            (skip-syntax-backward " "))
+        ;; Did not skip, so restore point.
+        (goto-char save-point)))
+    (when (and term-skipped (> (point) (point-min)))
+      (backward-char)
+      (eq (char-after) ?.))))
+
+(defun js--skip-terms-backward ()
+  "Skip any number of terms backward.
+Move point to the earliest \".\" without changing paren levels.
+Returns t if successful, nil if no term was found."
+  (when (js--skip-term-backward)
+    ;; Found at least one.
+    (let ((last-point (point)))
+      (while (js--skip-term-backward)
+        (setq last-point (point)))
+      (goto-char last-point)
+      t)))
+
+(defun js--chained-expression-p ()
+  "A helper for js--proper-indentation that handles chained expressions.
+A chained expression is when the current line starts with '.' and the
+previous line also has a '.' expression.
+This function returns the indentation for the current line if it is
+a chained expression line; otherwise nil.
+This should only be called while point is at the start of the line's content,
+as determined by `back-to-indentation'."
+  (when js-chain-indent
+    (save-excursion
+      (when (and (eq (char-after) ?.)
+                 (js--continued-expression-p)
+                 (js--find-newline-backward)
+                 (js--skip-terms-backward))
+        (current-column)))))
 
 (defun js--end-of-do-while-loop-p ()
   "Return non-nil if point is on the \"while\" of a do-while statement.
@@ -1984,6 +2055,7 @@ indentation is aligned to that column."
                   ;; At or after the first loop?
                   (>= (point) beg)
                   (js--array-comp-indentation bracket beg))))
+          ((js--chained-expression-p))
           ((js--ctrl-statement-indentation))
           ((js--multi-line-declaration-indentation))
           ((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 0000000..2a29029
--- /dev/null
+++ b/test/manual/indent/js-chain.js
@@ -0,0 +1,29 @@
+// Normal chaining.
+let x = svg.mumble()
+           .zzz;
+
+// Chaining with an intervening line comment.
+let x = svg.mumble()           // line comment
+           .zzz;
+
+// Chaining with multiple dots.
+let x = svg.selectAll().something()
+           .zzz;
+
+// Nested chaining.
+let x = svg.selectAll(d3.svg.something()
+                        .zzz);
+
+// Nothing to chain to.
+let x = svg()
+    .zzz;
+
+// Nothing to chain to.
+let x = svg().mumble.x() + 73
+    .zzz;
+
+// Local Variables:
+// indent-tabs-mode: nil
+// js-chain-indent: t
+// js-indent-level: 2
+// End:
diff --git a/test/manual/indent/js.js b/test/manual/indent/js.js
index d004b82..846c3a1 100644
--- a/test/manual/indent/js.js
+++ b/test/manual/indent/js.js
@@ -124,6 +124,10 @@ if (x > 72 &&
   do_something();
 }
 
+// Test that chaining doesn't happen when js-chain-indent is nil.
+let x = svg.mumble()
+    .zzz;
+
 // Local Variables:
 // indent-tabs-mode: nil
 // js-indent-level: 2



reply via email to

[Prev in Thread] Current Thread [Next in Thread]