Skip to content

Commit e67eaf2

Browse files
committed
Add target search query content parsing
1 parent 38d8870 commit e67eaf2

3 files changed

Lines changed: 134 additions & 90 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export function escapeRegExp(input: string) {
2+
return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
3+
}
4+
5+
/**
6+
* Returns a regular expression that can be used to match escape sequences in a string.
7+
*
8+
* It matches any backslash followed by any character that is either a backslash
9+
* or one of the specified value end delimiters.
10+
*
11+
* All other backslashes are not matched because users may not know that they need
12+
* to be escaped in the first place.
13+
*/
14+
export function getEscapeSequenceRegExp(...valueEndDelimiters: string[]): RegExp {
15+
return new RegExp(`\\\\(${['\\', ...valueEndDelimiters].map(escapeRegExp).join('|')})`, 'g')
16+
}

packages/annotation-comments/src/internal/tag-parser.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AnnotationTag } from '../core/types'
2+
import { getEscapeSequenceRegExp } from './escaping'
23

34
type ParseAnnotationTagsOptions = {
45
codeLines: string[]
@@ -72,17 +73,34 @@ const annotationTagRegex = new RegExp(
7273
'g'
7374
)
7475

76+
const unquotedValueEscapeSequence = getEscapeSequenceRegExp(':', ']')
77+
const quotedValueEscapeSequences = new Map<string, RegExp>([
78+
['"', getEscapeSequenceRegExp('"')],
79+
["'", getEscapeSequenceRegExp("'")],
80+
['/', getEscapeSequenceRegExp('/')],
81+
])
82+
83+
function parseTargetSearchQuery(rawTargetSearchQuery: string | undefined): string | undefined {
84+
if (rawTargetSearchQuery === undefined || rawTargetSearchQuery === '') return
85+
const quotedValueEscapeSequence = quotedValueEscapeSequences.get(rawTargetSearchQuery[0])
86+
const escapeSequenceRegExp = quotedValueEscapeSequence || unquotedValueEscapeSequence
87+
const unescapedQuery = rawTargetSearchQuery.replace(escapeSequenceRegExp, '$1')
88+
const queryWithoutQuotes = quotedValueEscapeSequence === undefined ? unescapedQuery : unescapedQuery.slice(1, -1)
89+
return queryWithoutQuotes
90+
}
91+
7592
export function parseAnnotationTags(options: ParseAnnotationTagsOptions): AnnotationTag[] {
7693
const { codeLines } = options
7794
const annotationTags: AnnotationTag[] = []
7895

7996
codeLines.forEach((line, lineIndex) => {
8097
const matches = [...line.matchAll(annotationTagRegex)]
8198
matches.forEach((match) => {
82-
const [, name, targetSearchQuery, relativeTargetRange] = match
99+
const [, name, rawTargetSearchQuery, relativeTargetRange] = match
83100
const rawTag = match[0]
84101
const startColIndex = match.index
85102
const endColIndex = startColIndex + rawTag.length
103+
const targetSearchQuery = parseTargetSearchQuery(rawTargetSearchQuery)
86104
annotationTags.push({
87105
name,
88106
targetSearchQuery,

packages/annotation-comments/test/tag-parser.test.ts

Lines changed: 99 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ console.log('Some code');
1616
).toEqual([])
1717
})
1818

19-
describe('Name only', () => {
19+
describe('Parses tag names', () => {
2020
test(`[!tag]`, ({ task }) => {
2121
performTagTest({
2222
rawTag: task.name,
@@ -25,9 +25,17 @@ console.log('Some code');
2525
relativeTargetRange: undefined,
2626
})
2727
})
28+
test(`[!code tag] (for Shiki transformer compatibility)`, ({ task }) => {
29+
performTagTest({
30+
rawTag: task.name.replace(/ \(.*?\)/, ''),
31+
name: 'tag',
32+
targetSearchQuery: undefined,
33+
relativeTargetRange: undefined,
34+
})
35+
})
2836
})
2937

30-
describe('With relative target range', () => {
38+
describe('Parses relative target ranges', () => {
3139
test(`[!positive-range:3]`, ({ task }) => {
3240
performTagTest({
3341
rawTag: task.name,
@@ -47,118 +55,120 @@ console.log('Some code');
4755
})
4856
})
4957

50-
describe('With unquoted target search query', () => {
51-
test(`[!tag:search-term]`, ({ task }) => {
52-
performTagTest({
53-
rawTag: task.name,
54-
name: 'tag',
55-
targetSearchQuery: 'search-term',
56-
relativeTargetRange: undefined,
58+
describe('Parses target search queries', () => {
59+
describe('Tags with unquoted target search query', () => {
60+
test(`[!tag:search-term]`, ({ task }) => {
61+
performTagTest({
62+
rawTag: task.name,
63+
name: 'tag',
64+
targetSearchQuery: 'search-term',
65+
relativeTargetRange: undefined,
66+
})
5767
})
58-
})
5968

60-
test(`[!tag:term with spaces and chars like .,;?!"'/-]`, ({ task }) => {
61-
performTagTest({
62-
rawTag: task.name,
63-
name: 'tag',
64-
targetSearchQuery: `term with spaces and chars like .,;?!"'/-`,
65-
relativeTargetRange: undefined,
69+
test(`[!tag:term with spaces and chars like .,;?!"'/-]`, ({ task }) => {
70+
performTagTest({
71+
rawTag: task.name,
72+
name: 'tag',
73+
targetSearchQuery: `term with spaces and chars like .,;?!"'/-`,
74+
relativeTargetRange: undefined,
75+
})
6676
})
6777
})
68-
})
6978

70-
describe('With unquoted target search query and target range', () => {
71-
test(`[!tag:search-term:5]`, ({ task }) => {
72-
performTagTest({
73-
rawTag: task.name,
74-
name: 'tag',
75-
targetSearchQuery: 'search-term',
76-
relativeTargetRange: 5,
79+
describe('Tags with unquoted target search query and target range', () => {
80+
test(`[!tag:search-term:5]`, ({ task }) => {
81+
performTagTest({
82+
rawTag: task.name,
83+
name: 'tag',
84+
targetSearchQuery: 'search-term',
85+
relativeTargetRange: 5,
86+
})
7787
})
78-
})
7988

80-
test(`[!tag:term with spaces and chars like .;/"'?!-, too:-2]`, ({ task }) => {
81-
performTagTest({
82-
rawTag: task.name,
83-
name: 'tag',
84-
targetSearchQuery: `term with spaces and chars like .;/"'?!-, too`,
85-
relativeTargetRange: -2,
89+
test(`[!tag:term with spaces and chars like .;/"'?!-, too:-2]`, ({ task }) => {
90+
performTagTest({
91+
rawTag: task.name,
92+
name: 'tag',
93+
targetSearchQuery: `term with spaces and chars like .;/"'?!-, too`,
94+
relativeTargetRange: -2,
95+
})
8696
})
8797
})
88-
})
8998

90-
describe('With quoted target search query', () => {
91-
test(`[!tag:"double-quoted term"]`, ({ task }) => {
92-
performTagTest({
93-
rawTag: task.name,
94-
name: 'tag',
95-
targetSearchQuery: `"double-quoted term"`,
96-
relativeTargetRange: undefined,
99+
describe('Tags with quoted target search query', () => {
100+
test(`[!tag:"double-quoted term"]`, ({ task }) => {
101+
performTagTest({
102+
rawTag: task.name,
103+
name: 'tag',
104+
targetSearchQuery: `double-quoted term`,
105+
relativeTargetRange: undefined,
106+
})
97107
})
98-
})
99108

100-
test(`[!tag:'single-quoted term']`, ({ task }) => {
101-
performTagTest({
102-
rawTag: task.name,
103-
name: 'tag',
104-
targetSearchQuery: `'single-quoted term'`,
105-
relativeTargetRange: undefined,
109+
test(`[!tag:'single-quoted term']`, ({ task }) => {
110+
performTagTest({
111+
rawTag: task.name,
112+
name: 'tag',
113+
targetSearchQuery: `single-quoted term`,
114+
relativeTargetRange: undefined,
115+
})
106116
})
107-
})
108117

109-
test(`[!tag:"double-quoted term with 'single quotes' inside"]`, ({ task }) => {
110-
performTagTest({
111-
rawTag: task.name,
112-
name: 'tag',
113-
targetSearchQuery: `"double-quoted term with 'single quotes' inside"`,
114-
relativeTargetRange: undefined,
118+
test(`[!tag:"double-quoted term with 'single quotes' inside"]`, ({ task }) => {
119+
performTagTest({
120+
rawTag: task.name,
121+
name: 'tag',
122+
targetSearchQuery: `double-quoted term with 'single quotes' inside`,
123+
relativeTargetRange: undefined,
124+
})
115125
})
116-
})
117126

118-
test(`[!tag:'single-quoted term with "double quotes" inside']`, ({ task }) => {
119-
performTagTest({
120-
rawTag: task.name,
121-
name: 'tag',
122-
targetSearchQuery: `'single-quoted term with "double quotes" inside'`,
123-
relativeTargetRange: undefined,
127+
test(`[!tag:'single-quoted term with "double quotes" inside']`, ({ task }) => {
128+
performTagTest({
129+
rawTag: task.name,
130+
name: 'tag',
131+
targetSearchQuery: `single-quoted term with "double quotes" inside`,
132+
relativeTargetRange: undefined,
133+
})
124134
})
125-
})
126135

127-
test(`[!tag:"escaped \\"same style quotes\\" inside"]`, ({ task }) => {
128-
performTagTest({
129-
rawTag: task.name,
130-
name: 'tag',
131-
targetSearchQuery: `"escaped \\"same style quotes\\" inside"`,
132-
relativeTargetRange: undefined,
136+
test(`[!tag:"escaped \\"same style quotes\\" inside"]`, ({ task }) => {
137+
performTagTest({
138+
rawTag: task.name,
139+
name: 'tag',
140+
targetSearchQuery: `escaped "same style quotes" inside`,
141+
relativeTargetRange: undefined,
142+
})
133143
})
134-
})
135144

136-
test(`[!tag:"wild / ' mix / of:3] chars"]`, ({ task }) => {
137-
performTagTest({
138-
rawTag: task.name,
139-
name: 'tag',
140-
targetSearchQuery: `"wild / ' mix / of:3] chars"`,
141-
relativeTargetRange: undefined,
145+
test(`[!tag:"wild / ' mix / of:3] chars"]`, ({ task }) => {
146+
performTagTest({
147+
rawTag: task.name,
148+
name: 'tag',
149+
targetSearchQuery: `wild / ' mix / of:3] chars`,
150+
relativeTargetRange: undefined,
151+
})
142152
})
143153
})
144-
})
145154

146-
describe('With quoted target search query and target range', () => {
147-
test(`[!tag:"double-quoted term":10]`, ({ task }) => {
148-
performTagTest({
149-
rawTag: task.name,
150-
name: 'tag',
151-
targetSearchQuery: `"double-quoted term"`,
152-
relativeTargetRange: 10,
155+
describe('Tags with quoted target search query and target range', () => {
156+
test(`[!tag:"double-quoted term":10]`, ({ task }) => {
157+
performTagTest({
158+
rawTag: task.name,
159+
name: 'tag',
160+
targetSearchQuery: `double-quoted term`,
161+
relativeTargetRange: 10,
162+
})
153163
})
154-
})
155164

156-
test(`[!tag:'single-quoted term':-5]`, ({ task }) => {
157-
performTagTest({
158-
rawTag: task.name,
159-
name: 'tag',
160-
targetSearchQuery: `'single-quoted term'`,
161-
relativeTargetRange: -5,
165+
test(`[!tag:'single-quoted term':-5]`, ({ task }) => {
166+
performTagTest({
167+
rawTag: task.name,
168+
name: 'tag',
169+
targetSearchQuery: `single-quoted term`,
170+
relativeTargetRange: -5,
171+
})
162172
})
163173
})
164174
})

0 commit comments

Comments
 (0)