mirror of
https://github.com/purocean/yn.git
synced 2026-09-24 15:46:02 +08:00
feat: 支持解析 block html
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user