Closed rrousselGit closed 3 years ago
We use react-prism-renderer and it does not seem to support the ability to add such plugin, so unfortunately we can't support this unless they add support, but that seems not planned: https://github.com/FormidableLabs/prism-react-renderer/issues/2
I found a way to implement diff-highlight using magic comments.
The problem is the line number is wrong, but we can hide it.
markdown
```tsx
// diff-remove
<a href={`/${lng}/some-path`} />
// diff-remove
location.href = `${lng}/some-path`
// diff-add
<a href={'/some-path'} />
// diff-add
location.href = `/some-path`
### Implement
For `docusaurus.config.js`, set
```js
const config = {
prism: {
magicComments: [
{
className: 'code-block-diff-add-line',
line: 'diff-add'
},
{
className: 'code-block-diff-remove-line',
line: 'diff-remove'
}
]
}
}
And add custom css
.code-block-diff-add-line {
background-color: #ccffd8;
display: block;
margin: 0 -40px;
padding: 0 40px;
}
.code-block-diff-add-line::before {
position: absolute;
left: 8px;
padding-right: 8px;
content: '+';
}
.code-block-diff-remove-line {
background-color: #ffebe9;
display: block;
margin: 0 -40px;
padding: 0 40px;
}
.code-block-diff-remove-line::before {
position: absolute;
left: 8px;
padding-right: 8px;
content: '-';
}
/**
* use magic comments to mark diff blocks
*/
pre code:has(.code-block-diff-add-line) {
padding-left: 40px!important;
}
pre code:has(.code-block-diff-remove-line) {
padding-left: 40px!important;
}
Implemented diff-highlight compatible with docusaurus (prism-react-renderer)
You need to swizzle in your docusaurus repo:
yarn swizzle @docusaurus/theme-classic prism-include-languages
docusaurus.config.ts
(Example for using diff-ts) prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
additionalLanguages: ["diff", "diff-ts"],
},
prism-include-languages.ts
import siteConfig from "@generated/docusaurus.config";
import type * as PrismNamespace from "prismjs";
import { diffHighlight } from "./prism-diff-highlight";
import "./prism-diff-highlight.css";
const DIFF_LANGUAGE_REGEX = /^diff-([\w-]+)/i;
export default function prismIncludeLanguages(
PrismObject: typeof PrismNamespace
): void {
const {
themeConfig: { prism },
} = siteConfig;
const { additionalLanguages } = prism as { additionalLanguages: string[] };
// Prism components work on the Prism instance on the window, while prism-
// react-renderer uses its own Prism instance. We temporarily mount the
// instance onto window, import components to enhance it, then remove it to
// avoid polluting global namespace.
// You can mutate PrismObject: registering plugins, deleting languages... As
// long as you don't re-assign it
globalThis.Prism = PrismObject;
additionalLanguages.forEach((lang) => {
const langMatch = DIFF_LANGUAGE_REGEX.exec(lang);
if (!langMatch) {
require(`prismjs/components/prism-${lang}`); // not a language specific diff
} else {
if (!PrismObject.languages.diff) {
console.error(
"prism-include-languages:",
"You need to import 'diff' language first to use 'diff-xxxx' languages"
);
}
PrismObject.languages[lang] = PrismObject.languages.diff;
}
});
diffHighlight(PrismObject);
delete globalThis.Prism;
}
prism-diff-highlight.ts
import { EnvConfig, PrismLib } from "prism-react-renderer";
import { TokenStream, Token } from "prismjs";
const LANGUAGE_REGEX = /^diff-([\w-]+)/i;
const tokenStreamToString = (tokenStream: TokenStream): string => {
const result: string[] = [];
const stack: TokenStream[] = [tokenStream];
while (stack.length > 0) {
const item = stack.pop();
if (typeof item === "string") {
result.push(item);
} else if (Array.isArray(item)) {
for (let i = item.length - 1; i >= 0; i--) {
stack.push(item[i]);
}
} else {
// If it's a Token, convert it to a string and push it
stack.push(item.content);
}
}
return result.join("");
};
export function diffHighlight(Prism: PrismLib) {
Prism.hooks.add("after-tokenize", function (env: EnvConfig) {
let diffLanguage;
let diffGrammar;
const language = env.language;
if (language !== "diff") {
const langMatch = LANGUAGE_REGEX.exec(language);
if (!langMatch) {
return; // not a language specific diff
}
diffLanguage = langMatch[1];
diffGrammar = Prism.languages[diffLanguage];
if (!diffGrammar) {
console.error(
"prism-diff-highlight:",
`You need to add language '${diffLanguage}' to use '${language}'`
);
return;
}
} else return;
const newTokens = [];
env.tokens.forEach((token) => {
if (typeof token === "string") {
newTokens.push(...Prism.tokenize(token, diffGrammar));
} else if (token.type === "unchanged") {
newTokens.push(
...Prism.tokenize(tokenStreamToString(token), diffGrammar)
);
} else if (["deleted-sign", "inserted-sign"].includes(token.type)) {
token.alias = [
token.type === "deleted-sign"
? "diff-highlight-deleted"
: "diff-highlight-inserted",
];
// diff parser always return "deleted" and "inserted" lines with content of type array
if (token.content.length > 1) {
const newTokenContent: Array<string | Token> = [];
// preserve prefixes and don't parse them again
// subTokens from diff parser are of type Token
(token.content as Array<string | Token>).forEach(
(subToken: Token) => {
if (subToken.type === "prefix") {
newTokenContent.push(subToken);
} else {
newTokenContent.push(
...Prism.tokenize(tokenStreamToString(subToken), diffGrammar)
);
}
}
);
token.content = newTokenContent;
}
newTokens.push(token);
} else if (token.type === "coord") {
newTokens.push(token);
}
});
console.log(newTokens);
env.tokens = newTokens;
});
}
prism-diff-highlight.css
code .token.diff-highlight-deleted {
background-color: rgba(255, 0, 0, .1);
}
code .token.diff-highlight-inserted { background-color: rgba(0, 255, 128, .1); }
code .token.coord { font-weight: 700; }
- Result:
![image](https://github.com/facebook/docusaurus/assets/10998637/a7d3351e-738a-4047-8bfc-d73c52e26f18)
You can add whatever other language you want in `additionalLanguages`:
```ts
additionalLanguages: ["diff", "diff-ts", "powershell", "diff-powershell"],
NOTE FOR DEVS/INTERESTED PEOPLE:
Prism plugins do not work with prism-react-renderer because they rely in other hooks that are not called. They only call hooks "before-tokenize"
and "after-tokenize"
. This reimplementation uses "after-tokenize"
to edit tokens generated by prism-react-renderer tokenizer.
In theory every plugin could be re-implemented using those hooks to work with prism-react-renderer
EDIT: Code Update using typescript
EDIT2: Fix edge cases
EDIT3: Add support for "coord" token
EDIT4: Don't use recursion in tokenStreamToString
I think you can write you own component to display diff
like github.
$ npm install --save react-diff-view
I am using version 3.2.1. (https://www.npmjs.com/package/react-diff-view)
Create your component
src/components/CustomDiff/CustomDiff.js
import {Decoration, Diff, Hunk, parseDiff} from "react-diff-view";
import 'react-diff-view/style/index.css';
import './CustomDiff.css';
export default function CustomDiff({diffText}) {
const files = parseDiff(diffText);
return (
<div>
{files.map((
{hunks, newPath, oldPath}, i) =>
<Diff
renderToken={(token) => token.content}
key={i}
viewType="split"
diffType=""
hunks={hunks}
>
{
hunks => hunks.map(hunk => {
return <>
<Decoration key={'decoration-' + hunk.content}>
{`${oldPath}`}
</Decoration>
<Decoration key={'decoration-' + hunk.content}>
{`${newPath} - ${hunk.content}`}
</Decoration>
<Hunk key={hunk.content} hunk={hunk} />
</>
}
)}
</Diff>
)}
</div>
);
}
Css for your component.
src/components/CustomDiff/CustomDiff.css
:root {
--ifm-table-border-width: 0px;
}
table {
display: table;
}
.diff-code {
font-size: 85%;
}
How to use it in markdown?
import CustomDiff from "@site/src/components/CustomDiff/CustomDiff";
<CustomDiff diffText={`
diff --git a/package.json b/package.json
--- a/package.json (revision c38f44524532d3a87986bfa4b9b8b82d8f0e29a0)
+++ b/package.json (revision 93e71e98e457dae88403cdf204a8f4a85f60cc42)
@@ -1,6 +1,6 @@
{
"name": "SSO-Admin-UI",
- "version": "1.0.0",
+ "version": "2.0.0-alpha.0",
"license": "MIT",
"scripts": {
"start": "ng serve -o -c=dev --port=8500 --baseHref=/",
`}
/>
📚 Documentation
Prism comes with the ability to use prism plugins to add extra functionalities – such as diff-highlight to combine the syntax highlighting of a language with the
diff
feature.It is currently unclear how to enable prism plugins.
I've tried playing with
swizzle
to extract the code-highlighting renderer, but failed to implement this feature.Cross-reference:
Have you read the Contributing Guidelines on issues?
Yes