Skip to content

Commit 4662adb

Browse files
committed
Add multi-line and negative test cases
1 parent 0f7a337 commit 4662adb

4 files changed

Lines changed: 126 additions & 12 deletions

File tree

packages/annotation-comments/src/parsers/parent-comment.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,11 @@ export function parseParentComment(options: ParseParentCommentOptions): Annotati
9898
const lineContent = getNonWhitespaceContentInLine({ codeLines, lineIndex, startColumn: possibleContentStart })
9999
if (lineContent) {
100100
// Stop if the line has `---` as its only text content
101-
if (lineContent.content === '---') break
101+
if (lineContent.content === '---') {
102+
// Make the line part of the comment, but don't add its content
103+
comment.commentRange.end = { line: lineIndex }
104+
break
105+
}
102106
// Stop if the line starts with an annotation tag opening sequence `[!`
103107
if (lineContent.content.startsWith('[!')) break
104108
// Otherwise, add the content and expand the comment range

packages/annotation-comments/test/annotation-tags.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { parseAnnotationTags } from '../src/parsers/annotation-tags'
44
import { createGlobalRegExp } from '../src/internal/regexps'
55
import { splitCodeLines } from './utils'
66

7-
describe('parseAnnotationTags', () => {
7+
describe('parseAnnotationTags()', () => {
88
test('Returns an empty array when no annotation tags are found', () => {
99
expect(
1010
getTags(`

packages/annotation-comments/test/parent-comment.test.ts

Lines changed: 109 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
import { describe, expect, test } from 'vitest'
2-
import type { AnnotationTag, AnnotationComment } from '../src/core/types'
2+
import type { AnnotationComment } from '../src/core/types'
33
import { parseAnnotationTags } from '../src/parsers/annotation-tags'
44
import { parseParentComment } from '../src/parsers/parent-comment'
55
import { splitCodeLines } from './utils'
66

7-
describe('parseParentComment', () => {
8-
test('Returns undefined when no parent comment is found', () => {
9-
expect(
10-
getParentComment(`
11-
// This comment is not on the same line as the tag
12-
console.log('Some code containing [!ins] outside of a comment');
13-
`)
14-
).toEqual(undefined)
7+
describe('parseParentComment()', () => {
8+
describe('Returns undefined when no valid parent comment is found', () => {
9+
describe('Single-line comment syntax', () => {
10+
test('No comment syntax in the same line', () => {
11+
expect(getParentComment(`console.log('This is [!ins] in a string')`)).toEqual(undefined)
12+
})
13+
test('Comment syntax located after the annotation tag', () => {
14+
expect(getParentComment(`console.log('More [!test] text') // Hi!`)).toEqual(undefined)
15+
})
16+
test('Missing whitespace before comment opening syntax', () => {
17+
expect(getParentComment(`someCode()// [!note] Invalid syntax`)).toEqual(undefined)
18+
})
19+
test('Missing whitespace before annotation tag', () => {
20+
expect(getParentComment(`someCode() //[!note] Invalid syntax`)).toEqual(undefined)
21+
})
22+
test('Content between comment opening and annotation tag', () => {
23+
expect(getParentComment(`someCode() // Hi [!note] This won't work`)).toEqual(undefined)
24+
})
25+
})
1526
})
1627

17-
describe('Single-line comments', () => {
28+
describe('Supports single-line comments', () => {
1829
describe('Starting at the beginning of the line', () => {
1930
const syntaxes = [
2031
// Starting at column 0, followed by a space
@@ -146,6 +157,94 @@ console.log('Some code containing [!ins] outside of a comment');
146157
expect(comment.contentRanges).toEqual([{ start: { line: 2, column: commentLine.indexOf('Mismatching') }, end: { line: 2 } }])
147158
})
148159
})
160+
describe(`Allows multi-line content by repeating the same opening syntax`, () => {
161+
test('Single annotation comment with multi-line content', () => {
162+
const lines = [
163+
// Starts like a regular single-line annotation comment...
164+
'// [!note] Annotation content',
165+
// ...but continues on the next lines by repeating the comment opening syntax
166+
'// that spans multiple lines',
167+
'// until the comment ends',
168+
// ...and ends when the comment syntax is not repeated
169+
'someCode()',
170+
]
171+
const comment = getParentComment(getTestCode(lines.join('\n'))) as AnnotationComment
172+
expect(comment.contents).toEqual(['Annotation content', 'that spans multiple lines', 'until the comment ends'])
173+
expect(comment.commentRange).toEqual({ start: { line: 2 }, end: { line: 4 } })
174+
expect(comment.contentRanges).toEqual([
175+
{ start: { line: 2, column: lines[0].indexOf('Annotation') }, end: { line: 2 } },
176+
{ start: { line: 3, column: lines[1].indexOf('that') }, end: { line: 3 } },
177+
{ start: { line: 4, column: lines[2].indexOf('until') }, end: { line: 4 } },
178+
])
179+
})
180+
test('Multi-line content ends when encountering a different comment syntax', () => {
181+
const lines = [
182+
// Starts like a regular single-line annotation comment...
183+
'// [!note] Annotation content',
184+
// ...but continues on the next line by repeating the comment opening syntax
185+
'// that spans multiple lines',
186+
// ...and ends when encountering a different comment syntax
187+
'# This is a regular comment and not part of the annotation',
188+
]
189+
const comment = getParentComment(getTestCode(lines.join('\n'))) as AnnotationComment
190+
expect(comment.contents).toEqual(['Annotation content', 'that spans multiple lines'])
191+
expect(comment.commentRange).toEqual({ start: { line: 2 }, end: { line: 3 } })
192+
expect(comment.contentRanges).toEqual([
193+
{ start: { line: 2, column: lines[0].indexOf('Annotation') }, end: { line: 2 } },
194+
{ start: { line: 3, column: lines[1].indexOf('that') }, end: { line: 3 } },
195+
])
196+
})
197+
test('Multi-line content ends when encountering another annotation comment', () => {
198+
const lines = [
199+
// Starts like a regular single-line annotation comment...
200+
'// [!note] Annotation 1 content',
201+
// ...but continues on the next line by repeating the comment opening syntax
202+
'// that spans multiple lines',
203+
// ...and ends when encountering another annotation comment
204+
'// [!test] Annotation 2 content',
205+
]
206+
const comment = getParentComment(getTestCode(lines.join('\n'))) as AnnotationComment
207+
expect(comment.contents).toEqual(['Annotation 1 content', 'that spans multiple lines'])
208+
expect(comment.commentRange).toEqual({ start: { line: 2 }, end: { line: 3 } })
209+
expect(comment.contentRanges).toEqual([
210+
{ start: { line: 2, column: lines[0].indexOf('Annotation') }, end: { line: 2 } },
211+
{ start: { line: 3, column: lines[1].indexOf('that') }, end: { line: 3 } },
212+
])
213+
})
214+
test('Multi-line content ends when encountering "---" on its own line', () => {
215+
const lines = [
216+
// Starts like a regular single-line annotation comment...
217+
'// [!note] Annotation content',
218+
// ...but continues on the next line by repeating the comment opening syntax
219+
'// that spans multiple lines',
220+
// ...and ends when encountering "---" on its own line
221+
'// ---',
222+
'// This is a regular comment and not part of the annotation',
223+
]
224+
const comment = getParentComment(getTestCode(lines.join('\n'))) as AnnotationComment
225+
expect(comment.contents).toEqual(['Annotation content', 'that spans multiple lines'])
226+
// Expect the "---" line to be included in the comment range
227+
// so it will be removed when the comment is removed
228+
expect(comment.commentRange).toEqual({ start: { line: 2 }, end: { line: 4 } })
229+
// However, it must not be included in the content ranges
230+
expect(comment.contentRanges).toEqual([
231+
{ start: { line: 2, column: lines[0].indexOf('Annotation') }, end: { line: 2 } },
232+
{ start: { line: 3, column: lines[1].indexOf('that') }, end: { line: 3 } },
233+
])
234+
})
235+
test('Comments starting after code on the same line cannot be multi-line', () => {
236+
const lines = [
237+
// A regular single-line comment that starts after some code
238+
'someCode() // [!note] Annotation content',
239+
// ...cannot be continued on the next line
240+
'// This is a regular comment and not part of the annotation',
241+
]
242+
const comment = getParentComment(getTestCode(lines.join('\n'))) as AnnotationComment
243+
expect(comment.contents).toEqual(['Annotation content'])
244+
expect(comment.commentRange).toEqual({ start: { line: 2, column: lines[0].indexOf(' // [!') }, end: { line: 2 } })
245+
expect(comment.contentRanges).toEqual([{ start: { line: 2, column: lines[0].indexOf('Annotation') }, end: { line: 2 } }])
246+
})
247+
})
149248
})
150249

151250
function getTestCode(commentLine: string) {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { describe, expect, test } from 'vitest'
2+
3+
describe('parseAnnotationComments()', () => {
4+
// TODO: We need to test if the parser properly skips processing the second annotation tag
5+
// in the following code, which requires checking the comment range of the previously processed
6+
// annotation comment and skipping all tags located before the end of the comment range:
7+
// "someCode() // [!note] Mismatching comment # [!syntax] also prevents chaining"
8+
test('Todo', () => {
9+
expect(true).toBe(true)
10+
})
11+
})

0 commit comments

Comments
 (0)