Skip to content

Commit

Permalink
feat: add prism highlight support (#4119)
Browse files Browse the repository at this point in the history
* feat(default_config): add prismjs entry
* feat(backtick_filter): add prismjs support
* feat(code_tag): add prismjs support
* feat(include_code_tag): add prismjs support
* feat(backtick_code): remove strip-indent & add escape
* refactor(backtick_code): escape when no highlight enabled
* test(code_tags): disabled highlight
* refactor/feat(tag/code): bring up prismjs again
* chore: restore package.json
  • Loading branch information
SukkaW authored Jun 8, 2020
1 parent 7e604bf commit f36e29c
Show file tree
Hide file tree
Showing 7 changed files with 517 additions and 115 deletions.
6 changes: 6 additions & 0 deletions lib/hexo/default_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ module.exports = {
wrap: true,
hljs: false
},
prismjs: {
enable: false,
preprocess: true,
line_number: true,
tab_replace: ''
},
// Category & Tag
default_category: 'uncategorized',
category_map: {},
Expand Down
90 changes: 59 additions & 31 deletions lib/plugins/filter/before_post_render/backtick_code_block.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,38 @@
'use strict';

const { highlight } = require('hexo-util');
let highlight, prismHighlight;

const rBacktick = /^((?:[^\S\r\n]*>){0,3}[^\S\r\n]*)(`{3,}|~{3,})[^\S\r\n]*((?:.*?[^`\s])?)[^\S\r\n]*\n((?:[\s\S]*?\n)?)(?:(?:[^\S\r\n]*>){0,3}[^\S\r\n]*)\2(\n+|$)/gm;
const rAllOptions = /([^\s]+)\s+(.+?)\s+(https?:\/\/\S+|\/\S+)\s*(.+)?/;
const rLangCaption = /([^\s]+)\s*(.+)?/;

const escapeSwigTag = str => str.replace(/{/g, '{').replace(/}/g, '}');

