-
Notifications
You must be signed in to change notification settings - Fork 66.9k
Expand file tree
/
Copy pathmultiple-emphasis-patterns.js
More file actions
93 lines (77 loc) · 2.97 KB
/
multiple-emphasis-patterns.js
File metadata and controls
93 lines (77 loc) · 2.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import { addError } from 'markdownlint-rule-helpers'
import { getRange } from '../helpers/utils.js'
import frontmatter from '#src/frame/lib/read-frontmatter.js'
export const multipleEmphasisPatterns = {
names: ['GHD050', 'multiple-emphasis-patterns'],
description: 'Do not use more than one emphasis/strong, italics, or uppercase for a string',
tags: ['formatting', 'emphasis', 'style'],
severity: 'warning',
function: (params, onError) => {
// Skip autogenerated files
const frontmatterString = params.frontMatterLines.join('\n')
const fm = frontmatter(frontmatterString).data
if (fm && fm.autogenerated) return
const lines = params.lines
let inCodeBlock = false
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
const lineNumber = i + 1
// Track code block state
if (line.trim().startsWith('```')) {
inCodeBlock = !inCodeBlock
continue
}
// Skip code blocks and indented code
if (inCodeBlock || line.trim().startsWith(' ')) continue
// Check for multiple emphasis patterns
checkMultipleEmphasis(line, lineNumber, onError)
}
},
}
/**
* Check for multiple emphasis types in a single text segment
*/
function checkMultipleEmphasis(line, lineNumber, onError) {
// Focus on the clearest violations of the style guide
const multipleEmphasisPatterns = [
// Bold + italic combinations (***text***)
{ regex: /\*\*\*([^*]+)\*\*\*/g, types: ['bold', 'italic'] },
{ regex: /___([^_]+)___/g, types: ['bold', 'italic'] },
// Bold with code nested inside
{ regex: /\*\*([^*]*`[^`]+`[^*]*)\*\*/g, types: ['bold', 'code'] },
{ regex: /__([^_]*`[^`]+`[^_]*)__/g, types: ['bold', 'code'] },
// Code with bold nested inside
{ regex: /`([^`]*\*\*[^*]+\*\*[^`]*)`/g, types: ['code', 'bold'] },
{ regex: /`([^`]*__[^_]+__[^`]*)`/g, types: ['code', 'bold'] },
]
for (const pattern of multipleEmphasisPatterns) {
let match
while ((match = pattern.regex.exec(line)) !== null) {
// Skip if this is likely intentional or very short
if (shouldSkipMatch(match[0], match[1])) continue
const range = getRange(line, match[0])
addError(
onError,
lineNumber,
`Do not use multiple emphasis types in a single string: ${pattern.types.join(' + ')}`,
line,
range,
null, // No auto-fix as this requires editorial judgment
)
}
}
}
/**
* Determine if a match should be skipped (likely intentional formatting)
*/
function shouldSkipMatch(fullMatch, content) {
// Skip common false positives
if (!content) return true
// Skip very short content (likely intentional single chars)
if (content.trim().length < 2) return true
// Skip if it's mostly code-like content (constants, variables)
if (/^[A-Z_][A-Z0-9_]*$/.test(content.trim())) return true
// Skip file extensions or URLs
if (/\.[a-z]{2,4}$/i.test(content.trim()) || /https?:\/\//.test(content)) return true
return false
}