Skip to content

Commit e7702ae

Browse files
Diff: Added support for syntax highlighting inside diff blocks (#1889)
This adds support for syntax highlighting inside diff blocks via a new plugin called: Diff Highlight.
1 parent c884428 commit e7702ae

File tree

11 files changed

+265
-29
lines changed

11 files changed

+265
-29
lines changed

components.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,11 @@
10781078
"owner": "mAAdhaTTah",
10791079
"require": "toolbar",
10801080
"noCSS": true
1081+
},
1082+
"diff-highlight": {
1083+
"title": "Diff Highlight",
1084+
"owner": "RunDevelopment",
1085+
"require": "diff"
10811086
}
10821087
}
10831088
}

components/prism-diff.js

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,54 @@
1-
Prism.languages.diff = {
2-
'coord': [
3-
// Match all kinds of coord lines (prefixed by "+++", "---" or "***").
4-
/^(?:\*{3}|-{3}|\+{3}).*$/m,
5-
// Match "@@ ... @@" coord lines in unified diff.
6-
/^@@.*@@$/m,
7-
// Match coord lines in normal diff (starts with a number).
8-
/^\d+.*$/m
9-
],
10-
11-
// Match inserted and deleted lines. Support both +/- and >/< styles.
12-
'deleted': /^[-<].*$/m,
13-
'inserted': /^[+>].*$/m,
14-
15-
// Match "different" lines (prefixed with "!") in context diff.
16-
'diff': {
17-
'pattern': /^!(?!!).+$/m,
18-
'alias': 'important'
19-
}
20-
};
1+
(function (Prism) {
2+
3+
Prism.languages.diff = {
4+
'coord': [
5+
// Match all kinds of coord lines (prefixed by "+++", "---" or "***").
6+
/^(?:\*{3}|-{3}|\+{3}).*$/m,
7+
// Match "@@ ... @@" coord lines in unified diff.
8+
/^@@.*@@$/m,
9+
// Match coord lines in normal diff (starts with a number).
10+
/^\d+.*$/m
11+
]
12+
13+
// deleted, inserted, unchanged, diff
14+
};
15+
16+
/**
17+
* A map from the name of a block to its line prefix.
18+
*
19+
* @type {Object<string, string>}
20+
*/
21+
var PREFIXES = {
22+
'deleted-sign': '-',
23+
'deleted-arrow': '<',
24+
'inserted-sign': '+',
25+
'inserted-arrow': '>',
26+
'unchanged': ' ',
27+
'diff': '!',
28+
};
29+
30+
// add a token for each prefix
31+
Object.keys(PREFIXES).forEach(function (name) {
32+
var prefix = PREFIXES[name];
33+
34+
var alias = [];
35+
if (!/^\w+$/.test(name)) { // "deleted-sign" -> "deleted"
36+
alias.push(/\w+/.exec(name)[0]);
37+
}
38+
if (name === "diff") {
39+
alias.push("bold");
40+
}
41+
42+
Prism.languages.diff[name] = {
43+
// pattern: /^(?:[_].*(?:\r\n?|\n|(?![\s\S])))+/m
44+
pattern: RegExp('^(?:[' + prefix + '].*(?:\r\n?|\n|(?![\\s\\S])))+', 'm'),
45+
alias: alias
46+
};
47+
});
48+
49+
// make prefixes available to Diff plugin
50+
Object.defineProperty(Prism.languages.diff, 'PREFIXES', {
51+
value: PREFIXES
52+
});
53+
54+
}(Prism));

components/prism-diff.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/prism-diff.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@ <h2>Unified Diff</h2>
3030
headers: "src/*.h"
3131
- qt: core
3232
+ qt: core gui
33-
public_headers: "src/*.h"</code></pre>
33+
public_headers: "src/*.h"</code></pre>

