Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix #164] Indentation of blocks inside call arguments #169

Merged
merged 4 commits into from
May 21, 2015

Conversation

lionel-
Copy link
Member

@lionel- lionel- commented May 3, 2015

Since you don't want to add a new setting, I simply replaced the whole (progn) with (current-indentation).

But this restores this behaviour:

list(arg1 = {
    stuff
},
     arg2 = {
         stuff
     }) 

@lionel-
Copy link
Member Author

lionel- commented May 3, 2015

The offending behaviour is now fixed:

list(arg1 = {
    stuff
},
arg2 = {
    stuff
}) 

@vspinu
Copy link
Member

vspinu commented May 3, 2015

Thanks. Let's see what others have to say. I personally think this might be the best way to go given that's it's not a very common case and it's consistent (this time) with R-studio.

(eq (char-after last-sexp) ?{)
(and
;; in case we have additional non-block arguments
(eq (char-before last-sexp) ?\C-j)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these contextual checks for characters are a bit too specific. But If it works now I guess we can improve this later if we see any problems with it.

Could you please make this the default:

list(arg1 = {
    stuff
 }, arg2 = {
    stuff
 })

How about ess-indent-function-block-arguments as an option name? To me it sounds a bit more informative than ess-indent-block-arg-function`.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a perfect name.

About the testing scheme, I'm trying to set up different R files containing the same testing set but with different configurations. To achieve this I tried to specify different file-local variables with the indentation settings. This doesn't work and indeed in the source code of (ess-mode) we have this (kill-all-local-variables) nuking all the variables I'm trying to set (IIUC).

I'm now trying to hack something by setting locally a modified ess-local-customize-alist, but is there a canonical way to do this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To achieve this I tried to specify different file-local variables with the indentation settings.

Why do you need that? Are you using ert?

I was thinking about a very simple scheme:

(ert-deftest test-ess-RRR-indentaton ()
  (find-file-noselect "tests/indent-RRR.R" t)
  (ess-set-style 'RRR)
  (indent-region (poitn-min) (point-max))
  (should (not (buffer-modified-p))))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To bind custom stiles you can do something along:

(let ((ess-stile-alist 
         '(style
               (ess-indent-level . 4)
               (ess-first-continued-statement-offset . 2)
               (ess-continued-statement-offset . 0)
               (ess-brace-offset . -4)
               (ess-expression-offset . 4)
               (ess-else-offset . 0)
               (ess-close-brace-offset . 0)
               (ess-brace-imaginary-offset . 0)
               (ess-continued-brace-offset . 0)
               (ess-arg-function-offset . 4)
           (ess-arg-function-offset-new-line . '(4))
               )))
  (ess-set-style 'style)
  ....
)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok I'll look into ert.

I was thinking of simple R files to load, with file-local vars automatically set. Then we'd indent them and git diff them to check nothing has changed.

but it would be much better if it could be more automatic!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERT looks great :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can see how other packages do that and set up the test infrastructure. cider is a good example. Particularly run-tests script.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks. If I can't finish this tonight this'll have to wait until Friday though.

In the mean time I'm going to open an issue with the test cases I already collected, so people can complete it.

@vspinu
Copy link
Member

vspinu commented May 8, 2015

Good we slept on this one for a while. I have just stumbled on the following annoyance:

with(df, {
         stopifnot(identical(Amb, amb))
         stopifnot(identical(Corr, corr))
       })

which is considerably uglier than the old default

with(df, {
    stopifnot(identical(Amb, amb))
    stopifnot(identical(Corr, corr))
})

The worst part is with examples like the following:

substitute( {
               stopifnot(identical(Amb, amb))
               stopifnot(identical(Corr, corr))

We are already treating ({ specially just for this case but I don't see the end of it. So let's just revert to old behavior and be done with it. I would rather not maintain such exceptions for a style which is unlikely to be used by anyone.

@lionel- If you don't have anything else to add, please squash everything into one commit and I will merge.

@lionel-
Copy link
Member Author

lionel- commented May 8, 2015

I think we should merge the testing framework first, it's finished.

It allowed me to discover that there is a bug in this PR with closing braces:

## 11
fun <- fun_call({
    stuff
}, {
    stuff
},
{
    stuff
}
                )

## 14
fun_call( {
    body
}
         )

@kevinushey
Copy link
Contributor

Is this issue that RStudio provides this indentation:

list(arg1 = {
  stuff
},
arg2 = {
  stuff
}) 

when this form would be preferred (when vertical alignment is turned on):

list(arg1 = {
    stuff
},
     arg2 = {
         stuff
     }) 

Honestly, given that 'style' I am not sure if there is an indentation that everyone could agree on, e.g. I would prefer to write with each argument on a separate line

list(
  arg1 = {
    stuff
  },
  arg2 = {
    stuff
  }
)

which I imagine both ESS and RStudio would agree is the 'right' indentation.

IMHO, the best indentation for the original example (with vertical alignment on) would be something like:

list(arg1 = {
       stuff
     },
     arg2 = {
       stuff
     })

which is at least 'consistent'. But all forms of this indentation (where arg1 lies on the same line as the list( call look 'ugly' to my eyes.

@lionel-
Copy link
Member Author

lionel- commented May 8, 2015

On the other hand Kevin, imagine how testthat files would look with this kind of indentation... Also, inline lambda functions (defined in mapping functions such as lapply() for example) look best when the indentation is cropped.

Maybe opinions on this matter depend on the programmer's setup. If you work on a laptop with the screen splitted in two, you can't afford wasting horizontal spacing for lambdas and test_that() definitions.

There are no easy answer here so maybe making it a setting is best.

@kevinushey
Copy link
Contributor

Very good point! I agree entirely.

@lionel-
Copy link
Member Author

lionel- commented May 10, 2015

@vspinu

I fixed all remaining bugs and now misc1 can be indented without error. One of the bug occurred when ess-arg-function-offset-new-linewas set to a number. Ultimately it was caused by a problem with (forward-sexp).

To see it, put your cursor on the opening paren in the following snippet and try to call (forward-sexp -1):

object[(
        index)]

Now I don't know if we want to fix that by defining our own sexp motion functions. Anyway, I realised that since the open paren is not a function call, ess-arg-function-offset-new-line probably has no business here. So I modified (ess-looking-at-last-open-paren-p) to recognise function calls. This circumvents the (forward-sexp) bug but this does change the RRR style a bit, so could you take a look? Before you'd have:

fun_call(parameter = (
    stuff
    ),

Now you have:

fun_call(parameter = (
                      stuff
                      )

This makes sense because stuff is not a function argument here. This pattern should be very uncommon in real code anyway.

Also I noticed that ESS does not consider back-quoted names as R symbols and fixed that. Finally, the last commit implements a setting to turn indentation of block arguments on. It is very easy to implement and works well (see new test file).

@vspinu
Copy link
Member

vspinu commented May 10, 2015

I realised that since the open paren is not a function call, ess-arg-function-offset-new-line

I think we should use the same indentation on open parenthesis irrespective if it's a function call or not. Moreover, I think we should do the same on [

fun_call(parameter = (
       stuff
       )
data_table[
     stuff
     ]

Instead of adding new offsets, I would rename ess-arg-function-offset-new-line into something more generic and more sugestive. How about ess-offset-after-last-([?

To see it, put your cursor on the opening paren in the following snippet and try to call (forward-sexp -1):

That's how forward-sexp works. We are commonly including it into ignore-errors, and I don't think this is an exception here. There is no previous sexp to move over.

@lionel-
Copy link
Member Author

lionel- commented May 10, 2015

it's an old bug, but you need to set ess-arg-function-offset-new-line to a number to trigger it.

I'll try to circumvent the (forward-sexp) error and use a regexp instead then.

@lionel-
Copy link
Member Author

lionel- commented May 10, 2015

By the way I'm working on roxygen indentation and autofilling code now. Can I use the new advice system of Emacs 24.4? Or should the new ESS releases be compatible with old Emacs versions?

@vspinu
Copy link
Member

vspinu commented May 11, 2015

Or should the new ESS releases be compatible with old Emacs versions?

We are trying to support 24 entirely and ESS apparently works fine with emacs 23. So let's keep the old system for now. The advice system is easy to change, so we will do it in due time.

@lionel-
Copy link
Member Author

lionel- commented May 13, 2015

I think we should use the same indentation on open parenthesis irrespective if it's a function call or not.

I'm now trying to figure out the corner cases.

The documentation of ess-arg-function-offset-new-line says that when the setting is set to a number, the offset starts from the function call. For square brackets we can start from the object name instead. But in the case of lone parentheses, what should we do? I think the consistently general behaviour is to start from the opening parenthesis itself, but then instead of:

object[(
    index)]

we'd have:

object[(
            index)]

That's not ideal but the indentation you want will still be available by setting ess-arg-function-offset-new-line to '(N).

How about ess-offset-after-last-([

We are not allowed those characters in elisp names but it is indeed a good idea to make the settings more self explicit. We could also rename ess-arg-function-offset? It's a very confusing name I think. But isn't renaming settings going to upset users?

Also, since we have all those options pushing the indentation towards the left margin (block arguments, but also the ess-close-paren-offset set to '(N), etc), I'm trying to fix the issues regarding the alignment of subsequent arguments. The only reasonable option I think is to never go further than the previous argument indent. For example, with block indentation turned off:

{
    fun_call(argument,
             argument, {
        stuff
    }
    ,
    argument, argument,
    argument)
}

However it is not sufficient to just check the indentation of the previous line because in many situations it will be over-indented compared to its "natural" indentation. Thus the easiest and most reliable way to find out the right indentation level is to climb over all arguments of the function call and use the minimum indentation found on the way. I implemented such an argument climber and the strategy seems to work well.

@vspinu
Copy link
Member

vspinu commented May 13, 2015

For square brackets we can start from the object name instead.

Yes. That makes a lot of sense.

I think the consistently general behaviour is to start from the opening parenthesis itself, but then instead of:

Why? The point of this setting is to indent ( and [ the same if they are ate the end of the line.

We are not allowed those characters in elisp

Of course:) Lisp is so permisive that at some point you think that everything is allowed.

We could also rename ess-arg-function-offset?

I agree.

But isn't renaming settings going to upset users?

We will alias and deprecate those.

However it is not sufficient to just check the indentation of the previous line because in many situations it will be over-indented compared to its "natural" indentation.

Are you sure? Which example do you have in mind? If last arg declaration ended in }, it should be fine. It worked correctly a couple of days ago, right?

@lionel-
Copy link
Member Author

lionel- commented May 13, 2015

Are you sure? Which example do you have in mind?

Here is one example where looking at the previous line does not work:

{
    fun_call(argument, {
        stuff
    },
    parameter = fun_argument(arg,     # this one! <--
        sub_fun(arg,                  # not this one
                param = sub_fun(arg,  # not this one
                    arg               # not this one
                    ),                # not this one
                arg                   # not this one
                ), arg                # not this one
        ), arg,                       # not this one
    my_argument, # --> Which previous line informs the indentation?
    argument,
    argument, argument,
    argument)
}

What about renaming ess-arg-function-offset-new-line to ess-offset-arguments-after-hard-return? Then the new Rstudio style setting could be called ess-offset-arguments, set to nil by default.

And ess-arg-function-offset is about indenting parameters of inner functions from the parameter name of an outer function. So it could be renamed to ess-indent-inner-args-from-outer-arg-name. This is lengthy but the principle behind this setting is also complicated... Speaking of which, we could simplify it by making it a boolean, and then we'd indent according to the two ess-offset-arguments-* options. Would make things easier and more consistent on the whole.

## ess-offset-arguments-after-hard-return = 4
fun_call1(argument = fun_call2(
              subarg
              )  # closing parents does not go further than minimum indent
          )

## ess-offset-arguments = 4
fun_call1(argument = fun_call2(subarg1,
              subarg2
              )
          )

And it would be ignored in all following situations:

## ess-offset-arguments-after-hard-return = '(4)
fun_call1(argument = fun_call2(
    subarg
    )
    )

## ess-offset-arguments = '(4)
fun_call1(argument = fun_call2(subarg1,
    subarg2
    )
    )

## ess-offset-arguments = nil
fun_call1(argument = fun_call2(subarg1,
                               subarg2
                               )
          )

Why? The point of this setting is to indent ( and [ the same if they are ate the end of the line.

Should we settle for the closest opening delim attached to a name then?

{
    fun_call(argument, (
        (         # indented from fun_call(,
        (         # the closest named opening delim
        object[(
            (
            index  # indented from object[
            )
            )
            ]
        )
        )
    ))
}

What about parentheses at top level?

(
ggplot(...) +
    geom(...)
)

These are all weird corner cases but I think it's still important to get them consistently indented. This is the only way to avoid weird indentations in complicated situations we did not think about.

@vspinu
Copy link
Member

vspinu commented May 13, 2015

What about renaming ess-arg-function-offset-new-line to ess-offset-arguments-after-hard-return? Then the new Rstudio style setting could be called ess-offset-arguments, set to nil by default.

And ess-arg-function-offset is about indenting parameters of inner functions from the parameter name of an outer function. So it could be renamed to ess-indent-inner-args-from-outer-arg-name. This is lengthy but the principle behind this setting is also complicated... Speaking of which, we could simplify it by making it a boolean, and then we'd indent according to the two ess-offset-arguments-* options. Would make things easier and more consistent on the whole.

Yes. I like all of the above proposals, but can we make -hard-return more intuitive? How about ess-offset-arguments-after-last-open-delim?

Should we settle for the closest opening delim attached to a name then?

Ok. Now I see your point. I don't think that's a good idea.

How about we check if open delims are grouped at the end of the line and indent according to the leftmost. If leftmost is not lonely they we respect ess-offset-arguments-after-hard-return:

some_data[(
    stuff)]

some_data[
    (
        stuff)]

Would this work?

@lionel-
Copy link
Member Author

lionel- commented May 13, 2015

I think I found a way to simplify things further and make the indentation logic more consistent at the same time.

First, we change the behaviour of ess-offset-arguments (from what is proposed in #168) in the case where it's set to a number. This makes it more useful and will help us with closing paren as well (see below):

  • when nil, indent from opening paren
  • when a number, indent from closest inner function call
  • when a list '(N), indent from furthest outer function call

ess-close-paren-offset could be renamed to ess-offset-closing-paren and we could remove the list '(N) form for this setting to simplify things. Then:

  • when ess-offset-arguments is nil, indent from opening paren (as things are now)
  • when ess-offset-arguments is a number, indent from closest inner function call
  • when ess-offset-arguments is a list '(N), indent from furthest outer function call
## ess-offset-arguments = nil
## ess-offset-closing-paren = 0
{
    fun_call(argument1, fun_call(subarg1,
                                 subarg2
                                 ),
             argument2
             )
}
## ess-offset-arguments = 4
## ess-offset-closing-paren = 0
{
    fun_call(argument1, fun_call(subarg1,
                            subarg2
                        ),
        argument2
    )
}
## ess-offset-arguments = '(4)
## ess-offset-closing-paren = 0
{
    fun_call(argument1, fun_call(subarg1,
        subarg2
    ),
    argument2  # pushed off from its natural indentation
    )
}

@lionel-
Copy link
Member Author

lionel- commented May 13, 2015

Would this work?

I think so. In the second example, we treat the lone ( as if it were a curly brace?

@vspinu
Copy link
Member

vspinu commented May 13, 2015

ess-close-paren-offset could be renamed to ess-offset-closing-paren and we could remove the list '(N) form for this setting to simplify things.

Yes. I think the simplification that you proposed is the way to go. On this occasion we can rename all our offsets to start with ess-offset- for consistency

All these changes make my head reel. I am losing track already. You will have to document all the changes in etc/newfeat.texi once this is done.

@vspinu
Copy link
Member

vspinu commented May 13, 2015

I think so. In the second example, we treat the lone ( as if it were a curly brace?

How about treating all open delimiters the same? We can drop a bunch of offsets then (ess-brace-offset, ess-close-brace-offset, ess-brace-imaginary-offset (what on earth is that? If it's imaginary I am sure we can get rid of it).

@lionel-
Copy link
Member Author

lionel- commented May 13, 2015

yes it's really hairy but I think these changes will make things clearer.

How about treating all open delimiters the same?

And have them all behave as a function of ess-offset-arguments as proposed above? This seems like a good idea, I'll see if it works.

If it's imaginary I am sure we can get rid of it

yes please! Never understood the purpose of this setting. I also don't understand what ess-expression-offset is for.

And could we get rid of ess-arg-function-offset altogether? It will not be as useful with the new ess-offset-arguments options and I think this the most confusing setting in the indentation logic (IIRC there are several confused users showing up on stackoverflow and the mailing list about this setting). I understand removing this one might be more difficult because it will make the default RRR behaviour change a bit.

@lionel-
Copy link
Member Author

lionel- commented May 13, 2015

It will not be as useful with the new ess-offset-arguments options

To be more clear, ess-arg-function-offset is about saving horizontal space but if a user is worried about that she can set ess-offset-arguments to '(N).

stuff
),
argument)
argument1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one doesn't seem to be right. Do you indent with indent-tabs-mode set to nil? Github seems to screw tab indentation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is actually right. But do we want to push the inner call argument all the way to beginning of the previous line?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we do. This is the only way to be consistent and to me it looks better this way.

Here we have inner arguments pushed off to the last indentation (here, the zeroth column) because the arguments-newline is '(t). Since the closing delimiter offset is 0 (which will soon be hardcoded), we go all the way to bol at the next line.

But it wouldn't be hard to make the pushing behaviour a setting if you want.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it wouldn't be hard to make the pushing behaviour a setting if you want.

I thought that you already have a setting to indent arguments of the inner call.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it's ess-offset-arguments set to t. Should this be the default for RRR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have ess-indent-from-outer-parameter which seems not be respected here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because this one only makes sense when the offset type is t, as opposed to '(t) (as in here) or nil.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I be clearer about this in the doc string?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I think I finally started getting it. So arguments-newline take precedence everywhere right? So it's only when you keep an argument on the same line then ess-indent-from-outer-parameter kicks in. That's indeed consistent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more or less :)

I rewrote the docstring:

This setting only has an effect when indentation of arguments is
relative to the innermost function call. That is, when
ess-offset-arguments or ess-offset-arguments-newline is set to a
number N as opposed to a nil value or a list '(N).

@vspinu
Copy link
Member

vspinu commented May 20, 2015

I suggest that after the DEFAULT problem and naming conventions are settled down, I merge this up, tag it, and we go for next round of simplification and try to remove unnecessary offsets and other clutter. We can also get feedback from users in meanwhile.

I am also not sure that keeping ess-indent-level outside of styles is a good thing. I like that C-c C-e s sets everything at once, so I can quickly switch between different styles.

Your intent was to make all styles to pick up the default ess-indent-level, right? This causes the DEFAULT style delema If we go on that path, the tradeoff is that we will have to either ask old users to set their ess-indent-level to 2 or we have to set ess-indent-level in DEFAULT style. We don't want to break users who use DEFAULT style, nor we want this style not to pick up its values from the default setting of the variables.

@lionel-
Copy link
Member Author

lionel- commented May 21, 2015

I am also not sure that keeping ess-indent-level outside of styles is a good thing.

But it is part of styles. Each style sets its own indent level.

@vspinu
Copy link
Member

vspinu commented May 21, 2015

But it is part of styles. Each style sets its own indent level.

Ok. Then let's not change the default ess-indent-level.

Seems like I got confused with the new (t) feature. So if the style sets ess-indent-level, all (t) and t settings should pick that level. Clear.

argument2
)
,
argument3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well. Take this example. Right now (because ess-offset-arguments = nil, and ess-offset-arguments-newline = (t)) I get either this

fun_call(parameter = fun_argument(argument1,
                                   argument2
                                   )

or this


fun_call(parameter = fun_argument(
    argument1,
    argument2
 )

I cannot get anything in between because outer-parameter setting is ignored.

So my concern is three fold. First, after { this makes sense, does it really make sense after ( given that it's a very common pattern and all subsequent arguments will be shifted to 0. Second, it's not the current behavior, and folks will complain. Third, is there a way to make it


fun_call(parameter = fun_argument(
             argument1,
             argument2
         )

without affecting the indentation after last {?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To get the last behaviour you set ess-offset-arguments-newline to t instead of '(t). Should it be the default for RRR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be nice if we had ouuter-parameter as follows:

If nil, no effect at all (other variables set the indent).

If t,

   some_function(parameter = other_function(
                                 argument
                             ))

If (t):

   some_function(parameter = other_function(
                     argument
                 ))

And having it take precedence over offset-argument and offset-argument-newline unconditionally?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that indentation after { is controlled by ess-offset-block and is independent of all this. ess-indent-from-outer-parameter also has an effect for blocks, which I forgot to mention in the doc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To get the last behaviour you set ess-offset-arguments-newline to t instead of '(t). Should it be the default for RRR?

But does this affect indentation of {?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm... I don't think it's a good idea. I don't see where the problem is with how things are currently? You can get the behaviour you want by setting ess-offset-arguments-newline to t and ess-indent-from-outer-parameter to t as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have three main settings: blocks, arguments, and arguments after a newline. With your proposition, setting outer-parameter to t would override all three to be t. We loose a lot of flexibility.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But does this affect indentation of {?

No, this is controlled independently by ess-offset-block.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if iindentatioin of { is not affected then I am completely fine with the current situation. I just thought that you took my older suggestion of makeing all open delimiters behave identically litteraly.

The only thing that I disagree is the new defaults for RRR. It should be very close to the current one to avoid disturbing people. So yes, setting ess-offset-arguments-newline to t is the way to go.

@lionel-
Copy link
Member Author

lionel- commented May 21, 2015

here you go @vspinu. The styles look a lot tidier now that those delimiter offsets are gone :)

vspinu added a commit that referenced this pull request May 21, 2015
@vspinu vspinu merged commit 9ca0ecf into emacs-ess:master May 21, 2015
@vspinu
Copy link
Member

vspinu commented May 21, 2015

Ok. So I guess I hurried a bit up. The stuff breaks a couple of cases in ESS-R-bugs.R. I am surprised new test files don't capture that. Some of those seem to be related to comments, so should be easy to fix.

For instance (with RRR) style:

## 9
if (require(lme4)) {
    ## Model in p. 213
                   (fm1 <- lmer(logFEV1 ~ age + log(height) + age0 + log(height0) + (age | id),
                                data=fev1, subset=logFEV1 > -0.5))
    ## Table 8.3
    VarCorr(fm1)$id * 100

    ## Model in p. 216
    (fm2 <- update(fm1, . ~ . - (age | id) + (log(height) | id)))
}
## 16
parentContainer <-
    if(is.null(.getPrototype(.Object@host))) emptyenv()
else sdf
### ^-- here
## 14
a <- some.function(
         arg1,
         arg2)
##  ^--- RRR has ess-offset-argument-newline (4)
## 17
{
    my.long.Expression <- expression(
                              x[a[j]] == exp(theta[1] + theta[2]^2),
                              x[b[i]] == sin(theta[3] ~~ theta[4])
                          )
    ausdruck <- expression
    my.long.Expr...... <- ausdruck(
                              x[a[j]] == exp(theta[1] + theta[2]^2),
                          )
}

@lionel-
Copy link
Member Author

lionel- commented May 21, 2015

alright I'll take a look

@lionel-
Copy link
Member Author

lionel- commented May 21, 2015

@vspinu
I think we have a dilemma here. Before we had

    my.long.Expression <- expression(
        x[a[j]] == exp(theta[1] + theta[2]^2),
        x[b[i]] == sin(theta[3] ~~ theta[4])
        )

but now with ess-offset-arguments-newline set to t we have:

    my.long.Expression <- expression(
                              x[a[j]] == exp(theta[1] + theta[2]^2),
                              x[b[i]] == sin(theta[3] ~~ theta[4])
                          )

One way out would be to implement an idea that I had for the future, which is to allow different styles depending on the depth of embeddedness of expressions. For example we could decide to have '(t) at level 0 and t beyond. This way we'd have:

object <- fun_call(
  arg1,
  arg2
)

and

object <- fun_call(
  arg1, other_call(
          arg2,
          arg3
        ),
  arg4)

Under this scheme we'd solve the problem above. What do you think?

@vspinu
Copy link
Member

vspinu commented May 27, 2015

I think we need to change RRR style a bit. Currently we have non-null ess-offset-continued which leads to rather long staircases sometimes. I am setting it to 0 myself and using ess-offset-continued-first instead.

The intention of RRR is to mimic common R style. R inferior indents like

event ~ product + reg2a + reg2b + reg2c + days + notseen + start_day + 
    ddays + eddays + eyddays + enddays + mddays + sddays + rmaxddays + 
    rPdays + rPweeks + rCdur.day + eycdur + mcdur + scdur + dN + 
    rN + eN + eyN + ePN05 + ePN611 + ePN1217 + ePN1823 + rPans + 
    rPmob + rN.day + ecdisr + ecrtry + ecfrust + mcdisr + mcrtry + 
    mcfrust + ecost + mcost + edvst_Ncntr2 + edvst_Nid2 + rNcntr2 + 
    rNid2 + lat + lon + plc_income + plc_popul + plc_age + plc_Pprov

instead of what we do

event ~ product + reg2a + reg2b + reg2c + days + notseen + start_day + 
    ddays + eddays + eyddays + enddays + mddays + sddays + rmaxddays + 
        rPdays + rPweeks + rCdur.day + eycdur + mcdur + scdur + dN + 
            rN + eN + eyN + ePN05 + ePN611 + ePN1217 + ePN1823 + rPans + 
                rPmob + rN.day + ecdisr + ecrtry + ecfrust + mcdisr + mcrtry + 
                    mcfrust + ecost + mcost + edvst_Ncntr2 + edvst_Nid2 + rNcntr2 + 
                        rNid2 + lat + lon + plc_income + plc_popul + plc_age + plc_Pprov

RStudio also indents like the first case above.

We also want to minimize unnecessary differences introduced by comments. Currently comments are not respecting ess-offset-continued and, thus, each time you comment a line you will need to re-indent subsequent lines.

    product +
        ## batch +
        ## area +
        reg2a + 
            reg2b +
                reg2c + 
                    days +
                        notseen +
                            start_day + 

I think this is so bad that I doubt we should use ess-offset-continued in any of our default styles.

So, if there are no objections I would like to change that.

@lionel-
Copy link
Member Author

lionel- commented May 27, 2015

I'm even wondering if there are any happy users of this continuation style?

@vspinu
Copy link
Member

vspinu commented May 28, 2015

Yeh. In retrospect, we should have changed it when the predecessor of ess-offset-continued-newline was introduced a couple of years ago. Back then it was kept for consistency reasons what are not there anymore with the new engine. So I am going ahead and changing this.

There is one last thing that I am not completely happy about. I would like a bit nicer names for ess-indent-function-declaration and ess-indent-from-lhs. The first one is not intuitive, the second is imprecise. With the second it's easy, ess-indent-arguments-from-lhs would do the job because it's what it really does. With the first one is a bit trickier.

I have made some cleanups and renaming. The whole system is now documented in ess-styles-alist. Could you please have a look and tell me if you have any ideas about the names of those variable. Thanks.

@lionel-
Copy link
Member Author

lionel- commented May 28, 2015

ess-indent-arguments-from-lhs would do the job because it's what it really does

hmm, not since the generalisation of this setting to assignments with <-. And this setting could be generalised further to all horizontal continuations in the future. So I think the introduction of arguments would be misleading.

As for the other setting, it's hard to find another name, especially if we need to keep the ess-indent prefix. Maybe ess-indent-override-function-declarations?

@mmaechler
Copy link
Member

I think it is really good to think about good names for these.
One thing I hate in any code, including emacs-lisp, are too long names. After all, we have docstrings to describe the exact meaning of variable (or function) names.
Code should remain somewhat concise and that's typically not the case with names that are longer than say 25 characters. Yes, it is a matter of taste, too.

@vspinu
Copy link
Member

vspinu commented May 28, 2015

hmm, not since the generalisation of this setting to assignments with <-.

Why not? Those re still arguments what are aligned.

In any case, you are probably right that keeping this one generic will probably serve us well in the future. So let's keep this one as it is. @mmaechler should also like the short name :)

it's hard to find another name, especially if we need to keep the ess-indent prefix.

We need ess-indent- for consistency.

Maybe ess-indent-override-function-declarations?

Override can refer to anything. Also function-declaration is not specific enough. I think declaration-arguments is better.

How about ess-indent-rigid-declaration-arguments or ess-indent-declaration-arguments-rigidly? Or we can suggest the alignment: ess-indent-declaration-arguments-vertically.

@lionel-
Copy link
Member Author

lionel- commented May 28, 2015

ess-indent-declaration-arguments-rigidly looks good!

or should it be ess-indent-arguments-declaration-rigidly?

@vspinu
Copy link
Member

vspinu commented May 28, 2015

or should it be ess-indent-arguments-declaration-rigidly?

Then it should be ess-indent-argument-declarations-rigidly. Plural form of nouns as adjective is not common in English.

So in the first case (ess-indent-declaration-arguments-rigidly) you indent arguments which are special type of arguments - declaration arguments. In the second case (ess-indent-argument-declarations-rigidly) you indent declarations, which are special kind of declarations - argument declarations. Both can be, but given that we want to emphasize that we are indenting arguments, the first form is better IMO.

@lionel-
Copy link
Member Author

lionel- commented May 28, 2015

you're right, sounds good to me then.

vspinu added a commit that referenced this pull request May 29, 2015
@lionel- lionel- deleted the block-indent branch July 26, 2015 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants