Skip to content

Commit

Permalink
Diff: Added support for syntax highlighting inside diff blocks (#1889)
Browse files Browse the repository at this point in the history
This adds support for syntax highlighting inside diff blocks via a new plugin called: Diff Highlight.
  • Loading branch information
RunDevelopment committed Jul 21, 2019
1 parent c884428 commit e7702ae
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 29 deletions.
2 changes: 1 addition & 1 deletion components.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions components.json
Expand Up @@ -1078,6 +1078,11 @@
"owner": "mAAdhaTTah",
"require": "toolbar",
"noCSS": true
},
"diff-highlight": {
"title": "Diff Highlight",
"owner": "RunDevelopment",
"require": "diff"
}
}
}
74 changes: 54 additions & 20 deletions components/prism-diff.js
@@ -1,20 +1,54 @@
Prism.languages.diff = {
'coord': [
// Match all kinds of coord lines (prefixed by "+++", "---" or "***").
/^(?:\*{3}|-{3}|\+{3}).*$/m,
// Match "@@ ... @@" coord lines in unified diff.
/^@@.*@@$/m,
// Match coord lines in normal diff (starts with a number).
/^\d+.*$/m
],

// Match inserted and deleted lines. Support both +/- and >/< styles.
'deleted': /^[-<].*$/m,
'inserted': /^[+>].*$/m,

// Match "different" lines (prefixed with "!") in context diff.
'diff': {
'pattern': /^!(?!!).+$/m,
'alias': 'important'
}
};
(function (Prism) {

Prism.languages.diff = {
'coord': [
// Match all kinds of coord lines (prefixed by "+++", "---" or "***").
/^(?:\*{3}|-{3}|\+{3}).*$/m,
// Match "@@ ... @@" coord lines in unified diff.
/^@@.*@@$/m,
// Match coord lines in normal diff (starts with a number).
/^\d+.*$/m
]

// deleted, inserted, unchanged, diff
};

/**
* A map from the name of a block to its line prefix.
*
* @type {Object<string, string>}
*/
var PREFIXES = {
'deleted-sign': '-',
'deleted-arrow': '<',
'inserted-sign': '+',
'inserted-arrow': '>',
'unchanged': ' ',
'diff': '!',
};

// add a token for each prefix
Object.keys(PREFIXES).forEach(function (name) {
var prefix = PREFIXES[name];

var alias = [];
if (!/^\w+$/.test(name)) { // "deleted-sign" -> "deleted"
alias.push(/\w+/.exec(name)[0]);
}
if (name === "diff") {
alias.push("bold");
}

Prism.languages.diff[name] = {
// pattern: /^(?:[_].*(?:\r\n?|\n|(?![\s\S])))+/m
pattern: RegExp('^(?:[' + prefix + '].*(?:\r\n?|\n|(?![\\s\\S])))+', 'm'),
alias: alias
};
});

// make prefixes available to Diff plugin
Object.defineProperty(Prism.languages.diff, 'PREFIXES', {
value: PREFIXES
});

}(Prism));
2 changes: 1 addition & 1 deletion components/prism-diff.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/prism-diff.html
Expand Up @@ -30,4 +30,4 @@ <h2>Unified Diff</h2>
headers: "src/*.h"
- qt: core
+ qt: core gui
public_headers: "src/*.h"</code></pre>
public_headers: "src/*.h"</code></pre>
84 changes: 84 additions & 0 deletions plugins/diff-highlight/index.html
@@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<link rel="icon" href="favicon.png" />
<title>Data-URI Highlight ▲ Prism plugins</title>
<base href="../.." />
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="themes/prism.css" data-noprefix />
<link rel="stylesheet" href="plugins/diff-highlight/prism-diff-highlight.css" data-noprefix />
<script src="scripts/prefixfree.min.js"></script>

<script>var _gaq = [['_setAccount', 'UA-33746269-1'], ['_trackPageview']];</script>
<script src="https://www.google-analytics.com/ga.js" async></script>
</head>

<body class="language-none">

<header>
<div class="intro" data-src="templates/header-plugins.html" data-type="text/html"></div>

<h2>Diff Highlight</h2>
<p>Highlights the code inside diff blocks.</p>
</header>

<section>
<h1>How to use</h1>

<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>

<p>Optional:<br>
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>
</section>

<section>
<h1>Example</h1>

<p>Using <code>class="language-diff"</code>:</p>

<pre><code class="language-diff">@@ -4,6 +4,5 @@
- let foo = bar.baz([1, 2, 3]);
- foo = foo + 1;
+ const foo = bar.baz([1, 2, 3]) + 1;
console.log(`foo: ${foo}`);</code></pre>

<p>Using <code>class="language-diff diff-highlight"</code>:</p>

<pre><code class="language-diff diff-highlight">@@ -4,6 +4,5 @@
- let foo = bar.baz([1, 2, 3]);
- foo = foo + 1;
+ const foo = bar.baz([1, 2, 3]) + 1;
console.log(`foo: ${foo}`);</code></pre>

<p>Using <code>class="language-diff-javascript"</code>:</p>

<pre><code class="language-diff-javascript">@@ -4,6 +4,5 @@
- let foo = bar.baz([1, 2, 3]);
- foo = foo + 1;
+ const foo = bar.baz([1, 2, 3]) + 1;
console.log(`foo: ${foo}`);</code></pre>

<p>Using <code>class="language-diff-javascript diff-highlight"</code>:</p>

<pre><code class="language-diff-javascript diff-highlight">@@ -4,6 +4,5 @@
- let foo = bar.baz([1, 2, 3]);
- foo = foo + 1;
+ const foo = bar.baz([1, 2, 3]) + 1;
console.log(`foo: ${foo}`);</code></pre>

