eslint / eslint

Find and fix problems in your JavaScript code.
https://eslint.org
MIT License
24.44k stars 4.41k forks source link

Proposed Rule: Enforce lines between class methods #5949

Closed LinusU closed 6 years ago

LinusU commented 8 years ago

I would like to propose a rule that enforces empty lines between class functions. When enabled and set to "always" it should make sure that there is at least one empty line between every method in a class declaration. If set to "never" the opposite would be true, that is, there should not be any empty lines at all between class methods.

A line with a comment on should not count as an empty line.

This rule should not concern itself with the class padding (empty lines before the first method and after the last method) since that is already taken care of by padded-blocks.

It also shouldn't concern itself with how many empty lines that are present, since that is a job for no-multiple-empty-lines.

This is a stylistic rule.

The following patterns would be considered problems:

class Test1 {
  constructor () {
    // ...
  }
  otherFunc () {
    // ...
  }
}

class Test2 {
  constructor () {
    // ...
  }
  // comment
  otherFunc () {
    // ...
  }
}

The following pattern would be considered valid:

class Test3 {
  constructor () {
    // ...
  }

  otherFunc () {
    // ...
  }
}

class Test4 {
  constructor () {
    // ...
  }

  otherFunc () {
    // ...
  }
}

class Test5 {
  constructor () {
    // ...
  }

  // comment
  otherFunc () {
    // ...
  }
}

class Test6 {
  constructor () {
    // ...
  }

  // Comment

  otherFunc () {
    // ...
  }
}
LinusU commented 8 years ago

I think that this rule should be able to handle comments as well, the following pattern should be considered valid:

class Test4 {
  constructor () {
    // ...
  }

  // blah blah
  otherFunc () {
    // ...
  }
}
LinusU commented 8 years ago

Come to think about it, I think that always should allow multiple empty lines since there is another rule that can take care of that: no-multiple-empty-lines. Furthermore, it might be nice to allow the following:

class Test5 {
  constructor () {
    // ...
  }

  // Comment

  otherFunc () {
    // ...
  }
}
gyandeeps commented 8 years ago

Sounds like a reasonable request. @eslint/eslint-team thoughts?

alberto commented 8 years ago

There may be some overlapping with #3092

nzakas commented 8 years ago

@LinusU can you please spell out your complete proposal? It's a bit hard to follow when separated across multiple comments.

LinusU commented 8 years ago

Absolutely, sorry about that...

(edited the first post)

nzakas commented 8 years ago

Thanks. Per our guidelines, we need someone on the team to champion the rule and three :+1:s.

LinusU commented 8 years ago

Sounds good, I'd be happy to assist if there is anything needed from me πŸ‘Œ

mikesherov commented 8 years ago

I'd say this belongs as a configuration option of #3092

LinusU commented 8 years ago

I'm not sure that I agree with that, it seems like it's going to be a very complex options object if we put everything into that rule. This might be desirable though, I'm not familiar enough with the project to tell.

The thing I like by this rule is that it plays nicely with other rules already in place, including padded-blocks.

class T {
  // padded-blocks
  constructor () {
    // padded-blocks
    this.x = 5
    this.y = 10
    // padded-blocks
  }
  // lines-between-class-methods
  moveRight () {
    // padded-blocks
    this.x += 10
    // padded-blocks
  }
  // lines-between-class-methods
  moveDown () {
    // padded-blocks
    this.y += 10
    // padded-blocks
  }
  // padded-blocks
}

The style guide that we wanted to implement in standard would have padded-blocks to never and lines-between-class-methods to always. So that it looks like this:

class T {
  constructor () {
    this.x = 5
    this.y = 10
  }

  moveRight () {
    this.x += 10
  }

  moveDown () {
    this.y += 10
  }
}

Which in my opinion looks very nice. For that to be supported with padded-blocks there would have to be a separate setting for spaces between class methods, which would be this exact rule but as an option. I personally think that it would clutter up the padded-blocks rule, but that's of course only my opinion.

Thoughts?

