unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#20896: 25.0.50; [js-mode][FR] support chain syntax indentation
@ 2015-06-25 15:37 Rasmus
  2015-06-26  0:14 ` Rasmus
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Rasmus @ 2015-06-25 15:37 UTC (permalink / raw)
  To: 20896

Hi,

I would be lovely if js-mode would support the chain-syntax used by the
d3.js library.  d3.js is used to making svg graphics.  The syntax is very
readable if indentation is right:


var points = svg.selectAll(".scatter-dots")
               .data(data)
               .enter().append("path")
               .more_funs();
               

var an_axis = axes.append("g")
                  .call(d3.svg.axis()
                          .scale(Scale)
                          .orient("bottom"));
   
Usually calls are factored out so the latter example is not so important
to be able to match.  Just aligning on the first dot on the previous line
would be a big step forward.

I don't know if this is useful for other JS libraries than d3.  Some d3
documentation mentioned this selector-mechanism was inspired by jQuery,
but I don't know this library well enough to say whether it would be
useful with that.

Thanks,
Rasmus

-- 
Not everything that goes around comes back around, you know





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#20896: 25.0.50; [js-mode][FR] support chain syntax indentation
  2015-06-25 15:37 bug#20896: 25.0.50; [js-mode][FR] support chain syntax indentation Rasmus
@ 2015-06-26  0:14 ` Rasmus
  2017-01-10  6:29 ` bug#20896: patch to add chained indentation Tom Tromey
  2017-01-14 17:45 ` bug#20896: done Tom Tromey
  2 siblings, 0 replies; 7+ messages in thread
From: Rasmus @ 2015-06-26  0:14 UTC (permalink / raw)
  To: 20896

Hi,

Rasmus <rasmus@gmx.us> writes:

> var an_axis = axes.append("g")
>                   .call(d3.svg.axis()
>                           .scale(Scale)
>                           .orient("bottom"));


Actually, this case is not as crazy as first assumed and should ideally be
supported.  Here's a pretty reasonable example (except for names being too
long):

      plot.axes.yScale = d3.scale.ordinal()
                           .domain(d3.range(bar.ybins).map(function(d){return d*10;}))
                           .range(d3.range(bar.ybins)
                                    .map(function(d,i) {
                                        return plot.height-plot.height/bar.ybins * i;}));


Also, notice there's another, potential, indentation typo in this code.
The last "return" should be one character to the right IMO, i.e.

    .map(function(d,i) {
         return plot.height-plot.height/bar.ybins * i;}));

Thanks,
Rasmus

-- 
Dung makes an excellent fertilizer






^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#20896: patch to add chained indentation
  2015-06-25 15:37 bug#20896: 25.0.50; [js-mode][FR] support chain syntax indentation Rasmus
  2015-06-26  0:14 ` Rasmus
@ 2017-01-10  6:29 ` Tom Tromey
  2017-01-12  1:57   ` Dmitry Gutov
  2017-01-14 17:45 ` bug#20896: done Tom Tromey
  2 siblings, 1 reply; 7+ messages in thread
From: Tom Tromey @ 2017-01-10  6:29 UTC (permalink / raw)
  To: 20896; +Cc: Daniel Colascione

This patch adds chained indentation, as requested in the bug.  It comes
with some tests (added to a file that first appears in patch in another
bug -- I can commit these when the time comes, mostly I'm interested in
review).

Tom

diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index 0551f2a..1211631 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -552,6 +552,20 @@ js-indent-first-init
   :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,62 @@ js--continued-expression-p
                   (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."
+  (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 +2054,7 @@ js--proper-indentation
                   ;; 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/lisp/progmodes/js-tests.el b/test/lisp/progmodes/js-tests.el
index de322f2..effd58c 100644
--- a/test/lisp/progmodes/js-tests.el
+++ b/test/lisp/progmodes/js-tests.el
@@ -69,6 +69,77 @@
     (should (equal (buffer-substring (point-at-bol) (point-at-eol))
                    "\tdo_something();"))))
 
