import { invoke } from '@tauri-apps/api/core'; import { ParseMarkdownRequest, ParseMarkdownResponse, QueryNodesRequest, QueryNodesResponse, FindNodeAtPositionRequest, FindNodeAtPositionResponse, ExtractOutlineRequest, ExtractOutlineResponse, ExtractLinksRequest, ExtractLinksResponse, ValidateMarkdownRequest, ValidateMarkdownResponse, MarkdownParseResult, MarkdownNode, OutlineItem, LinkInfo, ValidationResult, MarkdownParserConfig, QueryType, } from '../types/markdown'; /** * Markdown解析服务类 * 封装与Tauri后端的通信,提供易用的API接口 */ export class MarkdownService { private static instance: MarkdownService; private constructor() {} /** * 获取单例实例 */ public static getInstance(): MarkdownService { if (!MarkdownService.instance) { MarkdownService.instance = new MarkdownService(); } return MarkdownService.instance; } /** * 解析Markdown文档 * @param text Markdown文本 * @param config 解析器配置 * @returns 解析结果 */ public async parseMarkdown( text: string, config?: MarkdownParserConfig ): Promise { const request: ParseMarkdownRequest = { text, config, }; const response = await invoke('parse_markdown', { request, }); if (!response.success || !response.result) { throw new Error(response.error || 'Failed to parse markdown'); } return response.result; } /** * 查询特定类型的节点 * @param text Markdown文本 * @param queryType 查询类型 * @returns 匹配的节点列表 */ public async queryNodes( text: string, queryType: QueryType | string ): Promise { const request: QueryNodesRequest = { text, query_name: queryType, }; const response = await invoke('query_markdown_nodes', { request, }); if (!response.success || !response.nodes) { throw new Error(response.error || 'Failed to query nodes'); } return response.nodes; } /** * 根据位置查找节点 * @param text Markdown文本 * @param line 行号(从0开始) * @param column 列号(从0开始) * @returns 找到的节点,如果没有找到则返回null */ public async findNodeAtPosition( text: string, line: number, column: number ): Promise { const request: FindNodeAtPositionRequest = { text, line, column, }; const response = await invoke( 'find_markdown_node_at_position', { request } ); if (!response.success) { throw new Error(response.error || 'Failed to find node at position'); } return response.node || null; } /** * 提取文档大纲 * @param text Markdown文本 * @returns 大纲项目列表 */ public async extractOutline(text: string): Promise { const request: ExtractOutlineRequest = { text, }; const response = await invoke( 'extract_markdown_outline', { request } ); if (!response.success || !response.outline) { throw new Error(response.error || 'Failed to extract outline'); } return response.outline; } /** * 提取所有链接 * @param text Markdown文本 * @returns 链接信息列表 */ public async extractLinks(text: string): Promise { const request: ExtractLinksRequest = { text, }; const response = await invoke( 'extract_markdown_links', { request } ); if (!response.success || !response.links) { throw new Error(response.error || 'Failed to extract links'); } return response.links; } /** * 验证Markdown文档结构 * @param text Markdown文本 * @returns 验证结果 */ public async validateMarkdown(text: string): Promise { const request: ValidateMarkdownRequest = { text, }; const response = await invoke( 'validate_markdown', { request } ); if (!response.success || !response.validation) { throw new Error(response.error || 'Failed to validate markdown'); } return response.validation; } /** * 查询标题节点 * @param text Markdown文本 * @returns 标题节点列表 */ public async queryHeadings(text: string): Promise { return this.queryNodes(text, QueryType.Headings); } /** * 查询链接节点 * @param text Markdown文本 * @returns 链接节点列表 */ public async queryLinkNodes(text: string): Promise { return this.queryNodes(text, QueryType.Links); } /** * 查询代码块节点 * @param text Markdown文本 * @returns 代码块节点列表 */ public async queryCodeBlocks(text: string): Promise { return this.queryNodes(text, QueryType.Code); } /** * 获取节点的纯文本内容 * @param node Markdown节点 * @returns 纯文本内容 */ public extractTextContent(node: MarkdownNode): string { if (node.node_type === 'Text') { return node.content; } return node.children .map((child) => this.extractTextContent(child)) .join(''); } /** * 检查节点是否在指定范围内 * @param node Markdown节点 * @param startLine 开始行号 * @param endLine 结束行号 * @returns 是否在范围内 */ public isNodeInRange( node: MarkdownNode, startLine: number, endLine: number ): boolean { return ( node.range.start.line >= startLine && node.range.end.line <= endLine ); } /** * 获取节点的行数 * @param node Markdown节点 * @returns 节点占用的行数 */ public getNodeLineCount(node: MarkdownNode): number { return node.range.end.line - node.range.start.line + 1; } /** * 查找包含指定文本的节点 * @param nodes 节点列表 * @param searchText 搜索文本 * @returns 匹配的节点列表 */ public findNodesContainingText( nodes: MarkdownNode[], searchText: string ): MarkdownNode[] { const results: MarkdownNode[] = []; const searchInNode = (node: MarkdownNode) => { if (node.content.toLowerCase().includes(searchText.toLowerCase())) { results.push(node); } node.children.forEach(searchInNode); }; nodes.forEach(searchInNode); return results; } /** * 获取节点的层级深度 * @param node Markdown节点 * @returns 层级深度 */ public getNodeDepth(node: MarkdownNode): number { let depth = 0; let current = node; while (current.children.length > 0) { depth++; current = current.children[0]; } return depth; } /** * 将位置信息转换为字符串 * @param line 行号 * @param column 列号 * @returns 位置字符串 */ public formatPosition(line: number, column: number): string { return `${line + 1}:${column + 1}`; } /** * 将范围信息转换为字符串 * @param start 开始位置 * @param end 结束位置 * @returns 范围字符串 */ public formatRange( start: { line: number; column: number }, end: { line: number; column: number } ): string { return `${this.formatPosition(start.line, start.column)}-${this.formatPosition(end.line, end.column)}`; } } /** * 导出单例实例 */ export const markdownService = MarkdownService.getInstance(); /** * 默认导出 */ export default markdownService;