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

Method Params #47

Closed
firedev opened this issue Oct 6, 2014 · 21 comments
Closed

Method Params #47

firedev opened this issue Oct 6, 2014 · 21 comments

Comments

@firedev
Copy link

firedev commented Oct 6, 2014

It would be great if split join could do something like extracting params:

File.join(Rails.root, 'db/seed', "#{class_name.to_s.underscore}.yml")

File.join(
  Rails.root, 'db/seed', "#{class_name.to_s.underscore}.yml"
)
File.join(
  Rails.root, 
  'db/seed',
  "#{class_name.to_s.underscore}.yml"
)

Related to tpope/vim-surround#140

@firedev
Copy link
Author

firedev commented Oct 7, 2014

I managed to extract parameters with vim-surround using ysi(<C-J> but then I need a final motion to break the line at commas:

  def user_params
    params.require(:user).permit(:name, :email, :link, :phone, :password, :password_confirmation)
  end
  def user_params
    params.require(:user).permit(
    :name, :email, :link, :phone, :password, :password_confirmation
#   ^ break this line at every comma
    )
  end

I would like to have something like this in the end:

  def user_params
    params.require(:user).permit(
      :name,
      :email,
      :link,
      :phone,
      :password,
      :password_confirmation
    )
  end

Nothing :s couldn't do, but thats a bit of a menial labor and then you would have to re-indent again.

@AndrewRadev
Copy link
Owner

Well, theoretically, it would be possible, but right now, splitjoin breaks down options, and not arguments. So basically:

method_call "one", "two", three: "four", five: "six"

# splits into

method_call "one", "two", {
  three: "four",
  five:  "six",
}

This is probably the most common split you'd want. Now, it would be perfectly possible to also split the positional arguments, but in the above case, it wouldn't really look that good:

method_call(
  "one",
  "two",
  {
    three: "four",
    five:  "six",
  }
)

Or something like this, anyway. Of course, if you don't have options in there, it's a lot better:

method_call(
  "one",
  "two"
)

So it would be possible to implement this, but only if there are no options in the method call. Do you think this makes sense?

@AndrewRadev
Copy link
Owner

I've pushed a simple implementation of splitting to the splitting-ruby-arguments branch. You could check it out and let me know if it works well for your use case.

@firedev
Copy link
Author

firedev commented Oct 25, 2014

Thanks! I always miss that, when line gets too long first I put params on a separate line and if gets too long again – put them vertically.

@firedev
Copy link
Author

firedev commented Oct 25, 2014

Hmm, almost there. Few suggestions though...

First a bug: the following line cannot be split regardless of cursor position:

params.require(:article).permit(:title, :meta, :slug, :lead, :body, :category_id)

In regards to functionality, here is how it would work in my world:

.permit(:title, :meta, :slug, :lead, :body, :category_id)

gS:

.permit(
  :title, :meta, :slug, :lead, :body, :category_id
)

j gS (or even better position cursor inside the split, so j is not needed, see below):

.permit(
  :title,
  :meta,
  :slug,
  :lead,
  :body,
  :category_id
)

... if you get my drift.

Here is what it does now:

.permit(:title,
  :meta,
  :slug,
  :lead,
  :body,
  :category_id)

I believe I am offering a superiour scheme that also adds two different splits. Plus I find super-indented listings weird as always try to use closer 80 chars per line. I saw it splits like this:

xxxxxxxxxxx (:xxxxxxx,
              :xxxxxxx)

I'd rather prefer

xxxxxxxxxxx (
  :xxxxxxx,
  :xxxxxxx
)

Saw another strange split, instead of starting from innermost parens it added new ones around:

▋redirect_to article_path(@article, locale: @article.translations.first.locale)

gS I expect:

redirect_to article_path(
  @article, locale: @article.translations.first.locale
)

And then gS:

redirect_to article_path(
  @article,
  locale: @article.translations.first.locale
)

I get:

redirect_to (
  article_path(@article, locale: @article.translations.first.locale)
)

Thank you very much, I hope you will find the suggestions reasonable if having two additional ways of splitting is not too much to ask. I recently started enjoying Vim and got almost all bases covered, there are some bumps on the road that I hope to rectify.

@firedev
Copy link
Author

firedev commented Oct 25, 2014

Sorry just read your post, didn't notice it at first. I think we're talking about the same thing. It just need to start splitting from the inside of parens.

In regards to your examples:

method_call "one", "two", three: "four", five: "six"

# splits into

method_call "one", "two", {
  three: "four",
  five:  "six",
}

^ Perfect.

 method_call(
  "one",
  "two",
  {
    three: "four",
    five:  "six",
  }
)

^ Looks strange? Actually not at all when you have nested params with a few dozen fields.

Besides, who cares? Those who don't want automation can align everything by hands. I always use gg==G before save and leave file at that. I want to make my life simpler, not harder, otherwise I'd use Emacs.

Code that was splitted should not move after ==-ing the whole file. I think that is a valid goal to aim at.

@firedev
Copy link
Author

firedev commented Oct 27, 2014

For the record: after splitting long-indent style code stays still after ==

params.require(:permission).permit(:title, :action, :subject_type, :subject_id, :own)

gS

  def permission_params
    params.require(:permission).permit(:title,
                                       :action,
                                       :subject_type,
                                       :subject_id,
                                       :own)
  end

Now I do ysi)^J (sort of like change surround from (/) to (\n/\n)) and then fix the indentation with ==. This is what I was asking @tpope about. Maybe this is actually the way to go.

