blob: 627749e70da472c93500e8016152a3ba498a8959 [file] [log] [blame]
Simon Zündaebbf4f2024-06-27 08:42:161import { Line } from '../primitives.js';
Jack Franklinfd72c072022-12-21 11:45:012
3const reTag = /^@\S+/;
4
5/**
6 * Groups source lines in sections representing tags.
7 * First section is a block description if present. Last section captures lines starting with
8 * the last tag to the end of the block, including dangling closing marker.
9 * @param {Line[]} block souce lines making a single comment block
10 */
11export type Parser = (block: Line[]) => Line[][];
12
13/**
14 * Predicate telling if string contains opening/closing escaping sequence
15 * @param {string} source raw source line
16 */
17export type Fencer = (source: string) => boolean;
18
19/**
20 * `Parser` configuration options
21 */
22export interface Options {
23 // escaping sequence or predicate
24 fence: string | Fencer;
25}
26
27/**
28 * Creates configured `Parser`
29 * @param {Partial<Options>} options
30 */
31export default function getParser({
32 fence = '```',
33}: Partial<Options> = {}): Parser {
34 const fencer = getFencer(fence);
35 const toggleFence = (source: string, isFenced: boolean): boolean =>
36 fencer(source) ? !isFenced : isFenced;
37
38 return function parseBlock(source: Line[]): Line[][] {
39 // start with description section
40 const sections: Line[][] = [[]];
41
42 let isFenced = false;
43 for (const line of source) {
44 if (reTag.test(line.tokens.description) && !isFenced) {
45 sections.push([line]);
46 } else {
47 sections[sections.length - 1].push(line);
48 }
49 isFenced = toggleFence(line.tokens.description, isFenced);
50 }
51
52 return sections;
53 };
54}
55
56function getFencer(fence: string | Fencer): Fencer {
57 if (typeof fence === 'string')
58 return (source: string) => source.split(fence).length % 2 === 0;
59 return fence;
60}