Subject: bug#27503: 26.0.50; Not lining up Javascript arguments

From: James Nguyen <[email-masked]>
Date: Mon, 26 Jun 2017 18:49:59 -0700

I'd like to have js-mode line up arguments normally instead of lining up
arg-wise.

For example:

function functionName(arg1,
                                     arg2) {}

vs

function functionName(arg1,
  arg2)

I think js-mode only support the former at this point. The latter seems
to be fairly common so it'd be great if we could support it.

Following this:
https://emacs.stackexchange.com/questions/29973/stop-javascript-mode-from-lining-up-function-parameters-after-newline/29975#29975

seems to give accurate indentation similar to other editors.

(defun javascript/indent-args (parse-status)
    "Return the proper indentation for the current line."
    (save-excursion
      (back-to-indentation)
      (cond ((nth 4 parse-status)    ; inside comment
             (js--get-c-offset 'c (nth 8 parse-status)))
            ((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--chained-expression-p))
            ((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 "[]})]"))
                   (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
               (progn ; nothing following the opening paren/bracket
                 (skip-syntax-backward " ")
                 (when (eq (char-before) ?\)) (backward-list))
                 (back-to-indentation)
                 (js--maybe-goto-declaration-keyword-end parse-status)
                 (let* ((in-switch-p (unless same-indent-p
                                       (looking-at "\\_<switch\\_>")))
                        (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)))))

            ((js--continued-expression-p)
             (+ js-indent-level js-expr-indent-offset))
            (t (prog-first-column)))))

  (advice-add 'js--proper-indentation :override 'javascript/indent-args)
  
1. This removes an entire if block check. That is probably doing
something I'm not aware of.

2. We probably want to make it configuration either way (something
similar to js-comment-lineup-func (but that seems to only be for comments.))

Some source code to play with is:

  #+begin_src mhtml :tangle yes
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Lifecycle</title>
    <style>
      body, textarea {
        font-family: Courier;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- my app renders here -->
    </div>
    <script src="react/build/react.js"></script>
    <script src="react/build/react-dom.js"></script>
    <script>
      var logMixin = {
        _log: function(methodName, args) {
          console.log(this.name + '::' + methodName, args);
        },
        componentWillUpdate:  function() {this._log('componentWillUpdate',  arguments);},
        componentDidUpdate:   function() {this._log('componentDidUpdate',   arguments);},
        componentWillMount:   function() {this._log('componentWillMount',   arguments);},
        componentDidMount:    function() {this._log('componentDidMount',    arguments);},
        componentWillUnmount: function() {this._log('componentWillUnmount', arguments);},
      };

      var TextAreaCounter = React.createClass({
        name: 'TextAreaCounter',
        mixins: [logMixin],

        propTypes: {
          defaultValue: React.PropTypes.string,
        },

        getInitialState: function() {
          return {
            text: this.props.defaultValue,
          };
        },

        _textChange: function(ev) {
          this.setState({
            text: ev.target.value,
          });
        },

        render: function() {
          return React.DOM.div(null,
            React.DOM.textarea({
              value: this.state.text,
              onChange: this._textChange,
            }),
            React.DOM.h3(null, this.state.text.length)
          );
        }
      });

      var myTextAreaCounter = ReactDOM.render(
        React.createElement(TextAreaCounter, {
          defaultValue: "Bob",
        }),
        document.getElementById("app")
      );
    </script>
  </body>
</html>
  #+end_src
  
More specifically:

  #+begin_src mhtml :tangle yes
        render: function() {
          return React.DOM.div(null,
            React.DOM.textarea({
              value: this.state.text,
              onChange: this._textChange,
            }),
            React.DOM.h3(null, this.state.text.length)
          );
        }
  #+end_src
  
With the current indent settings, we get:

  #+begin_src mhtml :tangle yes
        render: function() {
          return React.DOM.div(null,
                               React.DOM.textarea({
                                 value: this.state.text,
                                 onChange: this._textChange,
                               }),
                               React.DOM.h3(null, this.state.text.length)
                              );
        }
  #+end_src
  
With the above advice:

#+begin_src mhtml :tangle yes
        render: function() {
          return React.DOM.div(null,
            React.DOM.textarea({
              value: this.state.text,
              onChange: this._textChange,
            }),
            React.DOM.h3(null, this.state.text.length)
          );
        }
#+end_src

In GNU Emacs 26.0.50 (build 4, x86_64-apple-darwin16.5.0, NS appkit-1504.82 Version 10.12.4 (Build 16E195))
of 2017-06-24 built on jamesretina.local
Repository revision: 16d2695674a4c8abbec846c427fe8abef97e07ef
Windowing system distributor 'Apple', version 10.3.1504
Recent messages:
The following feature was found in load-path, please check if that’s correct:
(obarray)
Successfully reloaded Org
Org-mode version 8.2.10 (release_8.2.10 @ /Users/james/Code/emacs/nextstep/Emacs.app/Contents/Resources/lisp/org/)
Mark set
Configuring package helm...
Configuring package tramp...done
Configuring package helm...done (0.310s)
Configuring package helm-flx...done
Configuring package helm-fuzzier...done

Configured using:
'configure --with-ns'

Configured features:
JPEG RSVG NOTIFY ACL GNUTLS LIBXML2 ZLIB TOOLKIT_SCROLL_BARS NS

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Org

Minor modes in effect:
  helm-fuzzier-mode: t
  helm-flx-mode: t
  helm-mode: t
  helm-autoresize-mode: t
  helm--remap-mouse-mode: t
  shell-dirtrack-mode: t
  focus-autosave-mode: t
  company-quickhelp-mode: t
  company-quickhelp-local-mode: t
  eval-sexp-fu-flash-mode: t
  flycheck-pos-tip-mode: t
  shackle-mode: t
  yas-global-mode: t
  yas-minor-mode: t
  global-company-mode: t
  company-mode: t
  global-evil-surround-mode: t
  evil-surround-mode: t
  global-evil-visualstar-mode: t
  evil-visualstar-mode: t
  global-evil-matchit-mode: t
  evil-matchit-mode: t
  evil-mode: t
  evil-local-mode: t
  global-undo-tree-mode: t
  undo-tree-mode: t
  recentf-mode: t
  ivy-mode: t
  smartparens-global-mode: t
  smartparens-mode: t
  global-hungry-delete-mode: t
  hungry-delete-mode: t
  ws-butler-global-mode: t
  ws-butler-mode: t
  show-paren-mode: t
  global-auto-revert-mode: t
  winner-mode: t
  override-global-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  mouse-wheel-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  column-number-mode: t
  line-number-mode: t
  transient-mark-mode: t

Load-path shadows:
/Users/james/.emacs.d/elpa/26/color-theme-solarized-20160626.743/solarized-theme hides /Users/james/.emacs.d/elpa/26/solarized-theme-20170430.800/solarized-theme
~/.emacs.d/fork/evil/evil hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil
~/.emacs.d/fork/evil/evil-vars hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-vars
~/.emacs.d/fork/evil/evil-types hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-types
~/.emacs.d/fork/evil/evil-states hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-states
~/.emacs.d/fork/evil/evil-search hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-search
~/.emacs.d/fork/evil/evil-repeat hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-repeat
~/.emacs.d/fork/evil/evil-pkg hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-pkg
~/.emacs.d/fork/evil/evil-maps hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-maps
~/.emacs.d/fork/evil/evil-macros hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-macros
~/.emacs.d/fork/evil/evil-jumps hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-jumps
~/.emacs.d/fork/evil/evil-integration hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-integration
~/.emacs.d/fork/evil/evil-ex hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-ex
~/.emacs.d/fork/evil/evil-digraphs hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-digraphs
~/.emacs.d/fork/evil/evil-core hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-core
~/.emacs.d/fork/evil/evil-common hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-common
~/.emacs.d/fork/evil/evil-commands hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-commands
~/.emacs.d/fork/evil/evil-command-window hides /Users/james/.emacs.d/elpa/26/evil-20170615.1320/evil-command-window

Features:
(shadow sort mail-extr emacsbug message rfc822 mml mml-sec epa epg
mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils
mailheader sendmail helm-fuzzier helm-flx helm-smex helm-command
helm-elisp helm-eval helm-mode helm-files image-dired tramp tramp-compat
tramp-loaddefs trampver parse-time dired-x dired-aux helm-buffers
helm-tags helm-bookmark helm-adaptive helm-info bookmark pp helm-locate
helm-grep helm-regexp helm-external helm-net helm-utils compile
helm-help helm-types helm helm-source eieio-compat helm-multi-match
helm-lib async smex ido loadhist solarized-light-theme solarized add-log
server pulse shell tabify org-element org-rmail org-mhe org-irc org-info
org-gnus org-docview doc-view image-mode dired dired-loaddefs org-bibtex
bibtex org-bbdb org-w3m org org-macro org-footnote org-pcomplete
pcomplete org-list org-faces org-entities org-version ob-emacs-lisp ob
ob-tangle ob-ref ob-lob ob-table ob-exp org-src ob-keys ob-comint comint
ansi-color ob-core ob-eval org-compat org-macs org-loaddefs JJ-org
cursor-sensor mhtml-mode rainbow-mode xterm-color css-mode smie eww puny
mm-url gnus nnheader gnus-util rmail rmail-loaddefs rfc2047 rfc2045
ietf-drums mail-utils mm-util mail-prsvr url-queue url url-proxy
url-privacy url-expand url-methods url-history url-cookie url-domsuf
url-util mailcap shr svg xml browse-url format-spec js cc-mode cc-fonts
cc-guess cc-menus cc-cmds cc-styles cc-align cc-engine cc-vars cc-defs
smartparens-html sgml-mode dom JJ-web focus-autosave-mode JJ-security
colir color counsel jka-compr esh-util rainbow-delimiters
evil-cleverparens evil-cleverparens-text-objects evil-cleverparens-util
paredit lispyville lispy swiper iedit iedit-lib multiple-cursors-core
lispy-inline avy semantic/db eieio-base semantic/util-modes
semantic/util semantic semantic/tag semantic/lex semantic/fw mode-local
cedet ediff-merg ediff-wind ediff-diff ediff-mult ediff-help ediff-init
ediff-util ediff help-fns radix-tree lispy-tags elisp-slime-nav
eval-sexp-fu company-quickhelp warnings highlight font-lock+
flycheck-pos-tip pos-tip flycheck json map find-func shackle
JJ-extra-lang make-mode JJ-elisp edebug-x edebug which-func imenu
JJ-autocomplete elixir-yasnippets yasnippet company-oddmuse
company-keywords company-etags etags xref project company-gtags
company-files company-capf company-cmake company-xcode company-clang
company-semantic company-eclim company-template company-css company-nxml
company-dabbrev-code company-dabbrev company-yasnippet company-bbdb
company JJ-evil evil-surround evil-visualstar evil-matchit evil
evil-integration evil-maps evil-commands flyspell ispell evil-jumps
evil-command-window evil-types evil-search evil-ex evil-macros
evil-repeat evil-states evil-core evil-common derived rect evil-digraphs
evil-vars undo-tree diff JJ-project recentf tree-widget wid-edit ivy flx
delsel ivy-overlay ffap JJ-pair-editing smartparens-config smartparens
thingatpt JJ-misc fold-dwim-org fold-dwim hideshow noutline outline
windmove hungry-delete ws-butler JJ-platform exec-path-from-shell
ls-lisp JJ-defaults paren whitespace autorevert filenotify winner
JJ-theme foggy-night-theme cl-extra help-mode theme-changer solar
cal-dst cal-menu calendar cal-loaddefs cl JJ-dependencies hydra ring lv
s dash JJ-funcs subr-x use-package diminish bind-key easy-mmode
finder-inf edmacro kmacro rx advice slime-autoloads info package
easymenu epg-config url-handlers url-parse auth-source cl-seq eieio
eieio-core cl-macs eieio-loaddefs password-cache url-vars seq byte-opt
gv bytecomp byte-compile cconv cl-loaddefs pcase cl-lib time-date
tooltip eldoc electric uniquify ediff-hook vc-hooks lisp-float-type
mwheel term/ns-win ns-win ucs-normalize mule-util term/common-win
tool-bar dnd fontset image regexp-opt fringe tabulated-list replace
newcomment text-mode elisp-mode lisp-mode prog-mode register page
menu-bar rfn-eshadow isearch timer select scroll-bar mouse jit-lock
font-lock syntax facemenu font-core term/tty-colors frame cl-generic
cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao
korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech
european ethiopic indian cyrillic chinese composite charscript charprop
case-table epa-hook jka-cmpr-hook help simple abbrev obarray minibuffer
cl-preloaded nadvice loaddefs button faces cus-face macroexp files
text-properties overlay sha1 md5 base64 format env code-pages mule
custom widget hashtable-print-readable backquote kqueue cocoa ns
multi-tty make-network-process emacs)

Memory information:
((conses 16 892577 534002)
(symbols 48 60548 281)
(miscs 40 882 3402)
(strings 32 161034 279476)
(string-bytes 1 5481778)
(vectors 16 98335)
(vector-slots 8 2434861 513384)
(floats 8 884 2213)
(intervals 56 6906 1373)
(buffers 976 24))