mixvideo-v2/apps/desktop/src/services/markdownService.ts

332 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<MarkdownParseResult> {
const request: ParseMarkdownRequest = {
text,
config,
};
const response = await invoke<ParseMarkdownResponse>('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<MarkdownNode[]> {
const request: QueryNodesRequest = {
text,
query_name: queryType,
};
const response = await invoke<QueryNodesResponse>('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<MarkdownNode | null> {
const request: FindNodeAtPositionRequest = {
text,
line,
column,
};
const response = await invoke<FindNodeAtPositionResponse>(
'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<OutlineItem[]> {
const request: ExtractOutlineRequest = {
text,
};
const response = await invoke<ExtractOutlineResponse>(
'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<LinkInfo[]> {
const request: ExtractLinksRequest = {
text,
};
const response = await invoke<ExtractLinksResponse>(
'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<ValidationResult> {
const request: ValidateMarkdownRequest = {
text,
};
const response = await invoke<ValidateMarkdownResponse>(
'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<MarkdownNode[]> {
return this.queryNodes(text, QueryType.Headings);
}
/**
* 查询链接节点
* @param text Markdown文本
* @returns 链接节点列表
*/
public async queryLinkNodes(text: string): Promise<MarkdownNode[]> {
return this.queryNodes(text, QueryType.Links);
}
/**
* 查询代码块节点
* @param text Markdown文本
* @returns 代码块节点列表
*/
public async queryCodeBlocks(text: string): Promise<MarkdownNode[]> {
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;