+(ert-deftest js-mode-indent-bug-20896-chain ()
+  (with-temp-buffer
+    (js-mode)
+    (setq-local js-chain-indent t)
+    (setq-local indent-tabs-mode nil)
+    (insert "let x = svg.mumble()\n.zzz")
+    (js-indent-line)
+    (should (equal (buffer-substring (point-at-bol) (point-at-eol))
+                   "           .zzz"))))
+
+(ert-deftest js-mode-indent-bug-20896-chain-comment ()
+  (with-temp-buffer
+    (js-mode)
+    (setq-local js-chain-indent t)
+    (setq-local indent-tabs-mode nil)
+    (insert "let x = svg.mumble() // line comment\n.zzz")
+    (js-indent-line)
+    (should (equal (buffer-substring (point-at-bol) (point-at-eol))
+                   "           .zzz"))))
+
+(ert-deftest js-mode-indent-bug-20896-chain-multi ()
+  (with-temp-buffer
+    (js-mode)
+    (setq-local js-chain-indent t)
+    (setq-local indent-tabs-mode nil)
+    ;; Must line up to the first "." at the same level.
+    (insert "let x = svg.selectAll().something()\n.zzz")
+    (js-indent-line)
+    (should (equal (buffer-substring (point-at-bol) (point-at-eol))
+                   "           .zzz"))))
+
+(ert-deftest js-mode-indent-bug-20896-chain-nested ()
+  (with-temp-buffer
+    (js-mode)
+    (setq-local js-chain-indent t)
+    (setq-local indent-tabs-mode nil)
+    ;; Must line up to the first "." at the same level.
+    (insert "let x = svg.selectAll(d3.svg.something()\n.zzz")
+    (js-indent-line)
+    (should (equal (buffer-substring (point-at-bol) (point-at-eol))
+                   "                        .zzz"))))
+
+(ert-deftest js-mode-indent-bug-20896-no-chain-1 ()
+  (with-temp-buffer
+    (js-mode)
+    ;; Don't set js-chain-indent.
+    (insert "let x = svg.mumble()\n.zzz")
+    (js-indent-line)
+    (should (equal (buffer-substring (point-at-bol) (point-at-eol))
+                   "    .zzz"))))
+
+(ert-deftest js-mode-indent-bug-20896-no-chain-2 ()
+  (with-temp-buffer
+    (js-mode)
+    (setq-local js-chain-indent t)
+    ;; Nothing to chain to.
+    (insert "let x = svg()\n.zzz")
+    (js-indent-line)
+    (should (equal (buffer-substring (point-at-bol) (point-at-eol))
+                   "    .zzz"))))
+
+(ert-deftest js-mode-indent-bug-20896-no-chain-2 ()
+  (with-temp-buffer
+    (js-mode)
+    (setq-local js-chain-indent t)
+    ;; Nothing to chain to.
+    (insert "let x = svg().mumble.x() + 73\n.zzz")
+    (js-indent-line)
+    (should (equal (buffer-substring (point-at-bol) (point-at-eol))
+                   "    .zzz"))))
+
 (provide 'js-tests)
 
 ;;; js-tests.el ends here





^ permalink raw reply related	[flat|nested] 7+ messages in thread

* bug#20896: patch to add chained indentation
  2017-01-10  6:29 ` bug#20896: patch to add chained indentation Tom Tromey
@ 2017-01-12  1:57   ` Dmitry Gutov
  2017-01-12  4:01     ` Tom Tromey
  0 siblings, 1 reply; 7+ messages in thread
From: Dmitry Gutov @ 2017-01-12  1:57 UTC (permalink / raw)
  To: Tom Tromey, 20896; +Cc: Daniel Colascione

On 10.01.2017 09:29, Tom Tromey wrote:
> This patch adds chained indentation, as requested in the bug.

Thanks.

> It comes
> with some tests

I'd just like to point out that it's much better to write indentation 
tests in the format used by test/manual/indent/js*.

> (added to a file that first appears in patch in another
> bug

Not sure which patch you mean. This file seems new.

As for review: js--skip-term-backward seems to be doing something 
similar to the loop in js--multi-line-declaration-indentation.

Maybe a extraction and unification is in order.





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#20896: patch to add chained indentation
  2017-01-12  1:57   ` Dmitry Gutov
@ 2017-01-12  4:01     ` Tom Tromey
  2017-01-13  1:09       ` Dmitry Gutov
  0 siblings, 1 reply; 7+ messages in thread
From: Tom Tromey @ 2017-01-12  4:01 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: 20896, Tom Tromey, Daniel Colascione

>> It comes with some tests

Dmitry> I'd just like to point out that it's much better to write indentation
Dmitry> tests in the format used by test/manual/indent/js*.

Thanks, I wasn't aware of this.  I'll redo the test this way.

>> (added to a file that first appears in patch in another
>> bug

Dmitry> Not sure which patch you mean. This file seems new.

I added js-tests.el in the bug#19399/bug#22431 patch, then further
amended it in bug#15582.  None of these have landed yet.  (There's also
bug#25389, which is related, but doesn't touch the test file.)

Dmitry> As for review: js--skip-term-backward seems to be doing something
Dmitry> similar to the loop in js--multi-line-declaration-indentation.
Dmitry> Maybe a extraction and unification is in order.

I don't really see it.  Could you explain more?

Tom





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#20896: patch to add chained indentation
  2017-01-12  4:01     ` Tom Tromey
@ 2017-01-13  1:09       ` Dmitry Gutov
  0 siblings, 0 replies; 7+ messages in thread
From: Dmitry Gutov @ 2017-01-13  1:09 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 20896, Daniel Colascione

On 12.01.2017 07:01, Tom Tromey wrote:

> I'll redo the test this way.

Thanks.

> Dmitry> As for review: js--skip-term-backward seems to be doing something
> Dmitry> similar to the loop in js--multi-line-declaration-indentation.
> Dmitry> Maybe a extraction and unification is in order.
>
> I don't really see it.  Could you explain more?

The `while' loop jumps to the beginning of the current "assignment 
expression".

`js--skip-terms-backward' is similar because it skips to the beginning 
of the call chain. It's more narrow, though (the other function also 
jumps over binary operators).

Anyway, I'm not so sure there's much value in unifying the 
implementations anymore.





^ permalink raw reply	[flat|nested] 7+ messages in thread

* bug#20896: done
  2015-06-25 15:37 bug#20896: 25.0.50; [js-mode][FR] support chain syntax indentation Rasmus
  2015-06-26  0:14 ` Rasmus
  2017-01-10  6:29 ` bug#20896: patch to add chained indentation Tom Tromey
@ 2017-01-14 17:45 ` Tom Tromey
  2 siblings, 0 replies; 7+ messages in thread
From: Tom Tromey @ 2017-01-14 17:45 UTC (permalink / raw)
  To: 20896-done

This was fixed by 502390822f9c0068898ae41285b37568bf0e4d1c.

Tom





^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-01-14 17:45 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-06-25 15:37 bug#20896: 25.0.50; [js-mode][FR] support chain syntax indentation Rasmus
2015-06-26  0:14 ` Rasmus
2017-01-10  6:29 ` bug#20896: patch to add chained indentation Tom Tromey
2017-01-12  1:57   ` Dmitry Gutov
2017-01-12  4:01     ` Tom Tromey
2017-01-13  1:09       ` Dmitry Gutov
2017-01-14 17:45 ` bug#20896: done Tom Tromey

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).