friendsoftwig / twigcs

The missing checkstyle for twig!
MIT License
343 stars 34 forks source link

Support for ??? "Empty Coalesce" operator #256

Open acalvino4 opened 2 years ago

acalvino4 commented 2 years ago

My issue is exactly what Kyle says here in the context of prettier's twig formatter: https://github.com/trivago/prettier-plugin-twig-melody/issues/47#issue-611254163

Is a custom syntax many people use in Craft, and while it makes sense to throw an error since it's not official twig syntax, I am wondering whether it is possible to, in preference order: a) include support anyway b) personally create a custom rule that makes this ok c) disable twigcs for these lines

I understand (a) probably doesn't make sense until twig officially supports it. But I thought I'd put it out there since it probably wouldn't hurt anything either. (b) would be great too, but I'm not sure if it's possible. I would rather not do (c) though it's better than nothing, and I'm not sure if it is supported either.

As a side note, if (c) is not possible, I would think it an important feature request!

acalvino4 commented 1 year ago

I got a chance to look into this more.

Looks like all we'd need for option (a) is to modify RulesetBuilder.php:

        $ops = $this->using(self::OP_VARS, [
            ...
            ['$➀\?\?\?➁$', $this->binaryOpSpace('???')], // Add this, must appear before following line
            ['$➀\?\?➁$', $this->binaryOpSpace('??')],
            ...
        ];

For people who don't use that syntax, it wouldn't hurt them, though I understand reluctance to include unofficial syntax in an official ruleset. But I'm curious if I could get a maintainer's thoughts. If you're not opposed, I'll submit a PR.

In the meantime, I was able to extend the RegEngineRule rule using a custom ruleset builder to get what I need, though it's more convoluted than I would have hoped, and am wondering if we might consider making an easier way to add new operators to the list.

class CustomRulesetBuilder extends RulesetBuilder {
    public function build(): array
    {
        $newOps = $this->using(self::OP_VARS, [['$➀\?\?\?➁$', $this->binaryOpSpace('???')]]);

        $build = parent::build();
        $expr = $build['expr'];
        $build['expr'] = array_merge(
            $newOps,
            $expr,
        );
        return $build;
    }
}

class CustomRuleset implements RulesetInterface
{
    private int $twigMajorVersion;

    public function __construct(int $twigMajorVersion)
    {
        $this->twigMajorVersion = $twigMajorVersion;
    }

    public function getRules()
    {
        $configurator = new RulesetConfigurator();
        $configurator->setTwigMajorVersion($this->twigMajorVersion);
        $builder = new CustomRulesetBuilder($configurator);

        return [
            new Rule\RegEngineRule(Violation::SEVERITY_ERROR, $builder->build()),
            new Rule\TrailingSpace(Violation::SEVERITY_ERROR),
            new Rule\UnusedMacro(Violation::SEVERITY_WARNING, $this->resolver),
            new Rule\UnusedVariable(Violation::SEVERITY_WARNING, $this->resolver),
        ];
    }
}