/* 文件:markdown/parser.ts — Markdown 解析器的扩展与语法注册入口。 */
import { Marked } from "marked";
import { NMPSettings } from "src/settings";
import { App, Vault } from "obsidian";
import AssetsManager from "../assets";
import { Extension, MDRendererCallback } from "./extension";
import { Blockquote} from "./blockquote";
import { CodeRenderer } from "./code";
import { EmbedBlockMark } from "./embed-block-mark";
import { SVGIcon } from "./icons";
import { LinkRenderer } from "./link";
import { LocalFile, LocalImageManager } from "./local-file";
import { MathRenderer } from "./math";
import { TextHighlight } from "./text-highlight";
import { Comment } from "./commnet";
import { Topic } from "./topic";
import { HeadingRenderer } from "./heading";
import { FootnoteRenderer } from "./footnote";
import { EmptyLineRenderer } from "./empty-line";
import { cleanUrl } from "../utils";
const markedOptiones = {
gfm: true,
breaks: true,
};
const customRenderer = {
hr(): string {
return '
';
},
list(body: string, ordered: boolean, start: number | ''): string {
const type = ordered ? 'ol' : 'ul';
const startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
return '<' + type + startatt + ' class="list-paddingleft-1">' + body + '' + type + '>';
},
listitem(text: string, task: boolean, checked: boolean): string {
return ``;
},
image(href: string, title: string | null, text: string): string {
const cleanHref = cleanUrl(href);
if (cleanHref === null) {
return text;
}
href = cleanHref;
if (!href.startsWith('http')) {
const res = AssetsManager.getInstance().getResourcePath(decodeURI(href));
if (res) {
href = res.resUrl;
const info = {
resUrl: res.resUrl,
filePath: res.filePath,
media_id: null,
url: null
};
LocalImageManager.getInstance().setImage(res.resUrl, info);
}
}
let out = '';
if (NMPSettings.getInstance().useFigcaption) {
out = `
0) {
out += `>${text}`;
}
else {
out += '>'
}
}
else {
out = `
';
}
return out;
}
};
export class MarkedParser {
extensions: Extension[] = [];
marked: Marked;
app: App;
vault: Vault;
constructor(app: App, callback: MDRendererCallback) {
this.app = app;
this.vault = app.vault;
const settings = NMPSettings.getInstance();
const assetsManager = AssetsManager.getInstance();
this.extensions.push(new LocalFile(app, settings, assetsManager, callback));
this.extensions.push(new Blockquote(app, settings, assetsManager, callback));
this.extensions.push(new EmbedBlockMark(app, settings, assetsManager, callback));
this.extensions.push(new SVGIcon(app, settings, assetsManager, callback));
this.extensions.push(new LinkRenderer(app, settings, assetsManager, callback));
this.extensions.push(new TextHighlight(app, settings, assetsManager, callback));
this.extensions.push(new CodeRenderer(app, settings, assetsManager, callback));
this.extensions.push(new Comment(app, settings, assetsManager, callback));
this.extensions.push(new Topic(app, settings, assetsManager, callback));
this.extensions.push(new HeadingRenderer(app, settings, assetsManager, callback));
this.extensions.push(new FootnoteRenderer(app, settings, assetsManager, callback));
if (settings.enableEmptyLine) {
this.extensions.push(new EmptyLineRenderer(app, settings, assetsManager, callback));
}
if (settings.isAuthKeyVaild()) {
this.extensions.push(new MathRenderer(app, settings, assetsManager, callback));
}
}
async buildMarked() {
this.marked = new Marked();
this.marked.use(markedOptiones);
for (const ext of this.extensions) {
this.marked.use(ext.markedExtension());
ext.marked = this.marked;
await ext.prepare();
}
this.marked.use({renderer: customRenderer});
}
async prepare() {
this.extensions.forEach(async ext => await ext.prepare());
}
async postprocess(html: string) {
let result = html;
for (let ext of this.extensions) {
result = await ext.postprocess(result);
}
return result;
}
async parse(content: string) {
if (!this.marked) await this.buildMarked();
await this.prepare();
let html = await this.marked.parse(content);
html = await this.postprocess(html);
return html;
}
}