But then I would need to split the the arguments line at commas anyway...

@firedev
Copy link
Author

firedev commented Oct 27, 2014

Ah, here you go! gSysi)^J produces the correct split without re-indenting!

Before:

params.require(:permission).permit(:title, :action, :subject_type, :subject_id, :own)

gSysi)^J

params.require(:permission).permit(
  :title,
  :action,
  :subject_type,
  :subject_id,
  :own
)

Perfect!

I don't know if this can be considered closed now or you might want to add an additional splits for parens and comma separated vars.

@jordwalke
Copy link

Could support for JS methods be added as well?

@AndrewRadev
Copy link
Owner

Okay, let's start from the top

I believe I am offering a superiour scheme that also adds two different
splits. Plus I find super-indented listings weird as always try to use closer
80 chars per line.

I'm not a big fan of this kind of formatting, but it seems to be common. The vim-ruby indentation handles it as well. Still, it would make sense to have a different way to split depending on coding style. I'd say it would be fair to enable:

params.require(:permission).permit(:title,
                                   :action,
                                   :subject_type,
                                   :subject_id,
                                   :own)
params.require(:permission).permit(
  :title,
  :action,
  :subject_type,
  :subject_id,
  :own
)

The way I imagine it is just like the settings of hash splitting: let g:splitjoin_ruby_hanging_arguments = 1 would enable the first style, and = 0 would set the second one. This wouldn't be hard to implement. I assume you'd agree with the idea?

Regarding this example:

redirect_to article_path(@article, locale: @article.translations.first.locale)

redirect_to (
  article_path(@article, locale: @article.translations.first.locale)
)