function backtickCodeBlock(data) {
const config = this.config.highlight || {};
if (!config.enable) return;
const hljsCfg = this.config.highlight || {};
const prismCfg = this.config.prismjs || {};

data.content = data.content.replace(rBacktick, ($0, start, $2, _args, _content, end) => {
let content = _content.replace(/\n$/, '');
const args = _args.split('=').shift();

const options = {
hljs: config.hljs,
autoDetect: config.auto_detect,
gutter: config.line_number,
tab: config.tab_replace,
wrap: config.wrap
};

if (options.gutter) {
config.first_line_number = config.first_line_number || 'always1';
if (config.first_line_number === 'inline') {
// neither highlight or prismjs is enabled, return escaped content directly.
if (!hljsCfg.enable && !prismCfg.enable) return escapeSwigTag($0);

// setup line number by inline
_args = _args.replace('=+', '=');
options.gutter = _args.includes('=');

// setup fiestLineNumber;
options.firstLine = options.gutter ? _args.split('=')[1] || 1 : 0;
}
}
// Extrace langauge and caption of code blocks
const args = _args.split('=').shift();
let lang, caption;

if (args) {
const match = rAllOptions.exec(args) || rLangCaption.exec(args);


if (match) {
options.lang = match[1];
lang = match[1];

if (match[2]) {
options.caption = `<span>${match[2]}</span>`;
caption = `<span>${match[2]}</span>`;

if (match[3]) {
options.caption += `<a href="${match[3]}">${match[4] ? match[4] : 'link'}</a>`;
caption += `<a href="${match[3]}">${match[4] ? match[4] : 'link'}</a>`;
}
}
}
Expand All @@ -59,11 +46,52 @@ function backtickCodeBlock(data) {
content = content.replace(regexp, '');
}

content = highlight(content, options)
.replace(/{/g, '&#123;')
.replace(/}/g, '&#125;');
// Since prismjs have better performance, so prismjs should have higher priority.
if (prismCfg.enable) {
if (!prismHighlight) prismHighlight = require('hexo-util').prismHighlight;

const options = {
lineNumber: prismCfg.line_number,
tab: prismCfg.tab_replace,
isPreprocess: prismCfg.preprocess,
lang
};

content = prismHighlight(content, options);
} else if (hljsCfg.enable) {
if (!highlight) highlight = require('hexo-util').highlight;

const options = {
hljs: hljsCfg.hljs,
autoDetect: hljsCfg.auto_detect,
gutter: hljsCfg.line_number,
tab: hljsCfg.tab_replace,
wrap: hljsCfg.wrap,
lang,
caption
};

if (options.gutter) {
hljsCfg.first_line_number = hljsCfg.first_line_number || 'always1';
if (hljsCfg.first_line_number === 'inline') {

// setup line number by inline
_args = _args.replace('=+', '=');
options.gutter = _args.includes('=');

// setup firstLineNumber;
options.firstLine = options.gutter ? _args.split('=')[1] || 1 : 0;
}
}

content = highlight(content, options);
}

return `${start}<!--hexoPostRenderEscape:${content}:hexoPostRenderEscape-->${end}`;
return start
+ '<!--hexoPostRenderEscape:'
+ escapeSwigTag(content)
+ ':hexoPostRenderEscape-->'
+ end;
});
}

Expand Down
70 changes: 47 additions & 23 deletions lib/plugins/tag/code.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// Based on: https://raw.github.com/imathis/octopress/master/plugins/code_block.rb

const { escapeHTML, highlight } = require('hexo-util');
const { escapeHTML, highlight, prismHighlight } = require('hexo-util');

const rCaptionUrlTitle = /(\S[\S\s]*)\s+(https?:\/\/\S+)\s+(.+)/i;
const rCaptionUrl = /(\S[\S\s]*)\s+(https?:\/\/\S+)/i;
Expand All @@ -25,13 +25,13 @@ const rCaption = /\S[\S\s]*/;
* Example: `mark:1,4-7,10` will mark line 1, 4 to 7 and 10.
* @param {Object} wrap Wrap the code block in <table>, value must be a boolean
* @returns {String} Code snippet with code highlighting
*/
*/

function getHighlightOptions(config, args) {
function parseArgs(args) {
const _else = [];
const len = args.length;
let lang = '';
let { line_number, wrap } = config;
let lang,
line_number, wrap;
let firstLine = 1;
const mark = [];
for (let i = 0; i < len; i++) {
Expand Down Expand Up @@ -86,8 +86,9 @@ function getHighlightOptions(config, args) {
}

const arg = _else.join(' ');
let caption = '';
let match;
// eslint-disable-next-line one-var
let match, caption = '';

if ((match = arg.match(rCaptionUrlTitle)) != null) {
caption = `<span>${match[1]}</span><a href="${match[2]}">${match[3]}</a>`;
} else if ((match = arg.match(rCaptionUrl)) != null) {
Expand All @@ -100,37 +101,60 @@ function getHighlightOptions(config, args) {
lang,
firstLine,
caption,
gutter: line_number,
hljs: config.hljs,
line_number,
mark,
tab: config.tab_replace,
autoDetect: config.auto_detect,
wrap
};
}

module.exports = ctx => function codeTag(args, content) {
const config = ctx.config.highlight || {};
let { enable } = config;
const hljsCfg = ctx.config.highlight || {};
const prismjsCfg = ctx.config.prismjs || {};

let index;
let enableHighlight = true;

if ((index = args.findIndex(item => item.startsWith('highlight:'))) !== -1) {
const arg = args[index];
const _enable = arg.slice(10);
enable = _enable === 'true';
const highlightStr = arg.slice(10);
enableHighlight = highlightStr === 'true';
args.splice(index, 1);
}

if (!enable) {
content = escapeHTML(content);
return `<pre><code>${content}</code></pre>`;
// If 'hilight: false' is given, return escaped code directly
// If neither highlight.js nor prism.js is enabled, return escaped code directly
if (!enableHighlight || (!hljsCfg.enable && !prismjsCfg.enable)) {
return `<pre><code>${escapeHTML(content)}</code></pre>`;
}

content = highlight(content, getHighlightOptions(config, args));

content = content.replace(/{/g, '&#123;')
.replace(/}/g, '&#125;');
const { lang, firstLine, caption, line_number, mark, wrap } = parseArgs(args);

if (prismjsCfg.enable) {
const prismjsOption = {
lang,
firstLine,
lineNumber: typeof line_number !== 'undefined' ? line_number : prismjsCfg.line_number,
mark,
tab: prismjsCfg.tab_replace,
isPreprocess: prismjsCfg.preprocess
};

content = prismHighlight(content, prismjsOption);
} else {
const hljsOption = {
lang: typeof lang !== 'undefined' ? lang : '',
firstLine,
caption,
gutter: typeof line_number !== 'undefined' ? line_number : hljsCfg.line_number,
hljs: hljsCfg.hljs,
mark,
tab: hljsCfg.tab_replace,
autoDetect: hljsCfg.auto_detect,
wrap: typeof wrap === 'undefined' ? wrap : hljsCfg.wrap
};

content = highlight(content, hljsOption);
}

return content;
return content.replace(/{/g, '&#123;').replace(/}/g, '&#125;');
};
34 changes: 22 additions & 12 deletions lib/plugins/tag/include_code.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const fs = require('hexo-fs');
const { basename, extname, join } = require('path');
const { highlight } = require('hexo-util');
const { highlight, prismHighlight } = require('hexo-util');

const rCaptionTitleFile = /(.*)?(?:\s+|^)(\/*\S+)/;
const rLang = /\s*lang:(\w+)/i;
Expand All @@ -17,7 +17,6 @@ const rTo = /\s*to:(\d+)/i;
*/

module.exports = ctx => function includeCodeTag(args) {
const config = ctx.config.highlight || {};
let codeDir = ctx.config.code_dir;
let arg = args.join(' ');

Expand Down Expand Up @@ -47,22 +46,31 @@ module.exports = ctx => function includeCodeTag(args) {

const path = match[2];

// If the title is not defined, use file name instead
const title = match[1] || basename(path);

// If the language is not defined, use file extension instead
lang = lang || extname(path).substring(1);

const src = join(ctx.source_dir, codeDir, path);

// If the title is not defined, use file name instead
const title = match[1] || basename(path);
const caption = `<span>${title}</span><a href="${ctx.config.root}${codeDir}${path}">view raw</a>`;

const options = {
const hljsCfg = ctx.config.highlight || {};
const prismjsCfg = ctx.config.prismjs || {};

const hljsOptions = {
lang,
caption,
gutter: config.line_number,
hljs: config.hljs,
tab: config.tab_replace
gutter: hljsCfg.line_number,
hljs: hljsCfg.hljs,
tab: hljsCfg.tab_replace
};

const prismjsOptions = {
lang,
lineNumber: prismjsCfg.line_number,
tab: prismjsCfg.tab_replace,
isPreprocess: prismjsCfg.preprocess
};

return fs.exists(src).then(exist => {
Expand All @@ -73,10 +81,12 @@ module.exports = ctx => function includeCodeTag(args) {
const lines = code.split('\n');
code = lines.slice(from, to).join('\n').trim();

if (!config.enable) {
return `<pre><code>${code}</code></pre>`;
if (prismjsCfg.enable) {
return prismHighlight(code, prismjsOptions);
} else if (hljsCfg.enable) {
return highlight(code, hljsOptions);
}

return highlight(code, options);
return `<pre><code>${code}</code></pre>`;
});
};
Loading

0 comments on commit f36e29c

Please sign in to comment.