@@ -14,7 +14,7 @@ import { findOffsets } from "../util.js";
14
14
//-----------------------------------------------------------------------------
15
15
16
16
/**
17
- * @import { Node, Paragraph } from "mdast";
17
+ * @import { Node, Heading, Paragraph, TableCell } from "mdast";
18
18
* @import { MarkdownRuleDefinition } from "../types.js";
19
19
* @typedef {"reversedSyntax" } NoReversedMediaSyntaxMessageIds
20
20
* @typedef {[] } NoReversedMediaSyntaxOptions
@@ -25,39 +25,40 @@ import { findOffsets } from "../util.js";
25
25
// Helpers
26
26
//-----------------------------------------------------------------------------
27
27
28
- /** Matches reversed link/image syntax like (text)[url], ignoring escaped characters like \(text\)[url]. */
28
+ /** Matches reversed link/image syntax like ` (text)[url]` , ignoring escaped characters like ` \(text\)[url]` . */
29
29
const reversedPattern =
30
30
/ (?< ! \\ ) \( ( (?: \\ .| [ ^ ( ) \\ ] | \( [ \s \S ] * \) ) * ) \) \[ ( (?: \\ .| [ ^ \] \\ \n ] ) * ) \] (? ! \( ) / gu;
31
31
32
32
/**
33
- * Checks if a match is within any of the code spans
33
+ * Checks if a match is within any skip range
34
34
* @param {number } matchIndex The index of the match
35
- * @param {Array<{startOffset: number, endOffset: number}> } codeSpans Array of code span positions
36
- * @returns {boolean } True if the match is within a code span
35
+ * @param {Array<{startOffset: number, endOffset: number}> } skipRanges The skip ranges
36
+ * @returns {boolean } True if the match is within a skip range
37
37
*/
38
- function isInCodeSpan ( matchIndex , codeSpans ) {
39
- return codeSpans . some (
40
- span => matchIndex >= span . startOffset && matchIndex < span . endOffset ,
38
+ function isInSkipRange ( matchIndex , skipRanges ) {
39
+ return skipRanges . some (
40
+ range =>
41
+ range . startOffset <= matchIndex && matchIndex < range . endOffset ,
41
42
) ;
42
43
}
43
44
44
45
/**
45
- * Finds all code spans in the paragraph node by traversing its children
46
- * @param {Paragraph } node The paragraph node to search
47
- * @returns {Array<{startOffset: number, endOffset: number}> } Array of code span positions
46
+ * Finds ranges of inline code and HTML nodes within a given node
47
+ * @param {Heading | Paragraph | TableCell } node The node to search
48
+ * @returns {Array<{startOffset: number, endOffset: number}> } Array of objects containing start and end offsets
48
49
*/
49
- function findCodeSpans ( node ) {
50
+ function findSkipRanges ( node ) {
50
51
/** @type {Array<{startOffset: number, endOffset: number}> } */
51
- const codeSpans = [ ] ;
52
+ const skipRanges = [ ] ;
52
53
53
54
/**
54
- * Recursively traverses the AST to find inline code nodes
55
+ * Recursively traverses the AST to find inline code and HTML nodes
55
56
* @param {Node } currentNode The current node being traversed
56
57
* @returns {void }
57
58
*/
58
59
function traverse ( currentNode ) {
59
- if ( currentNode . type === "inlineCode" ) {
60
- codeSpans . push ( {
60
+ if ( currentNode . type === "inlineCode" || currentNode . type === "html" ) {
61
+ skipRanges . push ( {
61
62
startOffset : currentNode . position . start . offset ,
62
63
endOffset : currentNode . position . end . offset ,
63
64
} ) ;
@@ -70,7 +71,7 @@ function findCodeSpans(node) {
70
71
}
71
72
72
73
traverse ( node ) ;
73
- return codeSpans ;
74
+ return skipRanges ;
74
75
}
75
76
76
77
//-----------------------------------------------------------------------------
@@ -97,73 +98,89 @@ export default {
97
98
} ,
98
99
99
100
create ( context ) {
100
- return {
101
- paragraph ( node ) {
102
- const text = context . sourceCode . getText ( node ) ;
103
- const codeSpans = findCodeSpans ( node ) ;
104
- let match ;
105
-
106
- while ( ( match = reversedPattern . exec ( text ) ) !== null ) {
107
- const [ reversedSyntax , label , url ] = match ;
108
- const matchIndex = match . index ;
109
- const matchLength = reversedSyntax . length ;
110
-
111
- if (
112
- isInCodeSpan (
113
- matchIndex + node . position . start . offset ,
114
- codeSpans ,
115
- )
116
- ) {
117
- continue ;
118
- }
119
-
120
- const {
121
- lineOffset : startLineOffset ,
122
- columnOffset : startColumnOffset ,
123
- } = findOffsets ( text , matchIndex ) ;
124
- const {
125
- lineOffset : endLineOffset ,
126
- columnOffset : endColumnOffset ,
127
- } = findOffsets ( text , matchIndex + matchLength ) ;
128
-
129
- const baseColumn = 1 ;
130
- const nodeStartLine = node . position . start . line ;
131
- const nodeStartColumn = node . position . start . column ;
132
- const startLine = nodeStartLine + startLineOffset ;
133
- const endLine = nodeStartLine + endLineOffset ;
134
- const startColumn =
135
- ( startLine === nodeStartLine
136
- ? nodeStartColumn
137
- : baseColumn ) + startColumnOffset ;
138
- const endColumn =
139
- ( endLine === nodeStartLine
140
- ? nodeStartColumn
141
- : baseColumn ) + endColumnOffset ;
142
-
143
- context . report ( {
144
- loc : {
145
- start : {
146
- line : startLine ,
147
- column : startColumn ,
148
- } ,
149
- end : {
150
- line : endLine ,
151
- column : endColumn ,
152
- } ,
101
+ /**
102
+ * Finds reversed link/image syntax in a node.
103
+ * @param {Heading | Paragraph | TableCell } node The node to check.
104
+ * @returns {void } Reports any reversed syntax found.
105
+ */
106
+ function findReversedMediaSyntax ( node ) {
107
+ const text = context . sourceCode . getText ( node ) ;
108
+ const skipRanges = findSkipRanges ( node ) ;
109
+ let match ;
110
+
111
+ while ( ( match = reversedPattern . exec ( text ) ) !== null ) {
112
+ const [ reversedSyntax , label , url ] = match ;
113
+ const matchIndex = match . index ;
114
+ const matchLength = reversedSyntax . length ;
115
+
116
+ if (
117
+ isInSkipRange (
118
+ matchIndex + node . position . start . offset ,
119
+ skipRanges ,
120
+ )
121
+ ) {
122
+ continue ;
123
+ }
124
+
125
+ const {
126
+ lineOffset : startLineOffset ,
127
+ columnOffset : startColumnOffset ,
128
+ } = findOffsets ( text , matchIndex ) ;
129
+ const {
130
+ lineOffset : endLineOffset ,
131
+ columnOffset : endColumnOffset ,
132
+ } = findOffsets ( text , matchIndex + matchLength ) ;
133
+
134
+ const baseColumn = 1 ;
135
+ const nodeStartLine = node . position . start . line ;
136
+ const nodeStartColumn = node . position . start . column ;
137
+ const startLine = nodeStartLine + startLineOffset ;
138
+ const endLine = nodeStartLine + endLineOffset ;
139
+ const startColumn =
140
+ ( startLine === nodeStartLine
141
+ ? nodeStartColumn
142
+ : baseColumn ) + startColumnOffset ;
143
+ const endColumn =
144
+ ( endLine === nodeStartLine ? nodeStartColumn : baseColumn ) +
145
+ endColumnOffset ;
146
+
147
+ context . report ( {
148
+ loc : {
149
+ start : {
150
+ line : startLine ,
151
+ column : startColumn ,
153
152
} ,
154
- messageId : "reversedSyntax" ,
155
- fix ( fixer ) {
156
- const startOffset =
157
- node . position . start . offset + matchIndex ;
158
- const endOffset = startOffset + matchLength ;
159
-
160
- return fixer . replaceTextRange (
161
- [ startOffset , endOffset ] ,
162
- `[${ label } ](${ url } )` ,
163
- ) ;
153
+ end : {
154
+ line : endLine ,
155
+ column : endColumn ,
164
156
} ,
165
- } ) ;
166
- }
157
+ } ,
158
+ messageId : "reversedSyntax" ,
159
+ fix ( fixer ) {
160
+ const startOffset =
161
+ node . position . start . offset + matchIndex ;
162
+ const endOffset = startOffset + matchLength ;
163
+
164
+ return fixer . replaceTextRange (
165
+ [ startOffset , endOffset ] ,
166
+ `[${ label } ](${ url } )` ,
167
+ ) ;
168
+ } ,
169
+ } ) ;
170
+ }
171
+ }
172
+
173
+ return {
174
+ heading ( node ) {
175
+ findReversedMediaSyntax ( node ) ;
176
+ } ,
177
+
178
+ paragraph ( node ) {
179
+ findReversedMediaSyntax ( node ) ;
180
+ } ,
181
+
182
+ tableCell ( node ) {
183
+ findReversedMediaSyntax ( node ) ;
167
184
} ,
168
185
} ;
169
186
} ,
0 commit comments