{"version":3,"file":"ParagraphSplitter.js","sourceRoot":"","sources":["../../src/parser/ParagraphSplitter.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\r\n// See LICENSE in the project root for license information.\r\n\r\nimport { DocSection, type DocNode, DocNodeKind, DocParagraph, type DocPlainText } from '../nodes';\r\n\r\n/**\r\n * The ParagraphSplitter is a secondary stage that runs after the NodeParser has constructed\r\n * the DocComment. It splits DocParagraph nodes into multiple paragraphs by looking for\r\n * paragraph delimiters. Following CommonMark conventions, paragraphs are delimited by\r\n * one or more blank lines. (These lines end with SoftBreak nodes.) The blank lines are\r\n * not discarded. Instead, they are attached to the preceding paragraph. If the DocParagraph\r\n * starts with blank lines, they are preserved to avoid creating a paragraph containing only\r\n * whitespace.\r\n */\r\nexport class ParagraphSplitter {\r\n private static readonly _whitespaceRegExp: RegExp = /^\\s*$/;\r\n\r\n /**\r\n * Split all paragraphs belonging to the provided subtree.\r\n */\r\n public static splitParagraphs(node: DocNode): void {\r\n if (node instanceof DocSection) {\r\n ParagraphSplitter.splitParagraphsForSection(node);\r\n\r\n // (We don't recurse here, since sections cannot contain subsections)\r\n } else {\r\n for (const childNode of node.getChildNodes()) {\r\n ParagraphSplitter.splitParagraphs(childNode);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Split all paragraphs belonging to the provided DocSection.\r\n */\r\n public static splitParagraphsForSection(docSection: DocSection): void {\r\n const inputNodes: ReadonlyArray = docSection.nodes;\r\n const outputNodes: DocNode[] = [];\r\n\r\n for (const oldNode of inputNodes) {\r\n if (oldNode.kind === DocNodeKind.Paragraph) {\r\n ParagraphSplitter._splitParagraph(oldNode as DocParagraph, outputNodes);\r\n } else {\r\n outputNodes.push(oldNode);\r\n }\r\n }\r\n\r\n // Replace the inputNodes with the outputNodes\r\n docSection.clearNodes();\r\n docSection.appendNodes(outputNodes);\r\n }\r\n\r\n private static _splitParagraph(oldParagraph: DocParagraph, outputNodes: DocNode[]): void {\r\n const inputParagraphNodes: ReadonlyArray = oldParagraph.nodes;\r\n\r\n let currentParagraph: DocParagraph = new DocParagraph({ configuration: oldParagraph.configuration });\r\n outputNodes.push(currentParagraph);\r\n\r\n enum SplitterState {\r\n Start,\r\n AwaitingTrailer,\r\n ReadingTrailer\r\n }\r\n let state: SplitterState = SplitterState.Start;\r\n\r\n let currentIndex: number = 0;\r\n while (currentIndex < inputParagraphNodes.length) {\r\n // Scan forwards to the end of the line\r\n let isBlankLine: boolean = true;\r\n let lineEndIndex: number = currentIndex; // non-inclusive\r\n do {\r\n const node: DocNode = inputParagraphNodes[lineEndIndex++];\r\n if (node.kind === DocNodeKind.SoftBreak) {\r\n break;\r\n }\r\n if (isBlankLine) {\r\n if (!this._isWhitespace(node)) {\r\n isBlankLine = false;\r\n }\r\n }\r\n } while (lineEndIndex < inputParagraphNodes.length);\r\n\r\n // At this point, the line and SoftBreak will be in inputParagraphNodes.slice(currentIndex, lineEndIndex)\r\n\r\n switch (state) {\r\n case SplitterState.Start:\r\n // We're skipping any blank lines that start the first paragraph\r\n if (!isBlankLine) {\r\n state = SplitterState.AwaitingTrailer;\r\n }\r\n break;\r\n case SplitterState.AwaitingTrailer:\r\n // We already saw some content, so now we're looking for a blank line that starts the trailer\r\n // at the end of this paragraph\r\n if (isBlankLine) {\r\n state = SplitterState.ReadingTrailer;\r\n }\r\n break;\r\n case SplitterState.ReadingTrailer:\r\n // We already found the trailer, so now we're looking for a non-blank line that will\r\n // begin a new paragraph\r\n if (!isBlankLine) {\r\n // Start a new paragraph\r\n currentParagraph = new DocParagraph({ configuration: oldParagraph.configuration });\r\n outputNodes.push(currentParagraph);\r\n\r\n state = SplitterState.AwaitingTrailer;\r\n }\r\n break;\r\n }\r\n\r\n // Append the line onto the current paragraph\r\n for (let i: number = currentIndex; i < lineEndIndex; ++i) {\r\n currentParagraph.appendNode(inputParagraphNodes[i]);\r\n }\r\n\r\n currentIndex = lineEndIndex;\r\n }\r\n }\r\n\r\n private static _isWhitespace(node: DocNode): boolean {\r\n switch (node.kind) {\r\n case DocNodeKind.PlainText:\r\n const docPlainText: DocPlainText = node as DocPlainText;\r\n return ParagraphSplitter._whitespaceRegExp.test(docPlainText.text);\r\n default:\r\n return false;\r\n }\r\n }\r\n}\r\n"]}