2020-07-09 17:23:56 -05:00
|
|
|
import Prism from 'prismjs'
|
|
|
|
import 'prismjs/components/prism-python'
|
|
|
|
import 'prismjs/components/prism-php'
|
|
|
|
import 'prismjs/components/prism-sql'
|
|
|
|
import 'prismjs/components/prism-java'
|
|
|
|
import React, { useState, useCallback, useMemo } from 'react'
|
|
|
|
import { Slate, Editable, withReact } from 'slate-react'
|
2021-03-29 09:37:24 -07:00
|
|
|
import { Text, createEditor, Element as SlateElement, Descendant } from 'slate'
|
2020-07-09 17:23:56 -05:00
|
|
|
import { withHistory } from 'slate-history'
|
|
|
|
import { css } from 'emotion'
|
|
|
|
|
|
|
|
const CodeHighlightingExample = () => {
|
2021-03-11 11:48:31 -08:00
|
|
|
const [value, setValue] = useState<Descendant[]>(initialValue)
|
2020-07-09 17:23:56 -05:00
|
|
|
const [language, setLanguage] = useState('html')
|
|
|
|
const renderLeaf = useCallback(props => <Leaf {...props} />, [])
|
2021-10-17 14:31:33 +01:00
|
|
|
const editor = useMemo(() => withHistory(withReact(createEditor())), [])
|
2020-07-09 17:23:56 -05:00
|
|
|
|
|
|
|
// decorate function depends on the language selected
|
|
|
|
const decorate = useCallback(
|
|
|
|
([node, path]) => {
|
|
|
|
const ranges = []
|
|
|
|
if (!Text.isText(node)) {
|
|
|
|
return ranges
|
|
|
|
}
|
|
|
|
const tokens = Prism.tokenize(node.text, Prism.languages[language])
|
|
|
|
let start = 0
|
|
|
|
|
|
|
|
for (const token of tokens) {
|
|
|
|
const length = getLength(token)
|
|
|
|
const end = start + length
|
|
|
|
|
|
|
|
if (typeof token !== 'string') {
|
|
|
|
ranges.push({
|
|
|
|
[token.type]: true,
|
|
|
|
anchor: { path, offset: start },
|
|
|
|
focus: { path, offset: end },
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
start = end
|
|
|
|
}
|
|
|
|
|
|
|
|
return ranges
|
|
|
|
},
|
|
|
|
[language]
|
|
|
|
)
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Slate editor={editor} value={value} onChange={value => setValue(value)}>
|
|
|
|
<div
|
|
|
|
contentEditable={false}
|
|
|
|
style={{ position: 'relative', top: '5px', right: '5px' }}
|
|
|
|
>
|
|
|
|
<h3>
|
|
|
|
Select a language
|
|
|
|
<select
|
|
|
|
value={language}
|
|
|
|
style={{ float: 'right' }}
|
|
|
|
onChange={e => setLanguage(e.target.value)}
|
|
|
|
>
|
|
|
|
<option value="js">JavaScript</option>
|
|
|
|
<option value="css">CSS</option>
|
|
|
|
<option value="html">HTML</option>
|
|
|
|
<option value="python">Python</option>
|
|
|
|
<option value="sql">SQL</option>
|
|
|
|
<option value="java">Java</option>
|
|
|
|
<option value="php">PHP</option>
|
|
|
|
</select>
|
|
|
|
</h3>
|
|
|
|
</div>
|
|
|
|
<Editable
|
|
|
|
decorate={decorate}
|
|
|
|
renderLeaf={renderLeaf}
|
|
|
|
placeholder="Write some code..."
|
|
|
|
/>
|
|
|
|
</Slate>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const getLength = token => {
|
|
|
|
if (typeof token === 'string') {
|
|
|
|
return token.length
|
|
|
|
} else if (typeof token.content === 'string') {
|
|
|
|
return token.content.length
|
|
|
|
} else {
|
|
|
|
return token.content.reduce((l, t) => l + getLength(t), 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// different token types, styles found on Prismjs website
|
|
|
|
const Leaf = ({ attributes, children, leaf }) => {
|
|
|
|
return (
|
|
|
|
<span
|
|
|
|
{...attributes}
|
|
|
|
className={css`
|
|
|
|
font-family: monospace;
|
|
|
|
background: hsla(0, 0%, 100%, .5);
|
|
|
|
|
|
|
|
${leaf.comment &&
|
|
|
|
css`
|
|
|
|
color: slategray;
|
2021-10-17 14:31:33 +01:00
|
|
|
`}
|
2020-07-09 17:23:56 -05:00
|
|
|
|
|
|
|
${(leaf.operator || leaf.url) &&
|
|
|
|
css`
|
|
|
|
color: #9a6e3a;
|
|
|
|
`}
|
|
|
|
${leaf.keyword &&
|
|
|
|
css`
|
|
|
|
color: #07a;
|
|
|
|
`}
|
|
|
|
${(leaf.variable || leaf.regex) &&
|
|
|
|
css`
|
|
|
|
color: #e90;
|
|
|
|
`}
|
|
|
|
${(leaf.number ||
|
|
|
|
leaf.boolean ||
|
|
|
|
leaf.tag ||
|
|
|
|
leaf.constant ||
|
|
|
|
leaf.symbol ||
|
2021-02-11 15:29:03 -08:00
|
|
|
leaf['attr-name'] ||
|
2020-07-09 17:23:56 -05:00
|
|
|
leaf.selector) &&
|
|
|
|
css`
|
|
|
|
color: #905;
|
|
|
|
`}
|
|
|
|
${leaf.punctuation &&
|
|
|
|
css`
|
|
|
|
color: #999;
|
|
|
|
`}
|
|
|
|
${(leaf.string || leaf.char) &&
|
|
|
|
css`
|
|
|
|
color: #690;
|
|
|
|
`}
|
2021-02-11 15:29:03 -08:00
|
|
|
${(leaf.function || leaf['class-name']) &&
|
2020-07-09 17:23:56 -05:00
|
|
|
css`
|
|
|
|
color: #dd4a68;
|
|
|
|
`}
|
|
|
|
`}
|
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</span>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-03-29 09:49:44 -07:00
|
|
|
const initialValue: Descendant[] = [
|
2020-07-09 17:23:56 -05:00
|
|
|
{
|
2021-03-11 11:48:31 -08:00
|
|
|
type: 'paragraph',
|
2020-07-09 17:23:56 -05:00
|
|
|
children: [
|
|
|
|
{
|
|
|
|
text: '<h1>Hi!</h1>',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
// modifications and additions to prism library
|
|
|
|
|
|
|
|
Prism.languages.python = Prism.languages.extend('python', {})
|
|
|
|
Prism.languages.insertBefore('python', 'prolog', {
|
|
|
|
comment: { pattern: /##[^\n]*/, alias: 'comment' },
|
|
|
|
})
|
|
|
|
Prism.languages.javascript = Prism.languages.extend('javascript', {})
|
|
|
|
Prism.languages.insertBefore('javascript', 'prolog', {
|
|
|
|
comment: { pattern: /\/\/[^\n]*/, alias: 'comment' },
|
|
|
|
})
|
|
|
|
Prism.languages.html = Prism.languages.extend('html', {})
|
|
|
|
Prism.languages.insertBefore('html', 'prolog', {
|
|
|
|
comment: { pattern: /<!--[^\n]*-->/, alias: 'comment' },
|
|
|
|
})
|
|
|
|
Prism.languages.markdown = Prism.languages.extend('markup', {})
|
|
|
|
Prism.languages.insertBefore('markdown', 'prolog', {
|
|
|
|
blockquote: { pattern: /^>(?:[\t ]*>)*/m, alias: 'punctuation' },
|
|
|
|
code: [
|
|
|
|
{ pattern: /^(?: {4}|\t).+/m, alias: 'keyword' },
|
|
|
|
{ pattern: /``.+?``|`[^`\n]+`/, alias: 'keyword' },
|
|
|
|
],
|
|
|
|
title: [
|
|
|
|
{
|
|
|
|
pattern: /\w+.*(?:\r?\n|\r)(?:==+|--+)/,
|
|
|
|
alias: 'important',
|
|
|
|
inside: { punctuation: /==+$|--+$/ },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pattern: /(^\s*)#+.+/m,
|
|
|
|
lookbehind: !0,
|
|
|
|
alias: 'important',
|
|
|
|
inside: { punctuation: /^#+|#+$/ },
|
|
|
|
},
|
|
|
|
],
|
|
|
|
hr: {
|
|
|
|
pattern: /(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m,
|
|
|
|
lookbehind: !0,
|
|
|
|
alias: 'punctuation',
|
|
|
|
},
|
|
|
|
list: {
|
|
|
|
pattern: /(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,
|
|
|
|
lookbehind: !0,
|
|
|
|
alias: 'punctuation',
|
|
|
|
},
|
|
|
|
'url-reference': {
|
|
|
|
pattern: /!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,
|
|
|
|
inside: {
|
|
|
|
variable: { pattern: /^(!?\[)[^\]]+/, lookbehind: !0 },
|
|
|
|
string: /(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,
|
|
|
|
punctuation: /^[\[\]!:]|[<>]/,
|
|
|
|
},
|
|
|
|
alias: 'url',
|
|
|
|
},
|
|
|
|
bold: {
|
|
|
|
pattern: /(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,
|
|
|
|
lookbehind: !0,
|
|
|
|
inside: { punctuation: /^\*\*|^__|\*\*$|__$/ },
|
|
|
|
},
|
|
|
|
italic: {
|
|
|
|
pattern: /(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,
|
|
|
|
lookbehind: !0,
|
|
|
|
inside: { punctuation: /^[*_]|[*_]$/ },
|
|
|
|
},
|
|
|
|
url: {
|
|
|
|
pattern: /!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,
|
|
|
|
inside: {
|
|
|
|
variable: { pattern: /(!?\[)[^\]]+(?=\]$)/, lookbehind: !0 },
|
|
|
|
string: { pattern: /"(?:\\.|[^"\\])*"(?=\)$)/ },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
Prism.languages.markdown.bold.inside.url = Prism.util.clone(
|
|
|
|
Prism.languages.markdown.url
|
|
|
|
)
|
|
|
|
Prism.languages.markdown.italic.inside.url = Prism.util.clone(
|
|
|
|
Prism.languages.markdown.url
|
|
|
|
)
|
|
|
|
Prism.languages.markdown.bold.inside.italic = Prism.util.clone(
|
|
|
|
Prism.languages.markdown.italic
|
|
|
|
)
|
|
|
|
Prism.languages.markdown.italic.inside.bold = Prism.util.clone(Prism.languages.markdown.bold); // prettier-ignore
|
|
|
|
|
|
|
|
export default CodeHighlightingExample
|