1/** 2 * @fileoverview Disallow mixed spaces and tabs for indentation 3 * @author Jary Niebur 4 */ 5"use strict"; 6 7//------------------------------------------------------------------------------ 8// Rule Definition 9//------------------------------------------------------------------------------ 10 11module.exports = { 12 meta: { 13 type: "layout", 14 15 docs: { 16 description: "disallow mixed spaces and tabs for indentation", 17 category: "Stylistic Issues", 18 recommended: true, 19 url: "https://eslint.org/docs/rules/no-mixed-spaces-and-tabs" 20 }, 21 22 schema: [ 23 { 24 enum: ["smart-tabs", true, false] 25 } 26 ], 27 28 messages: { 29 mixedSpacesAndTabs: "Mixed spaces and tabs." 30 } 31 }, 32 33 create(context) { 34 const sourceCode = context.getSourceCode(); 35 36 let smartTabs; 37 38 switch (context.options[0]) { 39 case true: // Support old syntax, maybe add deprecation warning here 40 case "smart-tabs": 41 smartTabs = true; 42 break; 43 default: 44 smartTabs = false; 45 } 46 47 //-------------------------------------------------------------------------- 48 // Public 49 //-------------------------------------------------------------------------- 50 51 return { 52 53 "Program:exit"(node) { 54 const lines = sourceCode.lines, 55 comments = sourceCode.getAllComments(), 56 ignoredCommentLines = new Set(); 57 58 // Add all lines except the first ones. 59 comments.forEach(comment => { 60 for (let i = comment.loc.start.line + 1; i <= comment.loc.end.line; i++) { 61 ignoredCommentLines.add(i); 62 } 63 }); 64 65 /* 66 * At least one space followed by a tab 67 * or the reverse before non-tab/-space 68 * characters begin. 69 */ 70 let regex = /^(?=( +|\t+))\1(?:\t| )/u; 71 72 if (smartTabs) { 73 74 /* 75 * At least one space followed by a tab 76 * before non-tab/-space characters begin. 77 */ 78 regex = /^(?=(\t*))\1(?=( +))\2\t/u; 79 } 80 81 lines.forEach((line, i) => { 82 const match = regex.exec(line); 83 84 if (match) { 85 const lineNumber = i + 1; 86 const loc = { 87 start: { 88 line: lineNumber, 89 column: match[0].length - 2 90 }, 91 end: { 92 line: lineNumber, 93 column: match[0].length 94 } 95 }; 96 97 if (!ignoredCommentLines.has(lineNumber)) { 98 const containingNode = sourceCode.getNodeByRangeIndex(sourceCode.getIndexFromLoc(loc.start)); 99 100 if (!(containingNode && ["Literal", "TemplateElement"].includes(containingNode.type))) { 101 context.report({ 102 node, 103 loc, 104 messageId: "mixedSpacesAndTabs" 105 }); 106 } 107 } 108 } 109 }); 110 } 111 }; 112 } 113}; 114