1

I am writing code in R, specifically using the Tidyverse packages. There is extensive use of the pipe %>% to chain functions together. The lines become long, so it is recommended to break them up on this character. I do it manually right now, but I'm sure there's a smarter way of doing it. Here's an example.

iris %>% group_by(Species) %>% summarise(Sepal.Length = mean(Sepal.Length))

I'd like to select this line in visual mode and hit a simple keystroke to convert it to the following

iris %>%
  group_by(Species) %>%
  summarise(Sepal.Length = mean(Sepal.Length))

Perhaps it's asking too much, but it would be really nice if the function arguments could be formatted, as follows.

iris %>% group_by(Species) %>% summarise(Sepal.Length = mean(Sepal.Length),Sepal.Width = mean(Sepal.Width),Species = n_distinct(Species) )

to

iris %>%
  group_by(Species) %>%
  summarise(
    Sepal.Length = mean(Sepal.Length),
    Sepal.Width = mean(Sepal.Width),
    Species = n_distinct(Species)
  )
2
  • Why do you want to select the line in visual mode before entering your shortcut? Are you expecting to be able to apply this to multiple lines at once by selecting more of them? Do you want to be able to apply only to a certain section of a line, while leaving instances of the pipe outside the selection unaffected?
    – Rich
    Commented Apr 29, 2020 at 8:28
  • @Rich No, I guess you're right...just applying it to one line would be fine! Commented Apr 29, 2020 at 10:58

1 Answer 1

3

In the absence of a complete solution, I thought I'd just write a quick answer that shows one way you can achieve the first part of your question.

Try the following mapping (for F3):

nnoremap <F3> :s/%>%/&\r/g<CR>V``j=gv>>

How it works

Splitting the line

First, it breaks the line on the pipes:

:s/%>%/&\r/g
:s/           # substitute
   %>%        # pipes
      /       # with
       &      # the matched text (See ":help s/\\&")
        \r    # followed by a newline
          /g  # everywhere on the line

Reindenting

This splits the line, but doesn't result in your desired indentation:

iris %>%
 group_by(Species) %>%
 summarise(Sepal.Length = mean(Sepal.Length)

So it then reindents using Vim's R filetype's automatic indentation (which is an indent expression) with the following normal mode commands:

V``j=
V      # enter linewise visual mode
 ``    # jump back
   j   # move down one line
    =  # reindent

Indent more!

However, this appears to just align the subsequent lines with the first line — which still isn't what you want — so it then indents these further with:

gv>>
gv    # reselect the previous visual mode selection
  >>  # indent

Going the rest of the way

You should be able to adapt this to write a similar mapping that splits the function parameters into multiple lines, but performing both sets of changes with a single keystroke is probably outside the scope of a single-line mapping: you're likely to need to write a more sophisticated context-sensitive function to do this.

Before embarking on such a venture, I'd definitely do a bit more research into whether anyone else has already solved this problem. You might like to look into Andrew Radev's splitjoin plugin which performs precisely this sort of edit. A brief skim of the README suggests it doesn't support R, but it might be possible to configure or adapt it to do so.

filbranden also suggests ArgWrap in the comments, which looks like it should handle the arguments, but perhaps not the pipes.

Even if neither of the above works for you, it wouldn't surprise me in the least to discover there is another plugin that does do this for R.

6
  • This is great, thank you! To make the lines line up, I had to modify the example text to have only one space after each %>% (I edited the example text to reflect this). This is not a problem though. Commented Apr 30, 2020 at 11:25
  • That's weird. The extra space should have been removed by the == command. If you're able to try running the commands manually one at a time I'd be interested to see where/when the behaviour in your config diverges from mine. Alternatively, you could just use a slightly different regexp pattern to remove all the whitespace when splitting the lines: :s/%>%\s*/g
    – Rich
    Commented Apr 30, 2020 at 13:05
  • See vim-argwrap for a more generic, configurable plug-in to wrap arguments. (Feel free to include thay in the answer.) You can configure it using buffer-local variables, so you can use an ftplugin for R to set the variables in a way that works well for that language.
    – filbranden
    Commented Apr 30, 2020 at 14:21
  • 1
    @filbranden Done. Thanks for the tipoff!
    – Rich
    Commented Apr 30, 2020 at 15:07
  • 1
    External formatter rfmt looks interesting though. Not sure if it's suitable for use with 'formatprg' but maybe with something like :%!rfmt or similar... R is not my language so it's hard for me to give more informed opinions than that...
    – filbranden
    Commented Apr 30, 2020 at 15:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.