12

I'm following the Douglas Crockford's code convention, but I can't get the correct identation in JS mode in Emacs. I tried to customize the indent options of the mode, tried another modes like js3, but nothing seems to work.

When I have parenthesis, and I have to break the expression, Emacs indent like this:

this.offices.each(this.addOfficesToMap,
                  this);

While the convention that I'm following, says that I should leave just 4 spaces when an expression is broken up. So the indentation should look like:

this.offices.each(this.addOfficesToMap,
    this);

Any idea of how I can change the indentation on broken up expressions?

dda
  • 6,030
  • 2
  • 25
  • 34
sanbor
  • 1,264
  • 15
  • 20
  • In this question http://stackoverflow.com/questions/344966/sane-tab-in-emacs I got something. If I press M-i, I get the expected identation. After presing C-h k the help system tells me that the executed command is tab-to-tab-stop. That is what I want as default identation. – sanbor Jun 12 '12 at 03:39
  • 1
    I think your requirement is incorrect: "When a statement will not fit on a single line, it may be necessary to break it. Place the break after an operator, ideally after a comma. A break after an operator decreases the likelihood that a copy-paste error will be masked by semicolon insertion. **The next line should be indented 8 spaces**." (rather than 4). – phils Jun 12 '12 at 05:18

4 Answers4

5

The behaviour you want to change is hard-coded into a function called js--proper-indentation. An inelegant fix to your problem would be to replace the function in your .emacs:

(require 'cl)

(eval-after-load "js" '(defun js--proper-indentation (parse-status)
 "Return the proper indentation for the current line."
 (save-excursion
   (back-to-indentation)
   (cond ((nth 4 parse-status)
          (js--get-c-offset 'c (nth 8 parse-status)))
         ((nth 8 parse-status) 0) ; inside string
         ((js--ctrl-statement-indentation))
         ((eq (char-after) ?#) 0)
         ((save-excursion (js--beginning-of-macro)) 4)
         ((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
                                "[]})]\\|\\_<case\\_>\\|\\_<default\\_>"))
                (continued-expr-p (js--continued-expression-p)))
            (goto-char (nth 1 parse-status)) ; go to the opening char
            (if (looking-at "[({[]\\s-*\\(/[/*]\\|$\\)")
                (progn ; nothing following the opening paren/bracket
                  (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
                            (case (char-after (nth 1 parse-status))
                              (?\( js-paren-indent-offset)
                              (?\[ js-square-indent-offset)
                              (?\{ js-curly-indent-offset))))))
              ;; If there is something following the opening
              ;; paren/bracket, everything else should be indented at
              ;; the same level.

      ;; Modified code here:
              (unless same-indent-p
                (move-beginning-of-line 1)
                (forward-char 4))
      ;; End modified code
              (current-column))))

         ((js--continued-expression-p)
          (+ js-indent-level js-expr-indent-offset))
         (t 0))))  )

I have modified three lines of code towards the bottom of the function. If you want your indentation to be 8 chars instead of 4, change the (forward-char 4) line accordingly.

Note that js--proper-indentation (and the js library) requires the cl.el library, but that using eval-after-load mucks this up. So you need to explicitly require cl in your .emacs for this to work.

Note that this 'solution' hard codes a 4 space indentation only for the situation you indicate, and does not handle nested code at all. But knowing the point in the code that deals with your situation should at least point you towards the bit that needs work for a more sophisticated solution.

Tyler
  • 9,872
  • 2
  • 33
  • 57
  • Really thanks for your help, but isn't working well. First, your code broke the indent-region feature (emacs argues ``Symbol's function definition is void: case`` when I do ``C+M+\``). Second, the hook it doesn't work. According to a friend: (a) he's missing a (lambda () ...). (b) I don't think Emacs has buffer-local bindings. (c) if you did it for every JS buffer you might as well just change the function globally.Finally, I will use tab-stops and forget about a JS auto-indentation. My code in my ~/.emacs is ``(add-hook 'js-mode-hook (lambda () (local-set-key [tab] 'tab-to-tab-stop)))`` – sanbor Jun 13 '12 at 04:09
  • This code works for me on Emacs 24.0.94.1 (run with emacs -Q). So either you are using an older version of Emacs that doesn't support the `case` statement, or there was some sort of copy/paste issue at your end. The hook works for me. I have edited the code to use `eval-after-load` though, which also works on my machine. Your friend is wrong, a) you don't need a lambda here, b) Emacs does have buffer-local bindings (although that isn't needed here), c) I've fixed this issue in my edit. – Tyler Jun 13 '12 at 20:11
  • Could you please create a gist of an .emacs file working with the ``(eval-after-load "js" '(defun js-proper-indentation (parse-status)``approach, because I cant make it work. – sanbor Jun 14 '12 at 03:34
  • I simplified my answer - try copying the edited code block directly into your .emacs – Tyler Jun 14 '12 at 10:17
  • Great! Thanks again for your help Tyler, unfortunately I still getting the ``+: Symbol's function definition is void: case`` message when I do indent-region. I removed my .emacs.d directory and tried with emacs -Q (and doing load-file ~/.emacs to load your script, of course). I'm using GNU Emacs 24.1.50.1 – sanbor Jun 15 '12 at 03:52
  • That's strange. I'm not sure where case is defined, but I haven't altered the section that includes it. I'm away for a couple of days now, I'll take another look next week. – Tyler Jun 15 '12 at 19:16
  • Can you post the file you are testing this with? The only difference between our tests that i can think of is the js source itself. – Tyler Jun 17 '12 at 22:01
  • I gave you the bounty for your time and patience. In my last testing, your code works with simple javascript. But the nasty ``Symbol's function definition is void: case`` happens when you try to indent real-world code, like the [JQuery library](http://code.jquery.com/jquery-1.7.2.js). Just download and open it with emacs, select all and do indent-region. – sanbor Jun 18 '12 at 23:26
  • Hey, I finally isolated the piece of code that generates the error: https://gist.github.com/2961503 Just try to indent the second line. – sanbor Jun 20 '12 at 19:22
  • I found it! js.el requires cl.el, but if you re-evaluate the indentation function from your .emacs before cl is loaded, it's messed up. The solution is to explicitly load cl before running my modification. I fixed the code above, it should work now! – Tyler Jun 20 '12 at 19:33
  • Note that this function sometimes causes emacs to freeze when auto-fill mode is turned on. It's fairly rare, but very annoying. Turning off auto-fill fixes the problem for me. – Bryan Larsen May 23 '13 at 15:13
1

you can try https://github.com/mooz/js2-mode ...it's a fork js2-mode but with some impovements like good indentation...other way is read this article: http://mihai.bazon.net/projects/editing-javascript-with-emacs-js2-mode .. but sincerely it's better idea replace the old js2-mode ..it has several improvements https://github.com/mooz/js2-mode/wiki/Changes-from-the-original-mode ...hope this can help you...

clagccs
  • 2,224
  • 2
  • 21
  • 33
1

You can file a feature request on js3-mode at https://github.com/thomblake/js3-mode/issues

Do you have a link to a style guide?

Tamzin Blake
  • 2,594
  • 20
  • 36
  • Hello there Thom! I think you have filled this issue with the [manual indentation option](https://github.com/thomblake/js3-mode/issues/39). The convention that I'm using is [Douglas Crockford's code convention](http://javascript.crockford.com/code.html). And the situation described in this question can be found in the [Line length section](http://javascript.crockford.com/code.html#line%20length). – sanbor Jun 26 '13 at 19:04
  • That's a tricky case. He says to break expressions after operators, ideally after commas. But he elsewhere says to avoid the comma operator, so I think he was actually thinking of breaking lines after the comma separator. So there are quite a few special cases there. I think this might be solved by just setting js3-boring-indentation t – Tamzin Blake Jun 26 '13 at 20:37
  • And what about if you have a dot? ```var foo = document /n.getElementById('bar');``` When I have to break a line with a dot the situation is pretty nasty, and I understand that I only should use 8 spaces, not align the dot with the broken sentence. – sanbor Jun 27 '13 at 13:05
  • If I understand Crockford's standard properly, he'd put the dot before the line break, and then indent the following line as a continued expression, which should just work. – Tamzin Blake Jun 27 '13 at 17:48
0

BTW, while the indentation conventions vary from language to language, and the preferences can even vary between users (such as in the above case), there is a fair bit of overlap and there are often ways to write your code such that there is little disagreement. E.g. your above code could be written:

this.offices.each(
    this.addOfficesToMap,
    this
);

or

this.offices.each
    (this.addOfficesToMap,
     this);

and most indentation styles would largely agree on how to indent it.

Stefan
  • 27,908
  • 4
  • 53
  • 82
  • Yes, my problem is that I have customized a lot of things but the indentation of a new line, in a breaked expression, always indent the code in that way. I'm not sure if it's possible to customize the indentation so far to match with any convention. Probably you would say that I can create some code in lisp to indent in that situation, but I'm just a newbie :B – sanbor Jun 09 '12 at 17:07