The problem here is that the plugin looks for the outermost method call (I think. I've forgotten the details, to be honest). This makes sense in most cases, since there's not much of a reason to split a nested method call without splitting the outer one. In this case, it has a method call respond_to which has one argument: article_path(@article, locale: @article.translations.first.locale). That's why it gets split like it does.

It is a bit odd, but it's hard to do it the other way around, and would break many other use cases, I think. One feature I've been sitting on for a while is splitting whatever's marked in visual mode (#32), which would make such ambiguities solvable, at least -- if it doesn't do what you want, simply mark your target and split that. Theoretically, that is. Plus, I have yet to implement it properly.

method_call(
"one",
"two",
{
three: "four",
five: "six",
}
)

^ Looks strange? Actually not at all when you have nested params with a few dozen fields.

That may be so, but it would be hard to decide when to split options only, and when to split arguments. The "looks strange" description is to say: "doesn't look like canonical ruby". Yes, if you have a ton of complex fields in the method call, maybe it would look okay like this (although maybe you want to extract some variables instead, but that's a different topic). But in most cases, you're going to have one or two positional arguments that are variables and a ton of options, so this kind of style would be much, much better (I think):

method_call(one, two, {
  three: "four",
  five:  "six",
  seven: "eight",
  nine:  "ten",
})

But you have only one mapping to split, it's kind of hard to make the guess of which one would look better. I'd rather tackle the more common case and leave it to the user to adjust the unusual situations.

On a related note, maybe I can interest you in my "extract variable" script? :)


Bottom line, I'll add an option to the code to split in either of the above two ways and I'll merge it to master (if you confirm that you're happy with it). I'll only split arguments if there is no option hash at the end -- if there is, that one gets split instead, since it should be a more common use case. Considering that the only cases in which I've ever had really long argument lists are attr_accessor-style methods or strong params in rails, this should work nicely.

@jordwalke, I'll see what I can do.

@AndrewRadev
Copy link
Owner

I've just pushed the new option and some minor fixes to the addition of round brackets to the branch. @firedev, could you give it a final test before I merge it into master?

@firedev
Copy link
Author

firedev commented Nov 24, 2014

Sorry I was on a trip in a place where internet is a luxury, will check it now.

In regards to the script, what is the intended behaviour? I can figure out it extracts and inlines variables somehow, would be great to have some pointers.

@firedev
Copy link
Author

firedev commented Nov 24, 2014

Here are my observations so far. I am not 100% sure they are even related to this branch, but anyway.

Don't want to be nitpicky but with the introduction of Rubocop all code I've written 6 months ago requires some improvements. Not that I didn't know about styling back then, but without constant nagging it's easy to disregard style guide violations here and there.

Example1:

FactoryGirl.create(:inquiry, state: state)

Expected:

FactoryGirl.create(:inquiry, {
  state: state
})

Actual:

FactoryGirl.create(:inquiry,{  # no space after comma
  state: state,                # extra comma
})

Example2:

expect(page).to(have_content('long string goes here'))

Expected:

expect(page).to(
  have_content('long string goes here')
)

Actual: Does nothing.

@AndrewRadev
Copy link
Owner

I just pushed a fix for example 1 (this has been a bug for a while, actually, I've noticed it myself, but never had the time/energy/attention to investigate).

As for example 2, I think you need to put let g:splitjoin_ruby_hanging_args = 0 in your vimrc. Otherwise splitjoin tries to split this in the "hanging" style (the one which aligns all arguments in a single column at the end of the line), which ends up doing nothing in the case of only one argument. If you do set the g:splitjoin_ruby_hanging_args variable to 0, the results should fit the expectation (as long as the cursor is in the relevant brackets, that is).

Could you double-check this?

@firedev
Copy link
Author

firedev commented Nov 30, 2014

Sorry, I was sick the whole week didn't do much work. Will do, thanks!

@AndrewRadev
Copy link
Owner

Ah, yes, regarding the "extract variable" script:

Marking a piece of code (on a single line) and then hitting so would ask you for a variable name and then extract a variable definition on the line above, replacing the selected area with the variable. It's particularly convenient in combination with an "argument" text object, like the one from my sideways.vim plugin.

Pressing si on a variable definition would remove the line and try to replace all occurrences of the variable in the current indent-based scope with the right-hand side.

The mnemonics for the mappings are "o" for "out", "i" for "in", and the "s" is just a prefix I don't use. They can be changed in the first two lines. The patterns for a variable definition for both mappings are also customizable using buffer-local variables, like here: https://github.com/AndrewRadev/Vimfiles/blob/1397687471612b7e1582648488c81740695ba460/ftplugin/go.vim#L9-10.

@firedev
Copy link
Author

firedev commented Dec 1, 2014

s is substitute in vim, do you mean gsi/gso or something?

@AndrewRadev
Copy link
Owner

Take a look at the top of the script:

xmap so :<c-u>call <SID>ExtractVar()<cr>
nmap si :<c-u>call <SID>InlineVar()<cr>

The top mapping is in visual mode (it's not vmap for reasons you can find out if you :help mapmode-x). That means pressing so in visual mode would trigger the variable extraction. The bottom one is si in normal mode, so just the keys s and i when on top of a variable definition.

@AndrewRadev
Copy link
Owner

I've merged the code to master, since I've been using it for a while and it seems mostly stable (I just made a fix for a spacing issue, but other than that). I'll close the issue for now, but feel free to reopen if you find problems.

@firedev
Copy link
Author

firedev commented Dec 24, 2014

Thank you, sorry for the lack of feedback, I've moved from refactoring to writing new code in the last week or two and couldn't collect enough data to report.

@AndrewRadev
Copy link
Owner

No worries, I'm not exactly quick with work on these plugins anyway. If you run into any issues, just let me know.

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

No branches or pull requests

3 participants