plugins/diff-highlight/index.html

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<link rel="icon" href="favicon.png" />
7+
<title>Data-URI Highlight ▲ Prism plugins</title>
8+
<base href="../.." />
9+
<link rel="stylesheet" href="style.css" />
10+
<link rel="stylesheet" href="themes/prism.css" data-noprefix />
11+
<link rel="stylesheet" href="plugins/diff-highlight/prism-diff-highlight.css" data-noprefix />
12+
<script src="scripts/prefixfree.min.js"></script>
13+
14+
<script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
15+
<script src="https://www.google-analytics.com/ga.js" async></script>
16+
</head>
17+
18+
<body class="language-none">
19+
20+
<header>
21+
<div class="intro" data-src="templates/header-plugins.html" data-type="text/html"></div>
22+
23+
<h2>Diff Highlight</h2>
24+
<p>Highlights the code inside diff blocks.</p>
25+
</header>
26+
27+
<section>
28+
<h1>How to use</h1>
29+
30+
<p>Replace the <code>language-diff</code> of your code block with a <code>language-diff-xxxx</code> class to enable syntax highlighting for diff blocks.</p>
31+
32+
<p>Optional:<br>
33+
You can add the <code>diff-highlight</code> class to your code block to indicate changes using the background color of a line rather than the color of the text.</p>
34+
</section>
35+
36+
<section>
37+
<h1>Example</h1>
38+
39+
<p>Using <code>class="language-diff"</code>:</p>
40+
41+
<pre><code class="language-diff">@@ -4,6 +4,5 @@
42+
- let foo = bar.baz([1, 2, 3]);
43+
- foo = foo + 1;
44+
+ const foo = bar.baz([1, 2, 3]) + 1;
45+
console.log(`foo: ${foo}`);</code></pre>
46+
47+
<p>Using <code>class="language-diff diff-highlight"</code>:</p>
48+
49+
<pre><code class="language-diff diff-highlight">@@ -4,6 +4,5 @@
50+
- let foo = bar.baz([1, 2, 3]);
51+
- foo = foo + 1;
52+
+ const foo = bar.baz([1, 2, 3]) + 1;
53+
console.log(`foo: ${foo}`);</code></pre>
54+
55+
<p>Using <code>class="language-diff-javascript"</code>:</p>
56+
57+
<pre><code class="language-diff-javascript">@@ -4,6 +4,5 @@
58+
- let foo = bar.baz([1, 2, 3]);
59+
- foo = foo + 1;
60+
+ const foo = bar.baz([1, 2, 3]) + 1;
61+
console.log(`foo: ${foo}`);</code></pre>
62+
63+
<p>Using <code>class="language-diff-javascript diff-highlight"</code>:</p>
64+
65+
<pre><code class="language-diff-javascript diff-highlight">@@ -4,6 +4,5 @@
66+
- let foo = bar.baz([1, 2, 3]);
67+
- foo = foo + 1;
68+
+ const foo = bar.baz([1, 2, 3]) + 1;
69+
console.log(`foo: ${foo}`);</code></pre>
70+
71+
</section>
72+
73+
<footer data-src="templates/footer.html" data-type="text/html"></footer>
74+
75+
<script src="prism.js"></script>
76+
<script src="components/prism-diff.js"></script>
77+
<script src="plugins/diff-highlight/prism-diff-highlight.js"></script>
78+
<script src="scripts/utopia.js"></script>
79+
<script src="components.js"></script>
80+
<script src="scripts/code.js"></script>
81+
82+
</body>
83+
84+
</html>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pre.diff-highlight > code .token.deleted:not(.prefix),
2+
pre > code.diff-highlight .token.deleted:not(.prefix) {
3+
background-color: rgba(255, 0, 0, .1);
4+
color: inherit;
5+
display: block;
6+
}
7+
8+
pre.diff-highlight > code .token.inserted:not(.prefix),
9+
pre > code.diff-highlight .token.inserted:not(.prefix) {
10+
background-color: rgba(0, 255, 128, .1);
11+
color: inherit;
12+
display: block;
13+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
(function () {
2+
3+
if (typeof Prism === 'undefined' || !Prism.languages['diff']) {
4+
return;
5+
}
6+
7+
8+
var LANGUAGE_REGEX = /diff-([\w-]+)/i;
9+
var HTML_TAG = /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/gi;
10+
//this will match a line plus the line break while ignoring the line breaks HTML tags may contain.
11+
var HTML_LINE = RegExp(/(?:__|[^\r\n<])*(?:\r\n?|\n|(?:__|[^\r\n<])(?![^\r\n]))/.source.replace(/__/g, HTML_TAG.source), 'gi');
12+
13+
var PREFIXES = Prism.languages.diff.PREFIXES;
14+
15+
16+
Prism.hooks.add('before-sanity-check', function (env) {
17+
var lang = env.language;
18+
if (LANGUAGE_REGEX.test(lang) && !env.grammar) {
19+
env.grammar = Prism.languages[lang] = Prism.languages['diff'];
20+
}
21+
});
22+
Prism.hooks.add('before-tokenize', function (env) {
23+
var lang = env.language;
24+
if (LANGUAGE_REGEX.test(lang) && !Prism.languages[lang]) {
25+
Prism.languages[lang] = Prism.languages['diff'];
26+
}
27+
});
28+
29+
Prism.hooks.add('wrap', function (env) {
30+
var diffLanguage, diffGrammar;
31+
32+
if (env.language !== 'diff') {
33+
var langMatch = LANGUAGE_REGEX.exec(env.language);
34+
if (!langMatch) {
35+
return; // not a language specific diff
36+
}
37+
38+
diffLanguage = langMatch[1];
39+
diffGrammar = Prism.languages[diffLanguage];
40+
}
41+
42+
// one of the diff tokens without any nested tokens
43+
if (env.type in PREFIXES) {
44+
/** @type {string} */
45+
var content = env.content.replace(HTML_TAG, ''); // remove all HTML tags
46+
47+
/** @type {string} */
48+
var decoded = content.replace(/&lt;/g, '<').replace(/&amp;/g, '&');
49+
50+
// remove any one-character prefix
51+
var code = decoded.replace(/(^|[\r\n])./g, '$1');
52+
53+
// highlight, if possible
54+
var highlighted;
55+
if (diffGrammar) {
56+
highlighted = Prism.highlight(code, diffGrammar, diffLanguage);
57+
} else {
58+
highlighted = Prism.util.encode(code);
59+
}
60+
61+
// get the HTML source of the prefix token
62+
var prefixToken = new Prism.Token('prefix', PREFIXES[env.type], [/\w+/.exec(env.type)[0]]);
63+
var prefix = Prism.Token.stringify(prefixToken, env.language);
64+
65+
// add prefix
66+
var lines = [], m;
67+
HTML_LINE.lastIndex = 0;
68+
while (m = HTML_LINE.exec(highlighted)) {
69+
lines.push(prefix + m[0]);
70+
}
71+
if (/(?:^|[\r\n]).$/.test(decoded)) {
72+
// because both "+a\n+" and "+a\n" will map to "a\n" after the line prefixes are removed
73+
lines.push(prefix);
74+
}
75+
env.content = lines.join('');
76+
77+
if (diffGrammar) {
78+
env.classes.push('language-' + diffLanguage);
79+
}
80+
}
81+
});
82+
83+
}());

plugins/diff-highlight/prism-diff-highlight.min.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
! qt: core
22

3+
unchanged
4+
35
- qt: core
46
+ qt: core gui
57

@@ -9,13 +11,14 @@
911
----------------------------------------------------
1012

1113
[
12-
["diff", "! qt: core"],
13-
["deleted", "- qt: core"],
14-
["inserted", "+ qt: core gui"],
15-
["deleted", "< qt: core"],
16-
["inserted", "> qt: core quick"]
14+
["diff", "! qt: core\r\n"],
15+
["unchanged", " unchanged\r\n"],
16+
["deleted-sign", "- qt: core\r\n"],
17+
["inserted-sign", "+ qt: core gui\r\n"],
18+
["deleted-arrow", "< qt: core\r\n"],
19+
["inserted-arrow", "> qt: core quick"]
1720
]
1821

1922
----------------------------------------------------
2023

21-
Checks for deleted, inserted and different lines.
24+
Checks for deleted, inserted and different lines.

0 commit comments

Comments
 (0)