diff --git a/packages/mini-markdown-ast-parser/src/core/parse/compose/blocks/list.ts b/packages/mini-markdown-ast-parser/src/core/parse/compose/blocks/list.ts index 5fc305f70eab7c01aa52dac683e50e43645cc35c..320906435ffbc662f707988305f0889aa1df3fdf 100644 --- a/packages/mini-markdown-ast-parser/src/core/parse/compose/blocks/list.ts +++ b/packages/mini-markdown-ast-parser/src/core/parse/compose/blocks/list.ts @@ -1,146 +1,129 @@ -import type { Tokens } from '@/types/tokens' -import type { ParseFnParams } from '..' -import { parseInlineElements } from '../inline' -import { TokenTypeVal } from '@/types/tokens-types' +import type { Tokens } from "@/types/tokens"; +import type { ParseFnParams } from ".."; +import { parseInlineElements } from "../inline"; +import { TokenTypeVal } from "@/types/tokens-types"; // 转换 list -export const parseList = ({ - line, - lines, - index, - currentStatus, - root -}: ParseFnParams) => { +export const parseList = ({ line, lines, index, currentStatus, root }: ParseFnParams) => { // 列表项处理 - const trimmedLine = line.trimStart() - const indent = line.length - trimmedLine.length - const match = trimmedLine.match(/^(-|\d+\.)\s+(.*)/) + const trimmedLine = line.trimStart(); + const indent = line.length - trimmedLine.length; + const match = trimmedLine.match(/^(-|\d+\.)\s+(.*)/); if (match) { - const [_, marker, content] = match - const ordered = marker.includes('.') - const startOffset = calculateOffset(index + 1, indent + 1, lines) - const contentStartOffset = calculateOffset( - index + 1, - indent + marker.length + 1, - lines - ) - const contentEndOffset = contentStartOffset + content.length + const [_, marker, content] = match; + const ordered = marker.includes("."); // 是否有序列表 + const startOffset = calculateOffset(index + 1, indent + 1, lines); // 列表项开始偏移量 + const contentStartOffset = calculateOffset(index + 1, indent + marker.length + 1, lines); // 列表项内容开始偏移量 + const contentEndOffset = contentStartOffset + content.length; // 列表项内容结束偏移量 // 解析行内元素 - const children = parseInlineElements(content, index, contentStartOffset) + const children = parseInlineElements(content, index, contentStartOffset); const listItem = { - type: 'listItem', + type: "listItem", spread: false, checked: null, children: [ { - type: 'paragraph', + type: "paragraph", children: children, position: { start: { line: index + 1, column: indent + marker.length + 1, - offset: contentStartOffset + offset: contentStartOffset, }, end: { line: index + 1, column: indent + marker.length + 1 + content.length, - offset: contentEndOffset - } - } - } + offset: contentEndOffset, + }, + }, + }, ], position: { start: { line: index + 1, column: indent + 1, - offset: startOffset + offset: startOffset, }, end: { line: index + 1, column: indent + marker.length + 1 + content.length, - offset: contentEndOffset - } - } - } + offset: contentEndOffset, + }, + }, + }; if (indent > currentStatus.currentIndent) { if (currentStatus.currentListItem) { - if ( - !currentStatus.currentListItem.children?.some( - (child) => child.type === 'list' - ) - ) { + if (!currentStatus.currentListItem.children?.some((child) => child.type === "list")) { const newList = { - type: 'list', + type: "list", ordered, start: null, spread: false, children: [], - position: listItem.position - } - currentStatus.currentListItem.children?.push(newList as Tokens) - currentStatus.listStack.push(currentStatus.currentList as Tokens) - currentStatus.currentList = newList as Tokens + position: listItem.position, + }; + currentStatus.currentListItem.children?.push(newList as Tokens); + currentStatus.listStack.push(currentStatus.currentList as Tokens); + currentStatus.currentList = newList as Tokens; } - currentStatus.currentList?.children?.push(listItem as Tokens) + currentStatus.currentList?.children?.push(listItem as Tokens); } } else if (indent < currentStatus.currentIndent) { while ( currentStatus.listStack.length > 0 && - currentStatus.listStack[currentStatus.listStack.length - 1].position - .start.column >= indent + currentStatus.listStack[currentStatus.listStack.length - 1].position.start.column >= indent ) { - currentStatus.currentList = currentStatus.listStack.pop() as Tokens + currentStatus.currentList = currentStatus.listStack.pop() as Tokens; } if (currentStatus.currentList) { - currentStatus.currentList.children?.push(listItem as Tokens) + currentStatus.currentList.children?.push(listItem as Tokens); } else { root.children.push({ - type: 'list' as TokenTypeVal, + type: "list" as TokenTypeVal, ordered, start: null, spread: false, children: [listItem], - position: listItem.position - } as Tokens) - currentStatus.currentList = root.children[root.children.length - 1] + position: listItem.position, + } as Tokens); + currentStatus.currentList = root.children[root.children.length - 1]; } } else { // 如果当前列表类型与新列表项类型不同,则创建新的列表节点 - if ( - currentStatus.currentList && - currentStatus.currentList.ordered !== ordered - ) { - currentStatus.currentList = null + if (currentStatus.currentList && currentStatus.currentList.ordered !== ordered) { + currentStatus.currentList = null; + currentStatus.listStack = []; } if (!currentStatus.currentList) { root.children.push({ - type: 'list', + type: "list", ordered, start: null, spread: false, children: [listItem as Tokens], - position: listItem.position - } as Tokens) - currentStatus.currentList = root.children[root.children.length - 1] + position: listItem.position, + } as Tokens); + currentStatus.currentList = root.children[root.children.length - 1]; } else { - currentStatus.currentList.children?.push(listItem as Tokens) + currentStatus.currentList.children?.push(listItem as Tokens); } } - currentStatus.currentListItem = listItem as Tokens - currentStatus.currentIndent = indent - return true + currentStatus.currentListItem = listItem as Tokens; + currentStatus.currentIndent = indent; + return true; } -} +}; // 辅助函数:计算偏移量 const calculateOffset = (index: number, column: number, lines: string[]) => { - let offset = 0 + let offset = 0; for (let i = 0; i < index - 1; i++) { - offset += lines[i].length + 1 // +1 是因为换行符 + offset += lines[i].length + 1; // +1 是因为换行符 } - return offset + column - 1 -} + return offset + column - 1; +}; diff --git a/packages/mini-markdown-ast-parser/src/core/parse/tokenizer.ts b/packages/mini-markdown-ast-parser/src/core/parse/tokenizer.ts index c4e03eb464ad0c5b6310f9ab4f7b0bd3e8385512..73b217f259ef35a98b5858cf34e7c7084b64a58b 100644 --- a/packages/mini-markdown-ast-parser/src/core/parse/tokenizer.ts +++ b/packages/mini-markdown-ast-parser/src/core/parse/tokenizer.ts @@ -1,13 +1,13 @@ -import type { RootTokens } from '../../types/tokens' -import { parseMap, defaultParse } from './compose' +import type { RootTokens } from "../../types/tokens"; +import { parseMap, defaultParse } from "./compose"; // 词法分析器 export const tokenizer = (lines: string[], root: RootTokens) => { if (!Array.isArray(lines)) { - return new Error('The parameter is an array') + return new Error("The lines parameter is not an array"); } - let currentOffset = 0 + let currentOffset = 0; let currentStatus = { // heading 的层级 depth: 0, @@ -15,8 +15,8 @@ export const tokenizer = (lines: string[], root: RootTokens) => { currentBlockquote: null, // code inCodeBlock: false, - codeBlockLang: '', - codeBlockValue: '', + codeBlockLang: "", + codeBlockValue: "", codeBlockStartOffset: 0, codeBlockStartLine: 0, // list @@ -26,31 +26,32 @@ export const tokenizer = (lines: string[], root: RootTokens) => { listStack: [], // 用于存储列表栈 // table currentTable: null, - } + }; const resetCurrentStatus = () => { currentStatus = { depth: 0, currentBlockquote: null, inCodeBlock: false, - codeBlockLang: '', - codeBlockValue: '', + codeBlockLang: "", + codeBlockValue: "", codeBlockStartOffset: 0, codeBlockStartLine: 0, currentList: null, currentListItem: null, currentIndent: 0, listStack: [], - currentTable: null - } - } + currentTable: null, + }; + }; // 遍历每一行 lines.forEach((line, index) => { // 去除首尾空格 - const trimmedLine = line.trim() + const trimmedLine = line.trim(); // 是否继续转换 - let isParse = false + let isParse = false; + // TODO: 动态调整解析器顺序以优化性能? for (const [key, parseFn] of Object.entries(parseMap)) { const result = parseFn({ trimmedLine, @@ -60,14 +61,49 @@ export const tokenizer = (lines: string[], root: RootTokens) => { index, root, currentStatus, - resetCurrentStatus - }) + resetCurrentStatus, + }); + if (result) { - isParse = true - break + // 获取最后添加的节点类型 + const lastNode = root.children[root.children.length - 1]; + if (lastNode) { + // 根据不同类型的节点重置状态 + if (lastNode.type !== "list" && lastNode.type !== "listItem") { + currentStatus.currentList = null; + currentStatus.listStack = []; + currentStatus.currentListItem = null; + currentStatus.currentIndent = 0; + } + + if (lastNode.type !== "blockquote") { + currentStatus.currentBlockquote = null; + } + + if (lastNode.type !== "heading") { + currentStatus.depth = 0; + } + + if (lastNode.type !== "table") { + currentStatus.currentTable = null; + } + + if (lastNode.type !== "code") { + currentStatus.inCodeBlock = false; + currentStatus.codeBlockLang = ""; + currentStatus.codeBlockValue = ""; + currentStatus.codeBlockStartOffset = 0; + currentStatus.codeBlockStartLine = 0; + } + } + + isParse = true; + break; } } if (!isParse) { + // 如果没有特殊解析,则清空状态 + resetCurrentStatus(); defaultParse({ trimmedLine, line, @@ -76,9 +112,9 @@ export const tokenizer = (lines: string[], root: RootTokens) => { index, root, currentStatus, - resetCurrentStatus - }) + resetCurrentStatus, + }); } - currentOffset += line.length + 1 - }) -} + currentOffset += line.length + 1; + }); +};