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

Add support for multiline indenting to ObjectOperatorIndentSniff #2224

Merged
merged 3 commits into from
Sep 22, 2019

Conversation

marcospassos
Copy link
Contributor

@marcospassos marcospassos commented Nov 12, 2018

This PR adds supports for multilevel indenting to the amazing sniff ObjectOperatorIndentSniff created by @gsherwood.

Multilevel indenting contributes to improve code readability in long chained method calls. This convention is widely used by Symfony in configuration definitions:

$rootNode
    ->children()
        ->booleanNode('auto_connect')
            ->defaultTrue()
        ->end()
        ->scalarNode('default_connection')
            ->defaultValue('default')
        ->end()
    ->end()
;

The option multilevel allows enabling/disabled the new behavior, which is disabled by default to be backward compatible with previous versions.

The sniff can automatically fix violations by adjusting the number of spaces based on the value of property indent and the previous indentation:

// Before fix
$rootNode
    ->children()
		        ->booleanNode('auto_connect')
        ->end();

// After fix
$rootNode
    ->children()
        ->booleanNode('auto_connect')
    ->end();

Tests are included to confirm the correctness.

$foundIndent,
];

$fix = $phpcsFile->addFixableError($error, $next, 'Incorrect', $data);
if ($fix === true) {
$spaces = str_repeat(' ', $requiredIndent);
$spaces = str_repeat(' ', $expectedIndent);
if ($foundIndent === 0) {
$phpcsFile->fixer->addContentBefore($next, $spaces);
} else {
$phpcsFile->fixer->replaceToken(($next - 1), $spaces);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Just wondering: should $previousIndent be (re)set in this leaf of the condition as well ? Maybe to $expectedIndent ?

Copy link
Contributor Author

@marcospassos marcospassos Nov 13, 2018

Choose a reason for hiding this comment

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

This is one of the possible heuristics.

Notice that it affects error reporting only:

// Keeping the last valid
$builder
    ->start()
            ->do() // Excpected 8, found 12
                ->end(); // Expected 8, found 16

// Keeping the last adjusted
$builder
    ->start()
            ->do() // Excpected 8, found 12
                ->end(); // Expected 12, found 16

Let's go with your suggestion as it may lead to more correct results most of the time, assuming that if there are extraneous spaces, it is more likely that there should be indentation than nothing.

@marcospassos
Copy link
Contributor Author

@jrfnl I've adjusted the logic based on your suggestion.

Copy link
Contributor

@jrfnl jrfnl left a comment

Choose a reason for hiding this comment

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

I haven't tested the PR, but based on the code itself, this looks to be a good change.

@marcospassos
Copy link
Contributor Author

@jrfnl any chances of merging it anytime soon, once the PR includes tests?

@jrfnl
Copy link
Contributor

jrfnl commented Nov 18, 2018

@marcospassos That's not up to me. Depends on when @gsherwood has time to have a look at this as well.

once the PR includes tests?

What do you mean by this ? AFAICS, the PR does include some unit tests.
If you like, I could have a look to see if I can think of cases not yet covered by current tests ?

Other than that, you may want to reset the custom property to the default after the tests covering the property, to prevent confusion if more unit tests are added later, but that's minor.

@marcospassos
Copy link
Contributor Author

marcospassos commented Nov 18, 2018

Sorry, I didn't realize you don't have such privilege.

Other than that, you may want to reset the custom property to the default after the tests covering the property, to prevent confusion if more unit tests are added later, but that's minor.

Nice catch.

@marcospassos
Copy link
Contributor Author

Done!

@marcospassos
Copy link
Contributor Author

@gsherwood, did you have time to take a look into this?

@gsherwood
Copy link
Member

gsherwood commented Dec 12, 2018

If I had to document this (which I will need to), would I say that the multilevel option allows for a line in a chained call to either be indented 1 level more or less than the previous line, with the only exception being that lines must be indented at least 1 level from the first line?

So this valid:

<?php
$rootNode
    ->one()
        ->two()
            ->three()
        ->four()
    ->five();

This is not:

<?php
$rootNode
    ->one()
        ->two()
            ->three()
    ->four()
    ->five();

This is not:

<?php
$rootNode
    ->one()
    ->two()
        ->three()
    ->four()
->five();

But is this valid, or does the chain need to close properly?

<?php
$rootNode
    ->one()
        ->two()
            ->three()
        ->four()
        ->five();

@marcospassos
Copy link
Contributor Author

marcospassos commented Dec 15, 2018

@gsherwood thanks for reviewing it.

If I had to document this (which I will need to), would I say that the multilevel option allows for a line in a chained call to either be indented 1 level more or less than the previous line, with the only exception being that lines must be indented at least 1 level from the first line?

Exactly, this precisely describes the current implementation.

I believe the current implementation covers the most common use cases. The widespread use case is balanced method calls, such as open() and close(), start() and end(), etc. Another common use case is nested builders that requires calling parent() to return to the parent builder, which is also covered in the current implementation. The last case you mentioned is valid to support cases where the chained calls are not necessarily balanced as shown in the following snippet:

$writer
    ->writeln("First line")
    ->indent()
        ->writeln("Indent level 1")
        ->indent()
            ->writeln("Indent level 2")

As a side note, I've been using it for two months now in different projects with no issues so far.

@marcospassos
Copy link
Contributor Author

@gsherwood are there any issues still pending in this PR?

@gsherwood
Copy link
Member

are there any issues still pending in this PR?

No, sorry. Just haven't had time to look at this yet.

@gsherwood gsherwood added this to the 3.5.0 milestone Jan 24, 2019
@gsherwood gsherwood merged commit 09c6f75 into squizlabs:master Sep 22, 2019
gsherwood added a commit that referenced this pull request Sep 22, 2019
gsherwood added a commit that referenced this pull request Sep 22, 2019
@gsherwood
Copy link
Member

Sorry this took so long to merge, but it is in now and will be released in 3.5.0. Thanks a lot for this PR.

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.

3 participants