diff --git a/src/renderer/plugins/markdown-html.ts b/src/renderer/plugins/markdown-html.ts
index 74e0c5c5..d5456c09 100644
--- a/src/renderer/plugins/markdown-html.ts
+++ b/src/renderer/plugins/markdown-html.ts
@@ -1,7 +1,9 @@
import { Plugin } from '@fe/useful/plugin'
-import MarkdownIt from 'markdown-it'
+import StateBlock from 'markdown-it/lib/rules_block/state_block'
import StateInline from 'markdown-it/lib/rules_inline/state_inline'
import Token from 'markdown-it/lib/token'
+import ParserInline from 'markdown-it/lib/parser_inline'
+import MarkdownIt from 'markdown-it'
const unquoted = '[^"\'=<>`\\x00-\\x20]+'
const singleQuoted = "'[^']*'"
@@ -25,7 +27,6 @@ function isLetter (ch: number) {
}
function htmlInline (state: StateInline): boolean {
- console.log('www', state)
const pos = state.pos
if (!state.md.options.html) { return false }
@@ -45,8 +46,6 @@ function htmlInline (state: StateInline): boolean {
const match = state.src.slice(pos).match(HTML_TAG_RE)
if (!match) { return false }
- console.log('mmm', match)
-
const content = state.src.slice(pos, pos + match[0].length)
// ignore comment
@@ -59,8 +58,6 @@ function htmlInline (state: StateInline): boolean {
const div = document.createElement('div')
div.innerHTML = content
- console.log(div.children, 'www')
-
const element = div.children[0]
if (!element) {
return
@@ -74,21 +71,21 @@ function htmlInline (state: StateInline): boolean {
}
}
- const prevHtmlTags = (state.md as any)._prev_html_tags
+ const prevHtmlTags = (state.md as any)._prev_inline_html_tags
let token: Token
if (content.startsWith('')) {
const prevHtmlTag = prevHtmlTags.pop()
if (prevHtmlTags && prevHtmlTag === tag) {
- token = state.push('html_inline_close', tag, -1)
+ token = state.push('html_close', tag, -1)
} else {
console.warn('html tag not match', prevHtmlTag, tag)
return false
}
} else if (content.endsWith('/>') || HTML_SELF_CLOSE_TAG_RE.test(content)) {
- token = state.push('html_inline_self', tag, 0)
+ token = state.push('html_self', tag, 0)
} else {
- token = state.push('html_inline_open', tag, 1)
+ token = state.push('html_open', tag, 1)
prevHtmlTags.push(tag)
}
@@ -99,15 +96,88 @@ function htmlInline (state: StateInline): boolean {
return true
}
+function htmlBlock (state: StateBlock, startLine: number, endLine: number) {
+ let nextLine; let lineText
+ let pos = state.bMarks[startLine] + state.tShift[startLine]
+ let max = state.eMarks[startLine]
+
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
+ if (!state.md.options.html) { return false }
+ if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false }
+
+ const inlineParse: any = state.md.inline.parse.bind(state.md.inline)
+ const pushState = state.push
+ const prevHtmlTags = (state.md as any)._prev_block_html_tags
+
+ lineText = state.src.slice(pos, max)
+ // 调用行内解析规则
+ inlineParse(lineText, state.md, state.env, state.tokens, prevHtmlTags, pushState)
+
+ // If we are here - we detected HTML block.
+ for (nextLine = startLine + 1; nextLine < endLine; nextLine++) {
+ if (state.sCount[nextLine] < state.blkIndent) { break }
+
+ pos = state.bMarks[nextLine] + state.tShift[nextLine]
+ max = state.eMarks[nextLine]
+ lineText = state.src.slice(pos, max)
+
+ if (lineText.length === 0) {
+ break
+ }
+
+ inlineParse(lineText, state.md, state.env, state.tokens, prevHtmlTags, pushState)
+ }
+
+ const token = state.push('html_end', '', 0)
+ token.hidden = true
+ token.map = [startLine, nextLine]
+ token.content = state.getLines(startLine, nextLine, state.blkIndent, true)
+
+ state.line = nextLine
+
+ return true
+}
+
+function inlineParse (this: ParserInline, src: string, md: any, env: any, outTokens: Token[], prevHtmlTags: string[], htmlBlock: boolean) {
+ // 每次解析 inline 时候,重置或者使用外部的 html 标签堆栈
+ md._prev_inline_html_tags = prevHtmlTags || []
+
+ const state = new this.State(src, md, env, outTokens)
+
+ // 如果是 html block 规则调用的,这里 hack 一下,避免 delimiters 为 undefined
+ if (htmlBlock) {
+ Object.defineProperty(state, 'delimiters', {
+ get () {
+ return this._delimiters || []
+ },
+ set (val) {
+ this._delimiters = val
+ },
+ })
+ }
+
+ this.tokenize(state)
+
+ this.ruler2.getRules('').forEach(rule => {
+ rule(state)
+ })
+}
+
+function parse (this: any, src: string, env: any) {
+ // 每次运行解析程序,设定一个 block html 标签栈,便于后续处理程序匹配标签
+ this._prev_block_html_tags = []
+ return MarkdownIt.prototype.parse.call(this, src, env)
+}
+
export default {
name: 'markdown-html',
register: ctx => {
ctx.markdown.registerPlugin(md => {
- md.parse = function (src: string, env: any) {
- (this as any)._prev_html_tags = []
- return MarkdownIt.prototype.parse.call(this, src, env)
- }
+ md.parse = parse
+ md.inline.parse = inlineParse as any
md.inline.ruler.at('html_inline', htmlInline)
+ md.block.ruler.at('html_block', htmlBlock)
})
}
} as Plugin