Skip to content

Commit

Permalink
feat: rule tab
Browse files Browse the repository at this point in the history
  • Loading branch information
lzear committed Nov 4, 2023
1 parent 8e88c2a commit b6897d7
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 0 deletions.
51 changes: 51 additions & 0 deletions docs/rules/tab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
title: tab
description: Use tabs to separate words in the code
---

<script setup lang="ts">
import CodeEditor from '../../.vitepress/theme/components/code-editor.vue';
import {ruleName, presetConfigs, initialText} from '../../src/sample-code/tab.js';
</script>

> "I just think it's weird that you use spaces instead of tabs." — Richard Hendricks
# Require word separators to be tabs, not spaces (`dont/tab`)

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

<!-- end auto-generated rule header -->

## 📖 Rule details

Tabs are better than spaces. However and unfortunately, most developers still use spaces to separate words.

## 💡 Examples

```js
// ❌ Incorrect
if (a) {
b = c;
function foo(d) {
e = f;
}
}

// ✅ Correct
if (a) {
b = c;
function foo(d) {
e = f;
}
}
```

## 🔧 Config

```js
{ rules: { 'dont/tab': 2 } }
```

## 🧑‍💻 Demo

<CodeEditor :rule="ruleName" :text="initialText" :presetConfigs="presetConfigs" />
61 changes: 61 additions & 0 deletions rules/tab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { RuleFixer } from '../utils/eslint-types/Rule.js'

import { createEslintRule } from '../utils/create-eslint-rule.js'

type MESSAGE_ID = 'replace'

type Options = []

export const RULE_NAME = 'tab'

export default createEslintRule<Options, MESSAGE_ID>({
name: RULE_NAME,
meta: {
type: 'problem',
docs: { description: 'require word separators to be tabs, not spaces' },
fixable: 'whitespace',
schema: [{ type: 'object' }],
messages: {
replace: 'Replace space by tab',
},
},
defaultOptions: [],
create: context => {
const { sourceCode } = context
return {
Program: () => {
const { lines } = sourceCode
let maxLength = 0
for (const line of lines)
maxLength = Math.max(maxLength, line.trim().length)

const { text, tokensAndComments } = sourceCode
for (const [leftIndex, leftToken] of tokensAndComments.entries()) {
if (leftIndex === tokensAndComments.length - 1) continue
const rightToken = tokensAndComments[leftIndex + 1]

const stringBetween = text.slice(
leftToken.range[1],
rightToken.range[0],
)

if (!/^\s*$/.test(stringBetween)) continue
if (!/ /.test(stringBetween)) continue

const replacedString = stringBetween.replaceAll(' ', '\t')

context.report({
node: rightToken,
loc: { start: leftToken.loc.end, end: rightToken.loc.start },
messageId: 'replace',
fix: (fixer: RuleFixer) =>
fixer.replaceTextRange(
[leftToken.range[1], rightToken.range[0]],
replacedString,
),
})
}
},
}
},
})
7 changes: 7 additions & 0 deletions src/sample-code/tab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { PresetConfig } from './presets.js'

export const ruleName = 'tab'

export const presetConfigs = [] satisfies PresetConfig[]

export { webstormJsSample as initialText } from './webstorm-js-sample.js'

0 comments on commit b6897d7

Please sign in to comment.