Skip to content

Commit

Permalink
feat(gatsby-remark-prismjs): syntax highlighting inside diffs (#26525)
Browse files Browse the repository at this point in the history
* feat(gatsby-remark-prismjs): syntax highlighting inside diffs

added support for syntax highlighting inside diffs using diff-xxxx
pattern as language identifier

✅ Closes: #20630

* add minimal documentation about `diff-[language]`

* \w means [a-zA-Z0-9_] so we can drop \d ([0-9]) as it's already covered

* add top level test to also cover regex part

Co-authored-by: Michal Piechowiak <misiek.piechowiak@gmail.com>
  • Loading branch information
kenchandev and pieh committed Sep 11, 2020
1 parent f1f312d commit 60fdd22
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 5 deletions.
8 changes: 8 additions & 0 deletions packages/gatsby-remark-prismjs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,14 @@ unless explicitly overridden by the `promptUser` and `promptHost` options in the
```shell{promptUser: alice}{promptHost: dev.localhost}
````

### Diff code blocks

You can specify language for `diff` code blocks by using `diff-[language]` to enable syntax highlighting in diffs:

````
```diff-javascript
````

### Line hiding

As well as highlighting lines, it's possible to _hide_ lines from the rendered output. Often this is handy when using `gatsby-remark-prismjs` along with [`gatsby-remark-embed-snippet`](https://www.gatsbyjs.org/packages/gatsby-remark-embed-snippet/).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ exports[`highlight code and lines with PrismJS for language cpp 1`] = `
"
`;
exports[`highlight code and lines with PrismJS for language diff-js 1`] = `
"
<span class=\\"token deleted-sign deleted language-js\\"><span class=\\"token prefix deleted\\">-</span> <span class=\\"token keyword\\">let</span> foo <span class=\\"token operator\\">=</span> bar<span class=\\"token punctuation\\">.</span><span class=\\"token function\\">baz</span><span class=\\"token punctuation\\">(</span><span class=\\"token punctuation\\">[</span><span class=\\"token number\\">1</span><span class=\\"token punctuation\\">,</span> <span class=\\"token number\\">2</span><span class=\\"token punctuation\\">,</span> <span class=\\"token number\\">3</span><span class=\\"token punctuation\\">]</span><span class=\\"token punctuation\\">)</span><span class=\\"token punctuation\\">;</span>
<span class=\\"token prefix deleted\\">-</span> foo <span class=\\"token operator\\">=</span> foo <span class=\\"token operator\\">+</span> <span class=\\"token number\\">1</span><span class=\\"token punctuation\\">;</span>
</span><span class=\\"token inserted-sign inserted language-js\\"><span class=\\"token prefix inserted\\">+</span> <span class=\\"token keyword\\">const</span> foo <span class=\\"token operator\\">=</span> bar<span class=\\"token punctuation\\">.</span><span class=\\"token function\\">baz</span><span class=\\"token punctuation\\">(</span><span class=\\"token punctuation\\">[</span><span class=\\"token number\\">1</span><span class=\\"token punctuation\\">,</span> <span class=\\"token number\\">2</span><span class=\\"token punctuation\\">,</span> <span class=\\"token number\\">3</span><span class=\\"token punctuation\\">]</span><span class=\\"token punctuation\\">)</span> <span class=\\"token operator\\">+</span> <span class=\\"token number\\">1</span><span class=\\"token punctuation\\">;</span>
</span><span class=\\"token unchanged language-js\\"><span class=\\"token prefix unchanged\\"> </span> console<span class=\\"token punctuation\\">.</span><span class=\\"token function\\">log</span><span class=\\"token punctuation\\">(</span>foo<span class=\\"token punctuation\\">)</span><span class=\\"token punctuation\\">;</span>
</span>"
`;
exports[`highlight code and lines with PrismJS for language jsx 1`] = `
"
<span class=\\"token keyword\\">import</span> React <span class=\\"token keyword\\">from</span> <span class=\\"token string\\">\\"react\\"</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,52 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`remark prism plugin diff supports language specifier 1`] = `
Object {
"children": Array [
Object {
"lang": "diff-javascript",
"position": Position {
"end": Object {
"column": 4,
"line": 6,
"offset": 141,
},
"indent": Array [
1,
1,
1,
1,
1,
],
"start": Object {
"column": 1,
"line": 1,
"offset": 0,
},
},
"type": "html",
"value": "<div class=\\"gatsby-highlight\\" data-language=\\"diff\\"><pre class=\\"language-diff-javascript\\"><code class=\\"language-diff-javascript\\"><span class=\\"token deleted-sign deleted language-javascript\\"><span class=\\"token prefix deleted\\">-</span> <span class=\\"token keyword\\">let</span> foo <span class=\\"token operator\\">=</span> bar<span class=\\"token punctuation\\">.</span><span class=\\"token function\\">baz</span><span class=\\"token punctuation\\">(</span><span class=\\"token punctuation\\">[</span><span class=\\"token number\\">1</span><span class=\\"token punctuation\\">,</span> <span class=\\"token number\\">2</span><span class=\\"token punctuation\\">,</span> <span class=\\"token number\\">3</span><span class=\\"token punctuation\\">]</span><span class=\\"token punctuation\\">)</span><span class=\\"token punctuation\\">;</span>
<span class=\\"token prefix deleted\\">-</span> foo <span class=\\"token operator\\">=</span> foo <span class=\\"token operator\\">+</span> <span class=\\"token number\\">1</span><span class=\\"token punctuation\\">;</span>
</span><span class=\\"token inserted-sign inserted language-javascript\\"><span class=\\"token prefix inserted\\">+</span> <span class=\\"token keyword\\">const</span> foo <span class=\\"token operator\\">=</span> bar<span class=\\"token punctuation\\">.</span><span class=\\"token function\\">baz</span><span class=\\"token punctuation\\">(</span><span class=\\"token punctuation\\">[</span><span class=\\"token number\\">1</span><span class=\\"token punctuation\\">,</span> <span class=\\"token number\\">2</span><span class=\\"token punctuation\\">,</span> <span class=\\"token number\\">3</span><span class=\\"token punctuation\\">]</span><span class=\\"token punctuation\\">)</span> <span class=\\"token operator\\">+</span> <span class=\\"token number\\">1</span><span class=\\"token punctuation\\">;</span>
</span><span class=\\"token unchanged language-javascript\\"><span class=\\"token prefix unchanged\\"> </span> console<span class=\\"token punctuation\\">.</span><span class=\\"token function\\">log</span><span class=\\"token punctuation\\">(</span>foo<span class=\\"token punctuation\\">)</span><span class=\\"token punctuation\\">;</span></span></code></pre></div>",
},
],
"position": Object {
"end": Object {
"column": 4,
"line": 6,
"offset": 141,
},
"start": Object {
"column": 1,
"line": 1,
"offset": 0,
},
},
"type": "root",
}
`;
exports[`remark prism plugin does not handle inline code if noInlineHighlight: true 1`] = `
Object {
"children": Array [
Expand Down
24 changes: 24 additions & 0 deletions packages/gatsby-remark-prismjs/src/__tests__/highlight-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,30 @@ export default Counter
expect(/<span[^>]*>[^<]*\n[^<]*<\/span>/g.exec(processed)).not.toBeTruthy()
})

it(`for language diff-js`, () => {
const highlightCode = require(`../highlight-code`)
const language = `diff`
const diffLanguage = `js`
const lineNumbersHighlight = []
const code = `
- let foo = bar.baz([1, 2, 3]);
- foo = foo + 1;
+ const foo = bar.baz([1, 2, 3]) + 1;
console.log(foo);
`

expect(
highlightCode(
language,
code,
{},
lineNumbersHighlight,
false,
diffLanguage
)
).toMatchSnapshot()
})

describe(`with language-text`, () => {
it(`escapes &, <, " elements and warns`, () => {
spyOn(console, `warn`)
Expand Down
14 changes: 14 additions & 0 deletions packages/gatsby-remark-prismjs/src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ describe(`remark prism plugin`, () => {
})
})

