From d405b11fc68813e0a715bab2fe730fc40333be60 Mon Sep 17 00:00:00 2001 From: purocean Date: Tue, 22 Jun 2021 14:41:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=9D=97=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/components/Preview.vue | 16 +- src/renderer/plugins/code-line-number.ts | 164 ------------------ src/renderer/plugins/markdown-code.ts | 159 +++++++++++++++++ src/renderer/plugins/markdown-render-vnode.ts | 2 +- src/renderer/useful/plugin/build-in.ts | 2 - 5 files changed, 161 insertions(+), 182 deletions(-) delete mode 100644 src/renderer/plugins/code-line-number.ts diff --git a/src/renderer/components/Preview.vue b/src/renderer/components/Preview.vue index b616f3cc..62ed26a7 100644 --- a/src/renderer/components/Preview.vue +++ b/src/renderer/components/Preview.vue @@ -47,7 +47,6 @@ import Markdown from 'markdown-it' import katex from 'markdown-it-katex' import MarkdownItAttrs from 'markdown-it-attrs' import MultimdTable from 'markdown-it-multimd-table' -import Highlight from 'highlight.js' import file from '@fe/useful/file' import { triggerHook } from '@fe/useful/plugin' @@ -59,20 +58,7 @@ import 'github-markdown-css/github-markdown.css' import 'highlight.js/styles/atom-one-dark.css' import 'katex/dist/katex.min.css' -const markdown = Markdown({ - linkify: true, - breaks: true, - html: true, - highlight: (str: string, lang: string) => { - if (lang && Highlight.getLanguage(lang)) { - try { - return Highlight.highlight(lang, str).value - } catch (__) {} - } - - return '' - } -}) +const markdown = Markdown({ linkify: true, breaks: true, html: true, }) .use(katex) .use(MarkdownItAttrs) .use(MultimdTable, { multiline: true }) diff --git a/src/renderer/plugins/code-line-number.ts b/src/renderer/plugins/code-line-number.ts deleted file mode 100644 index e727c02e..00000000 --- a/src/renderer/plugins/code-line-number.ts +++ /dev/null @@ -1,164 +0,0 @@ -/* eslint-disable */ - -// https://github.com/wcoder/highlightjs-line-numbers.js - -import { Plugin } from '@fe/useful/plugin' - -const TABLE_NAME = 'hljs-ln' -const LINE_NAME = 'hljs-ln-line' -const CODE_BLOCK_NAME = 'hljs-ln-code' -const NUMBERS_BLOCK_NAME = 'hljs-ln-numbers' -const NUMBER_LINE_NAME = 'hljs-ln-n' -const DATA_ATTR_NAME = 'data-line-number' -const BREAK_LINE_REGEXP = /\r\n|\r|\n/g - -function addStyles () { - var css = document.createElement('style') - css.type = 'text/css' - css.innerHTML = format( - '.{0}{border-collapse:collapse}' + - '.{0} td{padding:0}' + - '.{1}:before{content:attr({2})}', - [ - TABLE_NAME, - NUMBER_LINE_NAME, - DATA_ATTR_NAME - ]) - document.getElementsByTagName('head')[0].appendChild(css) -} - -function lineNumbersBlock (element: any, options?: any) { - if (typeof element !== 'object') return - - async(function () { - element.innerHTML = lineNumbersInternal(element, options) - }) -} - -function lineNumbersInternal (element: any, options: any) { - // define options or set default - options = options || { - singleLine: false - } - - // convert options - var firstLineIndex = options.singleLine ? 0 : 1 - - duplicateMultilineNodes(element) - - return addLineNumbersBlockFor(element.innerHTML, firstLineIndex) -} - -function addLineNumbersBlockFor (inputHtml: any, firstLineIndex: any) { - var lines = getLines(inputHtml) - - // if last line contains only carriage return remove it - if (lines[lines.length - 1].trim() === '') { - lines.pop() - } - - if (lines.length > firstLineIndex) { - var html = '' - - for (var i = 0, l = lines.length; i < l; i++) { - html += format( - '' + - '' + - '
' + - '' + - '' + - '
{6}
' + - '' + - '', - [ - NUMBERS_BLOCK_NAME, - LINE_NAME, - NUMBER_LINE_NAME, - DATA_ATTR_NAME, - CODE_BLOCK_NAME, - i + 1, - lines[i].length > 0 ? lines[i] : ' ' - ]) - } - - return format('{1}
', [ TABLE_NAME, html ]) - } - - return inputHtml -} - -/** - * Recursive method for fix multi-line elements implementation in highlight.js - * Doing deep passage on child nodes. - * @param {HTMLElement} element - */ -function duplicateMultilineNodes (element: any) { - var nodes = element.childNodes - for (var node in nodes) { - if (nodes.hasOwnProperty(node)) { - var child = nodes[node] - if (getLinesCount(child.textContent) > 0) { - if (child.childNodes.length > 0) { - duplicateMultilineNodes(child) - } else { - duplicateMultilineNode(child.parentNode) - } - } - } - } -} - -/** - * Method for fix multi-line elements implementation in highlight.js - * @param {HTMLElement} element - */ -function duplicateMultilineNode (element: any) { - var className = element.className - - if (!/hljs-/.test(className)) return - - var lines = getLines(element.innerHTML) - - for (var i = 0, result = ''; i < lines.length; i++) { - var lineText = lines[i].length > 0 ? lines[i] : ' ' - result += format('{1}\n', [ className, lineText ]) - } - - element.innerHTML = result.trim() -} - -function getLines (text: string) { - if (text.length === 0) return [] - return text.split(BREAK_LINE_REGEXP) -} - -function getLinesCount (text: string) { - return (text.trim().match(BREAK_LINE_REGEXP) || []).length -} - -function async (func: Function) { - setTimeout(func, 0) -} - -/** - * {@link https://wcoder.github.io/notes/string-format-for-string-formating-in-javascript} - * @param {string} format - * @param {array} args - */ -function format (format: string, args: any) { - return format.replace(/\{(\d+)\}/g, function (m, n) { - return args[n] ? args[n] : m - }) -} - -export default { - name: 'markdown-toc', - register: ctx => { - ctx.registerHook('ON_STARTUP', addStyles) - ctx.registerHook('ON_VIEW_RENDERED', ({ getViewDom }) => { - const refView: HTMLElement = getViewDom() - const nodes = refView.querySelectorAll('code[class^="language-"]') - nodes.forEach(lineNumbersBlock) - }) - } -} as Plugin diff --git a/src/renderer/plugins/markdown-code.ts b/src/renderer/plugins/markdown-code.ts index b4944ea3..43f3b9f4 100644 --- a/src/renderer/plugins/markdown-code.ts +++ b/src/renderer/plugins/markdown-code.ts @@ -1,9 +1,167 @@ +import Highlight from 'highlight.js' import { getKeyLabel } from '@fe/useful/shortcut' import { Plugin, Ctx } from '@fe/useful/plugin' +import { getLogger } from '../useful/utils' + +const logger = getLogger('markdown-code') + +/* eslint-disable */ + +// https://github.com/wcoder/highlightjs-line-numbers.js + +const TABLE_NAME = 'hljs-ln' +const LINE_NAME = 'hljs-ln-line' +const CODE_BLOCK_NAME = 'hljs-ln-code' +const NUMBERS_BLOCK_NAME = 'hljs-ln-numbers' +const NUMBER_LINE_NAME = 'hljs-ln-n' +const DATA_ATTR_NAME = 'data-line-number' +const BREAK_LINE_REGEXP = /\r\n|\r|\n/g + +function addStyles () { + var css = document.createElement('style') + css.type = 'text/css' + css.innerHTML = format( + '.{0}{border-collapse:collapse}' + + '.{0} td{padding:0}' + + '.{1}:before{content:attr({2})}', + [ + TABLE_NAME, + NUMBER_LINE_NAME, + DATA_ATTR_NAME + ]) + document.getElementsByTagName('head')[0].appendChild(css) +} + +function lineNumbersInternal (element: any, options?: any) { + // define options or set default + options = options || { + singleLine: false + } + + // convert options + var firstLineIndex = options.singleLine ? 0 : 1 + + duplicateMultilineNodes(element) + + return addLineNumbersBlockFor(element.innerHTML, firstLineIndex) +} + +function addLineNumbersBlockFor (inputHtml: any, firstLineIndex: any) { + var lines = getLines(inputHtml) + + // if last line contains only carriage return remove it + if (lines[lines.length - 1].trim() === '') { + lines.pop() + } + + if (lines.length > firstLineIndex) { + var html = '' + + for (var i = 0, l = lines.length; i < l; i++) { + html += format( + '' + + '' + + '
' + + '' + + '' + + '
{6}
' + + '' + + '', + [ + NUMBERS_BLOCK_NAME, + LINE_NAME, + NUMBER_LINE_NAME, + DATA_ATTR_NAME, + CODE_BLOCK_NAME, + i + 1, + lines[i].length > 0 ? lines[i] : ' ' + ]) + } + + return format('{1}
', [ TABLE_NAME, html ]) + } + + return inputHtml +} + +/** + * Recursive method for fix multi-line elements implementation in highlight.js + * Doing deep passage on child nodes. + * @param {HTMLElement} element + */ +function duplicateMultilineNodes (element: any) { + var nodes = element.childNodes + for (var node in nodes) { + if (nodes.hasOwnProperty(node)) { + var child = nodes[node] + if (getLinesCount(child.textContent) > 0) { + if (child.childNodes.length > 0) { + duplicateMultilineNodes(child) + } else { + duplicateMultilineNode(child.parentNode) + } + } + } + } +} + +/** + * Method for fix multi-line elements implementation in highlight.js + * @param {HTMLElement} element + */ +function duplicateMultilineNode (element: any) { + var className = element.className + + if (!/hljs-/.test(className)) return + + var lines = getLines(element.innerHTML) + + for (var i = 0, result = ''; i < lines.length; i++) { + var lineText = lines[i].length > 0 ? lines[i] : ' ' + result += format('{1}\n', [ className, lineText ]) + } + + element.innerHTML = result.trim() +} + +function getLines (text: string) { + if (text.length === 0) return [] + return text.split(BREAK_LINE_REGEXP) +} + +function getLinesCount (text: string) { + return (text.trim().match(BREAK_LINE_REGEXP) || []).length +} + +/** + * {@link https://wcoder.github.io/notes/string-format-for-string-formating-in-javascript} + * @param {string} format + * @param {array} args + */ +function format (format: string, args: any) { + return format.replace(/\{(\d+)\}/g, function (m, n) { + return args[n] ? args[n] : m + }) +} + +function highlight (str: string, lang: string) { + if (lang && Highlight.getLanguage(lang)) { + try { + const element = document.createElement('code') + element.innerHTML = Highlight.highlight(lang, str).value + return lineNumbersInternal(element) + } catch (error) { + logger.error(error) + } + } + + return '' +} export default { name: 'markdown-code', register: (ctx: Ctx) => { + ctx.registerHook('ON_STARTUP', addStyles) ctx.markdown.registerPlugin(md => { const Fun = (fn: Function) => (tokens: any, idx: any, options: any, env: any, slf: any) => { if (tokens[idx].attrIndex('title') < 0) { @@ -15,6 +173,7 @@ export default { } md.renderer.rules.code_inline = Fun(md.renderer.rules.code_inline!.bind(md.renderer.rules)) + md.options.highlight = highlight as any }) } } as Plugin diff --git a/src/renderer/plugins/markdown-render-vnode.ts b/src/renderer/plugins/markdown-render-vnode.ts index b9cb9cf6..3a242662 100644 --- a/src/renderer/plugins/markdown-render-vnode.ts +++ b/src/renderer/plugins/markdown-render-vnode.ts @@ -46,7 +46,7 @@ defaultRules.fence = function (tokens: Token[], idx: number, options: any, _: an const buildVNode = (arrts: any) => createVNode( 'pre', {}, - [createVNode('code', { key: highlighted, ...arrts, innerHTML: highlighted }, [])] + [createVNode('code', { ...arrts, innerHTML: highlighted }, [])] ) // If language exists, inject class gently, without modifying original token. diff --git a/src/renderer/useful/plugin/build-in.ts b/src/renderer/useful/plugin/build-in.ts index 8d1d2948..ac7ce02f 100644 --- a/src/renderer/useful/plugin/build-in.ts +++ b/src/renderer/useful/plugin/build-in.ts @@ -11,7 +11,6 @@ import markdownCode from '@fe/plugins/markdown-code' import markdownLink from '@fe/plugins/markdown-link' import markdownTaskList from '@fe/plugins/markdown-task-list' import markdownFootnote from '@/renderer/plugins/markdown-footnote' -import codeLineNumber from '@fe/plugins/code-line-number' import transformImgOutLink from '@fe/plugins/transform-img-out-link' import copyText from '@fe/plugins/copy-text' import tableCellEdit from '@fe/plugins/table-cell-edit' @@ -38,7 +37,6 @@ export default [ markdownLink, markdownTaskList, markdownFootnote, - codeLineNumber, transformImgOutLink, copyText, tableCellEdit,