Repository: VincentLanglet/Twig-CS-Fixer (php)

Issue:
Option to use tabs
### Rule(s) related to or rule(s) missing

- Whitespace\IndentRule
- Whitespace\IndentStyle ← missing

### Expected behavior

Should convert spaces to tabs by indention rule.

### Actual behavior

No rule to use tabs instead of spaces.


Pull request description:
Allow to use tab for indentation
Closes #232

Patch:
diff --git a/src/Rules/Whitespace/IndentRule.php b/src/Rules/Whitespace/IndentRule.php
index 162d698..70a2c47 100644
--- a/src/Rules/Whitespace/IndentRule.php
+++ b/src/Rules/Whitespace/IndentRule.php
@@ -9,22 +9,37 @@ use TwigCsFixer\Rules\ConfigurableRuleInterface;
 use TwigCsFixer\Token\Token;
 
 /**
- * Ensures that files are not indented with tabs.
+ * Ensures that files are indented with spaces (or tabs).
  */
 final class IndentRule extends AbstractFixableRule implements ConfigurableRuleInterface
 {
-    public function __construct(private int $spaceRatio = 4)
-    {
+    public function __construct(
+        private int $spaceRatio = 4,
+        private bool $useTab = false,
+    ) {
     }
 
     public function getConfiguration(): array
     {
         return [
             'spaceRatio' => $this->spaceRatio,
+            'useTab' => $this->useTab,
         ];
     }
 
     protected function process(int $tokenPosition, array $tokens): void
+    {
+        if ($this->useTab) {
+            $this->spaceToTab($tokenPosition, $tokens);
+        } else {
+            $this->tabToSpace($tokenPosition, $tokens);
+        }
+    }
+
+    /**
+     * @param array<int, Token> $tokens
+     */
+    private function tabToSpace(int $tokenPosition, array $tokens): void
     {
         $token = $tokens[$tokenPosition];
         if (!$this->isTokenMatching($token, Token::TAB_TOKENS)) {
@@ -41,4 +56,33 @@ final class IndentRule extends AbstractFixableRule implements ConfigurableRuleIn
             str_replace("\t", str_repeat(' ', $this->spaceRatio), $token->getValue())
         );
     }
+
+    /**
+     * @param array<int, Token> $tokens
+     */
+    private function spaceToTab(int $tokenPosition, array $tokens): void
+    {
+        $token = $tokens[$tokenPosition];
+        if (1 !== $token->getPosition()) {
+            return;
+        }
+
+        if (!$this->isTokenMatching($token, Token::WHITESPACE_TOKENS)) {
+            return;
+        }
+
+        if (\strlen($token->getValue()) < $this->spaceRatio) {
+            return;
+        }
+
+        $fixer = $this->addFixableError('A file must not be indented with spaces.', $token);
+        if (null === $fixer) {
+            return;
+        }
+
+        $fixer->replaceToken(
+            $tokenPosition,
+            str_replace(str_repeat(' ', $this->spaceRatio), "\t", $token->getValue())
+        );
+    }
 }


Test patch:
diff --git a/tests/Rules/Whitespace/Indent/IndentRuleTest.php b/tests/Rules/Whitespace/Indent/IndentRuleTest.php
index ac6fdf3..1488667 100644
--- a/tests/Rules/Whitespace/Indent/IndentRuleTest.php
+++ b/tests/Rules/Whitespace/Indent/IndentRuleTest.php
@@ -11,8 +11,15 @@ final class IndentRuleTest extends AbstractRuleTestCase
 {
     public function testConfiguration(): void
     {
-        static::assertSame(['spaceRatio' => 4], (new IndentRule())->getConfiguration());
-        static::assertSame(['spaceRatio' => 2], (new IndentRule(2))->getConfiguration());
+        static::assertSame([
+            'spaceRatio' => 4,
+            'useTab' => false,
+        ], (new IndentRule())->getConfiguration());
+
+        static::assertSame([
+            'spaceRatio' => 2,
+            'useTab' => true,
+        ], (new IndentRule(2, true))->getConfiguration());
     }
 
     public function testRule(): void
@@ -35,4 +42,17 @@ final class IndentRuleTest extends AbstractRuleTestCase
             __DIR__.'/IndentRuleTest.fixed2.twig',
         );
     }
+
+    public function testRuleWithTab(): void
+    {
+        $this->checkRule(
+            new IndentRule(useTab: true),
+            [
+                'Indent.Error:12:1' => 'A file must not be indented with spaces.',
+                'Indent.Error:14:1' => 'A file must not be indented with spaces.',
+            ],
+            __DIR__.'/IndentRuleTest.tab.twig',
+            __DIR__.'/IndentRuleTest.tab.fixed.twig',
+        );
+    }
 }
diff --git a/tests/Rules/Whitespace/Indent/IndentRuleTest.tab.fixed.twig b/tests/Rules/Whitespace/Indent/IndentRuleTest.tab.fixed.twig
new file mode 100644
index 0000000..dc9a1ca
--- /dev/null
+++ b/tests/Rules/Whitespace/Indent/IndentRuleTest.tab.fixed.twig
@@ -0,0 +1,19 @@
+{#
+	Tab in a comment
+#}
+	{{ tab }}
+
+{#
+	  Tab and space in a comment
+#}
+	  {{ tab and space }}
+
+{#
+	Space in a comment
+#}
+	{{ space }}
+
+{#
+  Space in a comment
+#}
+  {{ space }}
diff --git a/tests/Rules/Whitespace/Indent/IndentRuleTest.tab.twig b/tests/Rules/Whitespace/Indent/IndentRuleTest.tab.twig
new file mode 100644
index 0000000..46b571e
--- /dev/null
+++ b/tests/Rules/Whitespace/Indent/IndentRuleTest.tab.twig
@@ -0,0 +1,19 @@
+{#
+	Tab in a comment
+#}
+	{{ tab }}
+
+{#
+	  Tab and space in a comment
+#}
+	  {{ tab and space }}
+
+{#
+    Space in a comment
+#}
+    {{ space }}
+
+{#
+  Space in a comment
+#}
+  {{ space }}