</section>

<footer data-src="templates/footer.html" data-type="text/html"></footer>

<script src="prism.js"></script>
<script src="components/prism-diff.js"></script>
<script src="plugins/diff-highlight/prism-diff-highlight.js"></script>
<script src="scripts/utopia.js"></script>
<script src="components.js"></script>
<script src="scripts/code.js"></script>

</body>

</html>
13 changes: 13 additions & 0 deletions plugins/diff-highlight/prism-diff-highlight.css
@@ -0,0 +1,13 @@
pre.diff-highlight > code .token.deleted:not(.prefix),
pre > code.diff-highlight .token.deleted:not(.prefix) {
background-color: rgba(255, 0, 0, .1);
color: inherit;
display: block;
}

pre.diff-highlight > code .token.inserted:not(.prefix),
pre > code.diff-highlight .token.inserted:not(.prefix) {
background-color: rgba(0, 255, 128, .1);
color: inherit;
display: block;
}
83 changes: 83 additions & 0 deletions plugins/diff-highlight/prism-diff-highlight.js
@@ -0,0 +1,83 @@
(function () {

if (typeof Prism === 'undefined' || !Prism.languages['diff']) {
return;
}


var LANGUAGE_REGEX = /diff-([\w-]+)/i;
var HTML_TAG = /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/gi;
//this will match a line plus the line break while ignoring the line breaks HTML tags may contain.
var HTML_LINE = RegExp(/(?:__|[^\r\n<])*(?:\r\n?|\n|(?:__|[^\r\n<])(?![^\r\n]))/.source.replace(/__/g, HTML_TAG.source), 'gi');

var PREFIXES = Prism.languages.diff.PREFIXES;


Prism.hooks.add('before-sanity-check', function (env) {
var lang = env.language;
if (LANGUAGE_REGEX.test(lang) && !env.grammar) {
env.grammar = Prism.languages[lang] = Prism.languages['diff'];
}
});
Prism.hooks.add('before-tokenize', function (env) {
var lang = env.language;
if (LANGUAGE_REGEX.test(lang) && !Prism.languages[lang]) {
Prism.languages[lang] = Prism.languages['diff'];
}
});

Prism.hooks.add('wrap', function (env) {
var diffLanguage, diffGrammar;

if (env.language !== 'diff') {
var langMatch = LANGUAGE_REGEX.exec(env.language);
if (!langMatch) {
return; // not a language specific diff
}

diffLanguage = langMatch[1];
diffGrammar = Prism.languages[diffLanguage];
}

// one of the diff tokens without any nested tokens
if (env.type in PREFIXES) {
/** @type {string} */
var content = env.content.replace(HTML_TAG, ''); // remove all HTML tags

/** @type {string} */
var decoded = content.replace(/&lt;/g, '<').replace(/&amp;/g, '&');

// remove any one-character prefix
var code = decoded.replace(/(^|[\r\n])./g, '$1');

// highlight, if possible
var highlighted;
if (diffGrammar) {
highlighted = Prism.highlight(code, diffGrammar, diffLanguage);
} else {
highlighted = Prism.util.encode(code);
}

// get the HTML source of the prefix token
var prefixToken = new Prism.Token('prefix', PREFIXES[env.type], [/\w+/.exec(env.type)[0]]);
var prefix = Prism.Token.stringify(prefixToken, env.language);

// add prefix
var lines = [], m;
HTML_LINE.lastIndex = 0;
while (m = HTML_LINE.exec(highlighted)) {
lines.push(prefix + m[0]);
}
if (/(?:^|[\r\n]).$/.test(decoded)) {
// because both "+a\n+" and "+a\n" will map to "a\n" after the line prefixes are removed
lines.push(prefix);
}
env.content = lines.join('');

if (diffGrammar) {
env.classes.push('language-' + diffLanguage);
}
}
});

}());
1 change: 1 addition & 0 deletions plugins/diff-highlight/prism-diff-highlight.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 9 additions & 6 deletions tests/languages/diff/diff_feature.test
@@ -1,5 +1,7 @@
! qt: core

unchanged

- qt: core
+ qt: core gui

Expand All @@ -9,13 +11,14 @@
----------------------------------------------------

[
["diff", "! qt: core"],
["deleted", "- qt: core"],
["inserted", "+ qt: core gui"],
["deleted", "< qt: core"],
["inserted", "> qt: core quick"]
["diff", "! qt: core\r\n"],
["unchanged", " unchanged\r\n"],
["deleted-sign", "- qt: core\r\n"],
["inserted-sign", "+ qt: core gui\r\n"],
["deleted-arrow", "< qt: core\r\n"],
["inserted-arrow", "> qt: core quick"]
]

----------------------------------------------------

Checks for deleted, inserted and different lines.
Checks for deleted, inserted and different lines.
13 changes: 13 additions & 0 deletions themes/prism-funky.css
Expand Up @@ -115,3 +115,16 @@ code[class*="language-"] {
.token.deleted {
color: red;
}

/* Plugin styles: Diff Highlight */
pre.diff-highlight.diff-highlight > code .token.deleted:not(.prefix),
pre > code.diff-highlight.diff-highlight .token.deleted:not(.prefix) {
background-color: rgba(255, 0, 0, .3);
display: inline;
}

pre.diff-highlight.diff-highlight > code .token.inserted:not(.prefix),
pre > code.diff-highlight.diff-highlight .token.inserted:not(.prefix) {
background-color: rgba(0, 255, 128, .3);
display: inline;
}

0 comments on commit e7702ae

Please sign in to comment.