diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a22632..0270853 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.4.0 (2022-12-17)
+
+### ✨ Features
+
+ * Add show-invisibles plugins
+
+Credits
+
+* [@Bunlong](https://github.com/Bunlong)
+
## 0.3.0 (2022-12-14)
### ✨ Features
diff --git a/README.md b/README.md
index 29187f5..97927c2 100644
--- a/README.md
+++ b/README.md
@@ -788,11 +788,41 @@ export default function App() {
}
```
+### Show Invisibles
+
+Show hidden characters such as tabs and line breaks.
+
+```javascript
+import { usePrism } from 'next-prism'
+
+// Import a theme.css
+import 'next-prism/themes/tomorrow.css'
+
+// Import show-invisibles source
+import 'next-prism/plugins/show-invisibles/show-invisibles';
+// Import show-invisibles.css
+import 'next-prism/plugins/show-invisibles/show-invisibles.css';
+
+export default function App() {
+ const { Code } = usePrism()
+
+ return (
+ <>
+
+ {`
+ {Math.random()}
+
`}
+
+ >
+ )
+}
+```
+
## 📜 Changelog
-Latest version 0.3.0 (2022-12-14):
+Latest version 0.4.0 (2022-12-17):
- * Add highlightAll() function
+ * Add show-invisibles plugins
Details changes for each release are documented in the [CHANGELOG.md](https://github.com/Bunlong/next-prism/blob/master/CHANGELOG.md).
diff --git a/package.json b/package.json
index ec3f31a..45bf73f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "next-prism",
- "version": "0.3.1",
+ "version": "0.4.0",
"description": "A lightweight, robust, and elegant syntax highlighting component for your next React apps.",
"author": "Bunlong ",
"license": "MIT",
diff --git a/plugins/line-numbers/line-numbers.css b/plugins/line-numbers/line-numbers.css
index 8170f64..b67b684 100644
--- a/plugins/line-numbers/line-numbers.css
+++ b/plugins/line-numbers/line-numbers.css
@@ -1 +1,39 @@
-pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}
\ No newline at end of file
+pre[class*="language-"].line-numbers {
+ position: relative;
+ padding-left: 3.8em;
+ counter-reset: linenumber;
+}
+
+pre[class*="language-"].line-numbers > code {
+ position: relative;
+ white-space: inherit;
+}
+
+.line-numbers .line-numbers-rows {
+ position: absolute;
+ pointer-events: none;
+ top: 0;
+ font-size: 100%;
+ left: -3.8em;
+ width: 3em; /* works for line-numbers below 1000 lines */
+ letter-spacing: -1px;
+ border-right: 1px solid #999;
+
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.line-numbers-rows > span {
+ display: block;
+ counter-increment: linenumber;
+}
+
+.line-numbers-rows > span:before {
+ content: counter(linenumber);
+ color: #999;
+ display: block;
+ padding-right: 0.8em;
+ text-align: right;
+}
diff --git a/plugins/show-invisibles/show-invisibles.css b/plugins/show-invisibles/show-invisibles.css
new file mode 100644
index 0000000..c81928a
--- /dev/null
+++ b/plugins/show-invisibles/show-invisibles.css
@@ -0,0 +1,34 @@
+.token.tab:not(:empty),
+.token.cr,
+.token.lf,
+.token.space {
+ position: relative;
+}
+
+.token.tab:not(:empty):before,
+.token.cr:before,
+.token.lf:before,
+.token.space:before {
+ color: #808080;
+ opacity: 0.6;
+ position: absolute;
+}
+
+.token.tab:not(:empty):before {
+ content: '\21E5';
+}
+
+.token.cr:before {
+ content: '\240D';
+}
+
+.token.crlf:before {
+ content: '\240D\240A';
+}
+.token.lf:before {
+ content: '\240A';
+}
+
+.token.space:before {
+ content: '\00B7';
+}
diff --git a/plugins/show-invisibles/show-invisibles.js b/plugins/show-invisibles/show-invisibles.js
new file mode 100644
index 0000000..e1f97f9
--- /dev/null
+++ b/plugins/show-invisibles/show-invisibles.js
@@ -0,0 +1,83 @@
+(function () {
+
+ if (typeof Prism === 'undefined') {
+ return;
+ }
+
+
+ var invisibles = {
+ 'tab': /\t/,
+ 'crlf': /\r\n/,
+ 'lf': /\n/,
+ 'cr': /\r/,
+ 'space': / /
+ };
+
+
+ /**
+ * Handles the recursive calling of `addInvisibles` for one token.
+ *
+ * @param {Object|Array} tokens The grammar or array which contains the token.
+ * @param {string|number} name The name or index of the token in `tokens`.
+ */
+ function handleToken(tokens, name) {
+ var value = tokens[name];
+
+ var type = Prism.util.type(value);
+ switch (type) {
+ case 'RegExp':
+ var inside = {};
+ tokens[name] = {
+ pattern: value,
+ inside: inside
+ };
+ addInvisibles(inside);
+ break;
+
+ case 'Array':
+ for (var i = 0, l = value.length; i < l; i++) {
+ handleToken(value, i);
+ }
+ break;
+
+ default: // 'Object'
+ // eslint-disable-next-line no-redeclare
+ var inside = value.inside || (value.inside = {});
+ addInvisibles(inside);
+ break;
+ }
+ }
+
+ /**
+ * Recursively adds patterns to match invisible characters to the given grammar (if not added already).
+ *
+ * @param {Object} grammar
+ */
+ function addInvisibles(grammar) {
+ if (!grammar || grammar['tab']) {
+ return;
+ }
+
+ // assign invisibles here to "mark" the grammar in case of self references
+ for (var name in invisibles) {
+ if (invisibles.hasOwnProperty(name)) {
+ grammar[name] = invisibles[name];
+ }
+ }
+
+ // eslint-disable-next-line no-redeclare
+ for (var name in grammar) {
+ if (grammar.hasOwnProperty(name) && !invisibles[name]) {
+ if (name === 'rest') {
+ addInvisibles(grammar['rest']);
+ } else {
+ handleToken(grammar, name);
+ }
+ }
+ }
+ }
+
+ Prism.hooks.add('before-highlight', function (env) {
+ addInvisibles(env.grammar);
+ });
+}());
diff --git a/supports/create-react-app/src/App.js b/supports/create-react-app/src/App.js
index 153a680..38b376b 100644
--- a/supports/create-react-app/src/App.js
+++ b/supports/create-react-app/src/App.js
@@ -3,6 +3,9 @@ import { usePrism } from 'next-prism';
import 'next-prism/themes/tomorrow.css';
+import 'next-prism/plugins/show-invisibles/show-invisibles.css';
+import 'next-prism/plugins/show-invisibles/show-invisibles';
+
function App() {
const [count, setCount] = useState(0);
const { Code, highlightAll } = usePrism();