Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make Emacs ESS follow R style guide

I've been using Emacs/ESS for quite a while, and I'm familiar with Hadley's R style recommendations. I'd like to follow these conventions in ESS, like those nice spaces around operators, space after comma and after if statement, before curly braces, etc.

Did anyone even bothered to follow this style guide at all? IMHO, official style recommendations are quite modest, and they say nothing about the style whatsoever. Google R style guide are too similar with the ones I use when I code in JavaScript, so it's a no-no.

Long story short: is there anyone with (e)LISP skills willing to implement (Hadley's) style guide for ESS?

like image 237
aL3xa Avatar asked Sep 21 '11 15:09

aL3xa


5 Answers

I don't write Elisp, and I disagree with Hadley about the stylistic merits of underscores. Moreover, Hadley is still lost in the desert of not using the OneTrueEditor so we can expect no help from him on this on this issue.

But if you are open to follow R Core rather than Hadley, below is what the R Internals manual, section 8. "R Coding Standards" recommends. To me, it is R Core who defines R style first and foremost. Google's and Hadley's styles are nice secondary recommendations.

Anyway, back to Elisp. The following has served we well for many years, and I do like the fact that the basic R behaviour is similar to the Emacs C++ style as I happen to look at code in both modes a lot.

[...]

It is also important that code is written in a way that allows others to understand it. This is particularly helpful for fixing problems, and includes using self-descriptive variable names, commenting the code, and also formatting it properly. The R Core Team recommends to use a basic indentation of 4 for R and C (and most likely also Perl) code, and 2 for documentation in Rd format. Emacs (21 or later) users can implement this indentation style by putting the following in one of their startup files, and using customization to set the c-default-style' to"bsd"' and c-basic-offset' to4'.)

 ;;; ESS
 (add-hook 'ess-mode-hook
           (lambda ()
             (ess-set-style 'C++ 'quiet)
             ;; Because
             ;;                                 DEF GNU BSD K&R  C++
             ;; ess-indent-level                  2   2   8   5  4
             ;; ess-continued-statement-offset    2   2   8   5  4
             ;; ess-brace-offset                  0   0  -8  -5 -4
             ;; ess-arg-function-offset           2   4   0   0  0
             ;; ess-expression-offset             4   2   8   5  4
             ;; ess-else-offset                   0   0   0   0  0
             ;; ess-close-brace-offset            0   0   0   0  0
             (add-hook 'local-write-file-hooks
                       (lambda ()
                         (ess-nuke-trailing-whitespace)))))
 (setq ess-nuke-trailing-whitespace-p 'ask)
 ;; or even
 ;; (setq ess-nuke-trailing-whitespace-p t)
 ;;; Perl
 (add-hook 'perl-mode-hook
           (lambda () (setq perl-indent-level 4)))

(The `GNU' styles for Emacs' C and R modes use a basic indentation of 2, which has been determined not to display the structure clearly enough when using narrow fonts.)

I think the only additions I regularly make are to follow the last commented-out snippet:

;; or even
(setq ess-nuke-trailing-whitespace-p t)

You can of course turn off the underscore toggle if you really need to code with underscores.

like image 153
Dirk Eddelbuettel Avatar answered Nov 02 '22 16:11

Dirk Eddelbuettel


With the development version of ESS (to be released in September 2015), just add to your ess-mode-hook:

(ess-set-style 'RStudio)

RStudio also has a 'Vertically align arguments' checkbox that is set by default. If you want to reproduce the behaviour when this setting is unchecked (as in Hadley's code), you'll need to change ess-offset-arguments to prev-line:

(setq ess-offset-arguments 'prev-line)

Please file an issue on https://github.com/emacs-ess/ESS if you find an important discrepancy between ESS and RStudio indentation.

like image 31
Lionel Henry Avatar answered Nov 02 '22 14:11

Lionel Henry


The good point of Hadley's guide is spaceing around operators (except maybe around /)

There is a smart-operator package which implements it for almost every operator.

This is my setup (uncoment operators which you want to use):

(setq smart-operator-mode-map
  (let ((keymap (make-sparse-keymap)))
    (define-key keymap "=" 'smart-operator-self-insert-command)
    ;; (define-key keymap "<" 'smart-operator-<)
    ;; (define-key keymap ">" 'smart-operator->)
    ;; (define-key keymap "%" 'smart-operator-%)
    (define-key keymap "+" 'smart-operator-+)
    ;; (define-key keymap "-" 'smart-operator--)
    ;; (define-key keymap "*" 'smart-operator-*)
    ;; (define-key keymap "/" 'smart-operator-self-insert-command)
    (define-key keymap "&" 'smart-operator-&)
    (define-key keymap "|" 'smart-operator-self-insert-command)
    ;; (define-key keymap "!" 'smart-operator-self-insert-command)
    ;; (define-key keymap ":" 'smart-operator-:)
    ;; (define-key keymap "?" 'smart-operator-?)
    (define-key keymap "," 'smart-operator-,)
    ;; (define-key keymap "." 'smart-operator-.)
    keymap)
  "Keymap used my `smart-operator-mode'.")

See also a nice discussion on R styles here.

[edit] I am also using the defacto camelCase style of R code for globals. The underscore-separated names for local variables - it's easy to differentiate.

There is a special subword mode in emacs which redefines all editing and navigation commands to be used on capitalized sub-words

(global-subword-mode)
like image 6
VitoshKa Avatar answered Nov 02 '22 15:11

VitoshKa


Having the same style preference of the OP, I jumped here and I thank @lionel for your valid suggestions, but I had an issue coming from setting RStudio style in ~/emacs.d/init.el with:

(ess-set-style 'RStudio)

Emacs/ESS refused to apply the style (4-space indent instead of the 2-space, not discussing style here :) ).

My advice for the novice, using the following hook in your ~/emacs.d/init.el:

(add-hook 'find-file-hook 'my-r-style-hook)
(defun my-r-style-hook ()
  (when (string-match (file-name-extension buffer-file-name) "[r|R]$")
    (ess-set-style 'RStudio)))

will do the trick.

like image 4
Giupo Avatar answered Nov 02 '22 16:11

Giupo


One more component of the style guide which hasn't been covered here so far is indentation when a function runs over multiple lines. The style guide says to use indentation like

long_function_name <- function(a = "a long argument", 
                               b = "another argument",   # X
                               c = "another long argument") {
  # As usual code is indented by two spaces.
}

but using any of the default styles seems to indent line X by some fixed number of spaces rather than up to the (. The solution is to set ess-arg-function-offset to some non-number, e.g.

(set 'ess-arg-function-offset t)

This should be placed in a mode hook similar to @Dirk Eddelbuettel's answer. Also it must go after any calls to ess-set-style or it will be overridden.

like image 1
dshepherd Avatar answered Nov 02 '22 15:11

dshepherd