ilyavolodin commented 8 years ago

@eslint/eslint-team Anyone willing to champion this and support it?

mysticatea commented 8 years ago

Maybe I'm willing. I think this is one of JSCS compatibility rules, for a part of requirePaddingNewLinesAfterBlocks and disallowPaddingNewLinesAfterBlocks. Those rules enforce blank line style after all blocks, so it checks blank line(s) between class methods also.

I have some concerns.

LinusU commented 8 years ago

2 options are needed for this proposed rule to reproduce the behavior of JSCS rule.

I'd be happy to add that, are you thinking something like "always", "always-excpet-single-line-methods", "never", or something like { "single-line": "never", "multi-line": "always" }?

What about lines-between-class-members instead of lines-between-class-methods?

I personally would not like to enforce a blank line between every property if that lands. And I don't think that it's common to want that...

Consider the following example, it would look very spare if it required a newline between the props...

class Dog {
  x = 0
  y = 0

  name = 'Barkly'
  mood = 'Happy'
  breed = 'American Cocker Spaniel'
  origin = 'United States'

  constructor () {
    // ...
  }

  bark () {
    // ...
  }
}
mysticatea commented 8 years ago

Thank you for the fast reply!

I'm thinking something like:

{
    "lines-between-class-methods": ["error", "always" or "never"]
    // or
    "lines-between-class-methods": ["error", {
        "multiLine": "always" or "never",
        "singleLine": "always" or "never"
    }]
}

I personally would not like to enforce a blank line between every property if that lands. And I don't think that it's common to want that...

I agreed. But,

mikesherov commented 8 years ago

I'm willing to champion. I think this rule should just consider class methods. The analogy is that we have rules for functions and separate rules for variable declarations, and keep them separate.

nzakas commented 7 years ago

Sounds good. :+1:

LinusU commented 7 years ago

@mikesherov feel free to tell me how you want me to update my pull request to get this landed :+1:

mysticatea commented 7 years ago

Hmm, I'm OK that this name is kept, also.

:+1:

nzakas commented 7 years ago

Still looking for one more πŸ‘ To move forward

IanVS commented 7 years ago

Instead of "always" and "never" (or maybe in addition to), how about an object with min and max properties? That might be more flexible. For instance, I would like to enforce exactly one line between class methods, but I already have no-multiple-empty-lines set with a max of two empty lines (which I use in some cases to separate larger code blocks).

So, "always" would be an alias to {min: 1}, and "never" is equivalent to {max: 0}, but it would also be possible to have {min: 1, max: 1} to specify exactly one line as I want.

Might an option like this have merit?

LinusU commented 7 years ago

@IanVS I think that if we do that we might be getting a bit of unwanted overlap with the no-multiple-empty-lines. I'm not saying I'm against your idea, just the if you specify e.g. max: 2 in the rule, and you have no-multiple-empty-lines configured to only allow one consecutive empty line, what should happen? I think that ESLint has a policy to not try and have overlap between rules, however that would be best answered by someone on the team. Just my two cents...

hzoo commented 7 years ago

I see lines-between-class-methods - is there already a similar rule for objects as well?

Also class properties aren't supported in the default parser anyway (through babel-eslint) class A { asdf = 1}

nzakas commented 7 years ago

@hzoo we don't have anything for objects.

@ianvs I think always and never follows our other rules better, like padded-blocks and avoids the clash with max-empty-lines that @LinusU mentioned.

We still need one more πŸ‘ here.

IanVS commented 7 years ago

Sounds good, I'll give my πŸ‘.

kaicataldo commented 7 years ago

One more :+1: for good measure.

nzakas commented 7 years ago

There's a discussion about whether this should be a standalone rule or part of a larger "spaces after blocks" rule: https://github.com/eslint/eslint/issues/3092

caesarsol commented 7 years ago

Hello, I'd like to join the discussion. Just a couple thoughts:

TheSavior commented 7 years ago

What is the status of this issue?

