Skip to content

Commit

Permalink
feat: add new rule noGremlins
Browse files Browse the repository at this point in the history
  • Loading branch information
sabhas committed Dec 26, 2022
1 parent 6577280 commit 7d6fc8e
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 1 deletion.
15 changes: 15 additions & 0 deletions sasjslint-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@
"default": "/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding} <h4> SAS Macros </h4>{lineEnding}**/",
"examples": []
},
"noGremlins": {
"$id": "#/properties/noGremlins",
"type": "array",
"title": "noGremlins",
"description": "",
"default": [true],
"examples": [true, false]
},
"hasMacroNameInMend": {
"$id": "#/properties/hasMacroNameInMend",
"type": "boolean",
Expand Down Expand Up @@ -193,6 +201,13 @@
"enum": ["error", "warn"],
"default": "warn"
},
"noGremlins": {
"$id": "#/properties/severityLevel/noGremlins",
"title": "noGremlins",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"hasMacroNameInMend": {
"$id": "#/properties/severityLevel/hasMacroNameInMend",
"title": "hasMacroNameInMend",
Expand Down
1 change: 1 addition & 0 deletions src/rules/line/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { noGremlins } from './noGremlins'
export { indentationMultiple } from './indentationMultiple'
export { maxLineLength } from './maxLineLength'
export { noEncodedPasswords } from './noEncodedPasswords'
Expand Down
128 changes: 128 additions & 0 deletions src/rules/line/noGremlins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Diagnostic, LintConfig } from '../../types'
import { LineLintRule } from '../../types/LintRule'
import { LintRuleType } from '../../types/LintRuleType'
import { Severity } from '../../types/Severity'

const name = 'noGremlins'
const description = 'Disallow characters specified in grimlins array'
const message = 'Line contains a grimlin character'

const test = (value: string, lineNumber: number, config?: LintConfig) => {
const severity = config?.severityLevel[name] || Severity.Warning

const diagnostics: Diagnostic[] = []

const gremlins: any = {}

for (const [hexCode, config] of Object.entries(gremlinCharacters)) {
gremlins[charFromHex(hexCode)] = Object.assign({}, config, {
hexCode
})
}

const regexpWithAllChars = new RegExp(
Object.keys(gremlins)
.map((char) => `${char}+`)
.join('|'),
'g'
)

let match
while ((match = regexpWithAllChars.exec(value))) {
const matchedCharacter = match[0][0]
const gremlin = gremlins[matchedCharacter]

diagnostics.push({
message: `${message}: ${gremlin.description}, hexCode(${gremlin.hexCode})`,
lineNumber,
startColumnNumber: match.index + 1,
endColumnNumber: match.index + 1 + match[0].length,
severity
})
}

return diagnostics
}

/**
* Lint rule that checks if a given line of text contains any grimlin.
*/
export const noGremlins: LineLintRule = {
type: LintRuleType.Line,
name,
description,
message,
test
}

const charFromHex = (hexCode: string) => String.fromCodePoint(parseInt(hexCode))

const gremlinCharacters = {
'0x2013': {
description: 'en dash'
},
'0x2018': {
description: 'left single quotation mark'
},
'0x2019': {
description: 'right single quotation mark'
},
'0x2029': {
zeroWidth: true,
description: 'paragraph separator'
},
'0x2066': {
zeroWidth: true,
description: 'Left to right'
},
'0x2069': {
zeroWidth: true,
description: 'Pop directional'
},
'0x0003': {
description: 'end of text'
},
'0x000b': {
description: 'line tabulation'
},
'0x00a0': {
description: 'non breaking space'
},
'0x00ad': {
description: 'soft hyphen'
},
'0x200b': {
zeroWidth: true,
description: 'zero width space'
},
'0x200c': {
zeroWidth: true,
description: 'zero width non-joiner'
},
'0x200e': {
zeroWidth: true,
description: 'left-to-right mark'
},
'0x201c': {
description: 'left double quotation mark'
},
'0x201d': {
description: 'right double quotation mark'
},
'0x202c': {
zeroWidth: true,
description: 'pop directional formatting'
},
'0x202d': {
zeroWidth: true,
description: 'left-to-right override'
},
'0x202e': {
zeroWidth: true,
description: 'right-to-left override'
},
'0xfffc': {
zeroWidth: true,
description: 'object replacement character'
}
}
7 changes: 6 additions & 1 deletion src/types/LintConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
maxLineLength,
noEncodedPasswords,
noTabs,
noTrailingSpaces
noTrailingSpaces,
noGremlins
} from '../rules/line'
import { lowerCaseFileNames, noSpacesInFileNames } from '../rules/path'
import { LineEndings } from './LineEndings'
Expand Down Expand Up @@ -119,6 +120,10 @@ export class LintConfig {
this.fileLintRules.push(strictMacroDefinition)
}

if (json?.noGremlins) {
this.lineLintRules.push(noGremlins)
}

if (json?.severityLevel) {
for (const [rule, severity] of Object.entries(json.severityLevel)) {
if (severity === 'warn') this.severityLevel[rule] = Severity.Warning
Expand Down

0 comments on commit 7d6fc8e

Please sign in to comment.