Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Diff: Added support for syntax highlighting inside diffs #1889

Merged
merged 14 commits into from Jul 21, 2019
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"
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
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;
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
}

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.
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
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;
}