Skip to content

Commit 9e696d3

Browse files
authored
feat: Support additional options for Backtick Code Block (#5625)
1 parent fbb69c3 commit 9e696d3

File tree

6 files changed

+464
-36
lines changed

6 files changed

+464
-36
lines changed

lib/extend/syntax_highlight.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ export interface HighlightOptions {
1010

1111
// plugins/tag/code.ts
1212
language_attr?: boolean | undefined;
13-
firstLine?: number;
13+
firstLine?: string | number;
1414
line_number?: boolean | undefined;
1515
line_threshold?: number | undefined;
16-
mark?: number[];
16+
mark?: number[] | string;
1717
wrap?: boolean | undefined;
1818

1919
}

lib/plugins/filter/before_post_render/backtick_code_block.ts

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,83 @@
1+
import type { HighlightOptions } from '../../../extend/syntax_highlight';
12
import type Hexo from '../../../hexo';
23
import type { RenderData } from '../../../types';
34

45
const rBacktick = /^((?:(?:[^\S\r\n]*>){0,3}|[-*+]|[0-9]+\.)[^\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[^\S\r\n]?(\n+|$)/gm;
56
const rAllOptions = /([^\s]+)\s+(.+?)\s+(https?:\/\/\S+|\/\S+)\s*(.+)?/;
67
const rLangCaption = /([^\s]+)\s*(.+)?/;
8+
const rAdditionalOptions = /\s((?:line_number|line_threshold|first_line|wrap|mark|language_attr|highlight):\S+)/g;
79

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

10-
interface Options {
11-
lang: string,
12-
caption: string,
13-
lines_length: number,
14-
firstLineNumber?: string | number
12+
function parseArgs(args: string) {
13+
const matches = [];
14+
15+
let match: RegExpExecArray | null, language_attr: boolean,
16+
line_number: boolean, line_threshold: number, wrap: boolean;
17+
let enableHighlight = true;
18+
while ((match = rAdditionalOptions.exec(args)) !== null) {
19+
matches.push(match[1]);
20+
}
21+
22+
const len = matches.length;
23+
const mark: number[] = [];
24+
let firstLine = 1;
25+
for (let i = 0; i < len; i++) {
26+
const [key, value] = matches[i].split(':');
27+
28+
switch (key) {
29+
case 'highlight':
30+
enableHighlight = value === 'true';
31+
break;
32+
case 'line_number':
33+
line_number = value === 'true';
34+
break;
35+
case 'line_threshold':
36+
if (!isNaN(Number(value))) line_threshold = +value;
37+
break;
38+
case 'first_line':
39+
if (!isNaN(Number(value))) firstLine = +value;
40+
break;
41+
case 'wrap':
42+
wrap = value === 'true';
43+
break;
44+
case 'mark': {
45+
for (const cur of value.split(',')) {
46+
const hyphen = cur.indexOf('-');
47+
if (hyphen !== -1) {
48+
let a = +cur.slice(0, hyphen);
49+
let b = +cur.slice(hyphen + 1);
50+
if (Number.isNaN(a) || Number.isNaN(b)) continue;
51+
if (b < a) { // switch a & b
52+
[a, b] = [b, a];
53+
}
54+
55+
for (; a <= b; a++) {
56+
mark.push(a);
57+
}
58+
}
59+
if (!isNaN(Number(cur))) mark.push(+cur);
60+
}
61+
break;
62+
}
63+
case 'language_attr': {
64+
language_attr = value === 'true';
65+
break;
66+
}
67+
}
68+
}
69+
return {
70+
options: {
71+
language_attr,
72+
firstLine,
73+
line_number,
74+
line_threshold,
75+
mark,
76+
wrap
77+
},
78+
enableHighlight,
79+
_args: args.replace(rAdditionalOptions, '')
80+
};
1581
}
1682

1783
export = (ctx: Hexo): (data: RenderData) => void => {
@@ -26,6 +92,10 @@ export = (ctx: Hexo): (data: RenderData) => void => {
2692
// neither highlight or prismjs is enabled, return escaped content directly.
2793
if (!ctx.extend.highlight.query(ctx.config.syntax_highlighter)) return escapeSwigTag($0);
2894

95+
const parsedArgs = parseArgs(_args);
96+
if (!parsedArgs.enableHighlight) return escapeSwigTag($0);
97+
_args = parsedArgs._args;
98+
2999
// Extract language and caption of code blocks
30100
const args = _args.split('=').shift();
31101
let lang: string, caption: string;
@@ -54,10 +124,11 @@ export = (ctx: Hexo): (data: RenderData) => void => {
54124
content = content.replace(regexp, '');
55125
}
56126

57-
const options: Options = {
127+
const options: HighlightOptions = {
58128
lang,
59129
caption,
60-
lines_length: content.split('\n').length
130+
lines_length: content.split('\n').length,
131+
...parsedArgs.options
61132
};
62133
// setup line number by inline
63134
_args = _args.replace('=+', '=');

lib/plugins/highlight/highlight.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
1+
import type { HighlightOptions } from '../../extend/syntax_highlight';
12
import type Hexo from '../../hexo';
23

3-
interface Options {
4-
line_threshold?: number;
5-
line_number?: boolean;
6-
lines_length?: number;
7-
language_attr?: boolean;
8-
caption?: string;
9-
firstLine?: number;
10-
lang?: string;
11-
mark?: number[];
12-
firstLineNumber?: number;
13-
}
14-
154
// Lazy require highlight.js
16-
let highlight;
5+
let highlight: typeof import('hexo-util').highlight;
176

18-
module.exports = function highlightFilter(this: Hexo, code: string, options: Options) {
7+
module.exports = function highlightFilter(this: Hexo, code: string, options: HighlightOptions) {
198
const hljsCfg = this.config.highlight || {} as any;
209
const line_threshold = options.line_threshold || hljsCfg.line_threshold || 0;
2110
const shouldUseLineNumbers = typeof options.line_number === 'undefined' ? hljsCfg.line_number : options.line_number;
@@ -26,19 +15,19 @@ module.exports = function highlightFilter(this: Hexo, code: string, options: Opt
2615
const hljsOptions = {
2716
autoDetect: hljsCfg.auto_detect,
2817
caption: options.caption,
29-
firstLine: options.firstLine,
18+
firstLine: options.firstLine as number,
3019
gutter,
3120
hljs: hljsCfg.hljs,
3221
lang: options.lang,
3322
languageAttr,
34-
mark: options.mark,
23+
mark: options.mark as number[],
3524
tab: hljsCfg.tab_replace,
3625
wrap: hljsCfg.wrap,
3726
stripIndent: hljsCfg.strip_indent
3827
};
3928
if (hljsCfg.first_line_number === 'inline') {
4029
if (typeof options.firstLineNumber !== 'undefined') {
41-
hljsOptions.firstLine = options.firstLineNumber;
30+
hljsOptions.firstLine = options.firstLineNumber as number;
4231
} else {
4332
hljsOptions.gutter = false;
4433
}

lib/plugins/highlight/prism.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1+
import type { HighlightOptions } from '../../extend/syntax_highlight';
2+
import type Hexo from '../../hexo';
3+
14
// Lazy require prismjs
2-
let prismHighlight;
5+
let prismHighlight: typeof import('hexo-util').prismHighlight;
36

4-
module.exports = function(code, options) {
5-
const prismjsCfg = this.config.prismjs || {};
7+
module.exports = function(this: Hexo, code: string, options: HighlightOptions) {
8+
const prismjsCfg = this.config.prismjs || {} as any;
69
const line_threshold = options.line_threshold || prismjsCfg.line_threshold || 0;
710
const shouldUseLineNumbers = typeof options.line_number === 'undefined' ? prismjsCfg.line_number : options.line_number;
811
const surpassesLineThreshold = options.lines_length > line_threshold;
912
const lineNumber = shouldUseLineNumbers && surpassesLineThreshold;
1013

1114
const prismjsOptions = {
1215
caption: options.caption,
13-
firstLine: options.firstLine,
16+
firstLine: options.firstLine as number,
1417
isPreprocess: prismjsCfg.preprocess,
1518
lang: options.lang,
1619
lineNumber,
17-
mark: options.mark,
20+
mark: Array.isArray(options.mark) ? String(options.mark) : options.mark,
1821
tab: prismjsCfg.tab_replace,
1922
stripIndent: prismjsCfg.strip_indent
2023
};

lib/plugins/tag/code.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,7 @@ function parseArgs(args: string[]): HighlightOptions {
6969
let b = +cur.slice(hyphen + 1);
7070
if (Number.isNaN(a) || Number.isNaN(b)) continue;
7171
if (b < a) { // switch a & b
72-
const temp = a;
73-
a = b;
74-
b = temp;
72+
[a, b] = [b, a];
7573
}
7674

7775
for (; a <= b; a++) {

0 commit comments

Comments
 (0)