1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-01-17 21:49:20 +01:00
slate/site/examples/code-highlighting.tsx

239 lines
6.5 KiB
TypeScript
Raw Normal View History

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'
import { Text, createEditor, Element as SlateElement, Descendant } from 'slate'
import { withHistory } from 'slate-history'
import { css } from 'emotion'
const CodeHighlightingExample = () => {
const [value, setValue] = useState<Descendant[]>(initialValue)
const [language, setLanguage] = useState('html')
const renderLeaf = useCallback(props => <Leaf {...props} />, [])
const editor = useMemo(() => withHistory(withReact(createEditor())), [])
// 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;
`}
${(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 ||
leaf['attr-name'] ||
leaf.selector) &&
css`
color: #905;
`}
${leaf.punctuation &&
css`
color: #999;
`}
${(leaf.string || leaf.char) &&
css`
color: #690;
`}
${(leaf.function || leaf['class-name']) &&
css`
color: #dd4a68;
`}
`}
>
{children}
</span>
)
}
const initialValue: Descendant[] = [
{
type: 'paragraph',
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