describe(`diff`, () => {
test(`supports language specifier`, () => {
const code = `\`\`\`diff-javascript
- let foo = bar.baz([1, 2, 3]);
- foo = foo + 1;
+ const foo = bar.baz([1, 2, 3]) + 1;
console.log(foo);
\`\`\``
const markdownAST = remark.parse(code)
plugin({ markdownAST })
expect(markdownAST).toMatchSnapshot()
})
})

describe(`numberLines`, () => {
it(`adds line-number markup when necessary`, () => {
const code = `\`\`\`js{numberLines:5}\n//.foo { \ncolor: red;\n }\``
Expand Down
12 changes: 10 additions & 2 deletions packages/gatsby-remark-prismjs/src/highlight-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ const handleDirectives = require(`./directives`)
const escapeHTML = require(`./escape-html`)
const unsupportedLanguages = new Set()

require(`prismjs/components/prism-diff`)
require(`prismjs/plugins/diff-highlight/prism-diff-highlight`)

module.exports = (
language,
code,
additionalEscapeCharacters = {},
lineNumbersHighlight = [],
noInlineHighlight = false
noInlineHighlight = false,
diffLanguage = null
) => {
// (Try to) load languages on demand.
if (!Prism.languages[language]) {
Expand Down Expand Up @@ -40,7 +44,11 @@ module.exports = (
}

const grammar = Prism.languages[language]
const highlighted = Prism.highlight(code, grammar, language)
const highlighted = Prism.highlight(
code,
grammar,
diffLanguage ? `${language}-${diffLanguage}` : language
)
const codeSplits = handleDirectives(highlighted, lineNumbersHighlight)

let finalCode = ``
Expand Down
21 changes: 18 additions & 3 deletions packages/gatsby-remark-prismjs/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const addLineNumbers = require(`./add-line-numbers`)
const commandLine = require(`./command-line`)
const loadPrismShowInvisibles = require(`./plugins/prism-show-invisibles`)

const DIFF_HIGHLIGHT_SYNTAX = /^(diff)-([\w-]+)/i

module.exports = (
{ markdownAST },
{
Expand Down Expand Up @@ -34,6 +36,7 @@ module.exports = (

visit(markdownAST, `code`, node => {
let language = node.meta ? node.lang + node.meta : node.lang
let diffLanguage
let {
splitLanguage,
highlightLines,
Expand All @@ -46,7 +49,16 @@ module.exports = (
const showLineNumbers = showLineNumbersLocal || showLineNumbersGlobal
const promptUser = promptUserLocal || prompt.user
const promptHost = promptHostLocal || prompt.host
language = splitLanguage
const match = splitLanguage
? splitLanguage.match(DIFF_HIGHLIGHT_SYNTAX)
: null

if (match) {
language = match[1]
diffLanguage = normalizeLanguage(match[2])
} else {
language = splitLanguage
}

// PrismJS's theme styles are targeting pre[class*="language-"]
// to apply its styles. We do the same here so that users
Expand All @@ -65,7 +77,9 @@ module.exports = (
// re-process our already-highlighted markup.
//
// @see https://github.com/gatsbyjs/gatsby/issues/1486
const className = `${classPrefix}${languageName}`
const className = `${classPrefix}${languageName}${
diffLanguage ? `-${diffLanguage}` : ``
}`

// Replace the node with the markup we need to make
// 100% width highlighted code lines work
Expand All @@ -80,7 +94,8 @@ module.exports = (
node.value,
escapeEntities,
highlightLines,
noInlineHighlight
noInlineHighlight,
diffLanguage
)

let numLinesStyle, numLinesClass, numLinesNumber
Expand Down

0 comments on commit 60fdd22

Please sign in to comment.