I'm happy to help contribute to this to push it over the finish line. I wrote the JSCS rule this is discussing and this is one of the last rules left in my company's conversion from JSCS to eslint.

nzakas commented 7 years ago

I think there's still an open question around whether this should be a standalone rule or should be packaged as part of another rule (https://github.com/eslint/eslint/issues/3092). It looks like no one has expressed any strong opinion one way or another, so we should probably get that squared away.

quinn commented 7 years ago

superseded by https://github.com/eslint/eslint/issues/7356

hoetmaaiers commented 7 years ago

I would add this as part of padded blocks.

helgeFox commented 6 years ago

Could this issue be reopened again? It was never resolved by the other issues that closed this one...

platinumazure commented 6 years ago

@eslint/eslint-team I've reopened this since padding-line-between-statements does not include padding between method definitions.

Do we need a padding-line-between-methods rule?

helgeFox commented 6 years ago

Yes! πŸ‘

kaicataldo commented 6 years ago

I'm honestly not sure about this, so just want to bring it up as a discussion point: should this rule also include objects? Maybe with a config option to only enforce between methods?

Example:

class Foo {
  a() {}

  b() {}

  c() {}
}

// with config option to always enforce
const foo = {
  a,

  b,

  c() {},

  d() {}
}

// with config option to only enforce between methods
const foo = {
  a,
  b,
  c() {},

  d() {}
}
helgeFox commented 6 years ago

Good question but I don't know. I guess it would be nice to have in one of the projects I work on, where we use Backbone. In Backbone, at least in this old version we use, classes are declared as objects.

kaicataldo commented 6 years ago

To be clearer, I think I have two discussion questions:

1) Should this include methods in objects? 2) If it should, should we also enforce padded newlines between non-method properties?

platinumazure commented 6 years ago

I'm leaning towards a separate rule for objects, myself. Classes and objects just have a different grammar to them. Even experimental ECMAScript changes that might make classes more like objects (e.g., static properties/initializers) are going in a different direction than how they are handled in objects.

More generally, I could see a sensible pattern of rules for different types of member-containing entities:

And we would create one rule for each of those cases for each construct (object, array, class, block statement, etc.).

Might be thinking too far ahead, but in summary, I think we should handle classes and objects differently.

RobbieTheWagner commented 6 years ago

Seems like this has been open a really long time. Is this being worked on? I would like this to be able to be enforced in objects and classes.

kaicataldo commented 6 years ago

@rwwagner90 I don't believe it's being worked on, but please feel free to make a PR! Looks like the configuration schema that was accepted is https://github.com/eslint/eslint/issues/5949#issuecomment-223238513.

RobbieTheWagner commented 6 years ago

@kaicataldo did this get the required thumbs up? I definitely wouldn't want to spend time on this, if it's not going to be merged in.

platinumazure commented 6 years ago

@rwwagner90 This issue is accepted, so we should accept any PRs submitted (as long as the author is willing to work with us when we suggest changes or request tests, etc.).

By the way, you can just look for the "accepted" label in future to know if an issue has the required thumbs up. Otherwise, we use the "evaluating" label for issues that haven't yet met that requirement. Hope this helps!

aladdin-add commented 6 years ago

Iβ€˜ll work on this, perhaps this weekend.:(

RobbieTheWagner commented 6 years ago

@Aladdin-ADD if you have time, that would be great! If you need an extra hand, please reach out.

RobbieTheWagner commented 6 years ago

@Aladdin-ADD how is this going? Need any help?

aladdin-add commented 6 years ago

@rwwagner90 so sorry for the delay -- I have been so busy these days.

it's almost finished, but for the docs~

RobbieTheWagner commented 6 years ago

@Aladdin-ADD no problem at all! I just wanted to see if you needed help with anything.

RobbieTheWagner commented 6 years ago

@Aladdin-ADD would you like help with docs or anything?

aladdin-add commented 6 years ago

@rwwagner90 sure, could you help to improve the docs? my English is so poor.. πŸ˜‚

helgeh commented 6 years ago

I thought this was being worked on... Anything new?