重构markdown解析器
parent
29dff0d99c
commit
6b17ad76ba
|
@ -11,6 +11,8 @@
|
||||||
}
|
}
|
||||||
.markd {
|
.markd {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
// white-space: pre-wrap;
|
||||||
|
// word-wrap: break-word;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
@ -27,6 +29,12 @@ em {
|
||||||
strong {
|
strong {
|
||||||
color: nth($cd, 3);
|
color: nth($cd, 3);
|
||||||
}
|
}
|
||||||
|
a {
|
||||||
|
strong,
|
||||||
|
em {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
em,
|
em,
|
||||||
strong,
|
strong,
|
||||||
del {
|
del {
|
||||||
|
@ -39,19 +47,17 @@ img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote.md-quote {
|
||||||
&.md-quote {
|
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
|
line-height: 1.5;
|
||||||
border-left: 5px solid nth($ct, 1);
|
border-left: 5px solid nth($ct, 1);
|
||||||
background: #f2faf7;
|
background: #f2faf7;
|
||||||
color: nth($cgr, 1);
|
color: nth($cgr, 1);
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* 提醒文本 */
|
/* 提醒文本 */
|
||||||
.md-warn,
|
.md-warn,
|
||||||
|
@ -171,14 +177,13 @@ h6 {
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
visibility: hidden;
|
|
||||||
position: absolute;
|
|
||||||
left: -25px;
|
|
||||||
width: 25px;
|
|
||||||
padding: 0 3px;
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
text-align: center;
|
color: #333;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '# ';
|
||||||
|
color: nth($ct, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&:hover a {
|
&:hover a {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
|
@ -225,7 +230,12 @@ table {
|
||||||
}
|
}
|
||||||
|
|
||||||
code.inline {
|
code.inline {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 2px;
|
||||||
|
padding: 0 3px;
|
||||||
color: nth($co, 3);
|
color: nth($co, 3);
|
||||||
|
background: nth($cp, 1);
|
||||||
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -233,7 +243,8 @@ code.inline {
|
||||||
import $ from '../utils'
|
import $ from '../utils'
|
||||||
import '../code/index'
|
import '../code/index'
|
||||||
|
|
||||||
import parser from './core'
|
// import parser from './core'
|
||||||
|
import parser from './parser'
|
||||||
|
|
||||||
export default class Markd {
|
export default class Markd {
|
||||||
props = {}
|
props = {}
|
||||||
|
@ -246,7 +257,8 @@ export default class Markd {
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.__BOX__.innerHTML = parser.safe(this.textContent)
|
// this.__BOX__.innerHTML = parser.safe(this.textContent)
|
||||||
|
this.__BOX__.innerHTML = parser(this.textContent)
|
||||||
this.textContent = ''
|
this.textContent = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
/**
|
||||||
|
* markdown解析器
|
||||||
|
* @author yutent<yutent@doui.cc>
|
||||||
|
* @date 2020/02/07 17:14:19
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
const HR_LIST = ['=', '-', '_', '*']
|
||||||
|
const log = console.log
|
||||||
|
|
||||||
|
const Helper = {
|
||||||
|
// 是否分割线
|
||||||
|
isHr(str) {
|
||||||
|
var s = str[0]
|
||||||
|
if (HR_LIST.includes(s)) {
|
||||||
|
return str.startsWith(s.repeat(3))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Tool = {
|
||||||
|
// 初始化字符串, 处理多余换行等
|
||||||
|
init(str) {
|
||||||
|
// 去掉\r, 将\t转为空格(2个)
|
||||||
|
str = str.replace(/\r/g, '').replace(/\t/g, ' ')
|
||||||
|
var list = []
|
||||||
|
var lines = str.split('\n')
|
||||||
|
var isCodeBlock = false // 是否代码块
|
||||||
|
var emptyLineLength = 0 //连续空行的数量
|
||||||
|
|
||||||
|
for (let it of lines) {
|
||||||
|
let tmp = it.trim()
|
||||||
|
// 空行
|
||||||
|
if (!tmp) {
|
||||||
|
if (list.length === 0 || (!isCodeBlock && emptyLineLength > 0)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
emptyLineLength++
|
||||||
|
list.push(tmp)
|
||||||
|
} else {
|
||||||
|
emptyLineLength = 0
|
||||||
|
if (tmp.startsWith('```')) {
|
||||||
|
if (isCodeBlock) {
|
||||||
|
list.push('</wc-code>')
|
||||||
|
} else {
|
||||||
|
list.push(tmp.replace(/^```([\w\#\-]*?)$/, '<wc-code lang="$1">'))
|
||||||
|
}
|
||||||
|
isCodeBlock = !isCodeBlock
|
||||||
|
} else {
|
||||||
|
list.push(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log(list)
|
||||||
|
this.list = list
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
parse() {
|
||||||
|
var html = ''
|
||||||
|
var isCodeBlock = false // 是否代码块
|
||||||
|
var emptyLineLength = 0 //连续空行的数量
|
||||||
|
var isBlockquote = false
|
||||||
|
var blockquoteLevel = 0
|
||||||
|
var isParagraph = false
|
||||||
|
|
||||||
|
//
|
||||||
|
for (let it of this.list) {
|
||||||
|
// 空行
|
||||||
|
if (!it) {
|
||||||
|
// 如果是在代码中, 直接拼接, 并加上换行
|
||||||
|
if (isCodeBlock) {
|
||||||
|
html += it + '\n'
|
||||||
|
} else {
|
||||||
|
emptyLineLength++
|
||||||
|
|
||||||
|
// 引用结束
|
||||||
|
if (isBlockquote) {
|
||||||
|
isBlockquote = false
|
||||||
|
html += ''
|
||||||
|
if (emptyLineLength > 0) {
|
||||||
|
while (blockquoteLevel > 0) {
|
||||||
|
emptyLineLength = 0
|
||||||
|
blockquoteLevel--
|
||||||
|
html += '</blockquote>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (isParagraph) {
|
||||||
|
isParagraph = false
|
||||||
|
html += '</p>'
|
||||||
|
} /* else {
|
||||||
|
html += '<br>'
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// wc-code标签直接拼接
|
||||||
|
if (~it.indexOf('wc-code')) {
|
||||||
|
html += it
|
||||||
|
isCodeBlock = !isCodeBlock
|
||||||
|
} else {
|
||||||
|
// 同上代码块的处理
|
||||||
|
if (isCodeBlock) {
|
||||||
|
html += it + '\n'
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
it = it
|
||||||
|
.replace(/`(.*?)`/g, '<code class="inline">$1</code>')
|
||||||
|
.replace(/(\-\-|\*\*)(.*?)\1/g, '<strong>$2</strong>')
|
||||||
|
.replace(/(\-|\*)(.*?)\1/g, '<em>$2</em>')
|
||||||
|
.replace(/\!\[([^]*?)\]\(([^)]*?)\)/g, '<img src="$2" alt="$1">')
|
||||||
|
.replace(/\[([^]*?)\]\(([^)]*?)\)/g, '<a href="$2">$1</a>')
|
||||||
|
|
||||||
|
//
|
||||||
|
if (it.startsWith('>')) {
|
||||||
|
html += it.replace(/^(>+) /, m => {
|
||||||
|
let len = m.trim().length
|
||||||
|
let tmp = ''
|
||||||
|
if (isBlockquote) {
|
||||||
|
// 若之前已经有一个未闭合的引用, 需要减去已有缩进级别, 避免产生新的引用标签
|
||||||
|
len = len - blockquoteLevel
|
||||||
|
} else {
|
||||||
|
blockquoteLevel = len
|
||||||
|
}
|
||||||
|
log('bq: ', blockquoteLevel, it)
|
||||||
|
while (len > 0) {
|
||||||
|
len--
|
||||||
|
tmp += '<blockquote class="md-quote">'
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isBlockquote) {
|
||||||
|
html += '<br>'
|
||||||
|
}
|
||||||
|
isParagraph = false
|
||||||
|
isBlockquote = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBlockquote) {
|
||||||
|
html += it
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (it.startsWith('#')) {
|
||||||
|
isParagraph = false
|
||||||
|
let end = ''
|
||||||
|
html += it.replace(/^#{1,6} /, m => {
|
||||||
|
let level = m.trim().length
|
||||||
|
end = `</a></h${level}>`
|
||||||
|
return `<h${level}><a href="#">`
|
||||||
|
})
|
||||||
|
html += end
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// log('it => ', isParagraph, it)
|
||||||
|
if (isParagraph) {
|
||||||
|
html += `${it}<br>`
|
||||||
|
} else {
|
||||||
|
html += `<p>${it}<br>`
|
||||||
|
}
|
||||||
|
isParagraph = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function(str) {
|
||||||
|
return Tool.init(str).parse()
|
||||||
|
}
|
Reference in New Issue