mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-21 06:31:28 +02:00
syntax highlighting (#3762)
* syntax highlighting * Delete package-lock.json * Update package.json * Update package.json * Delete syntax-highlighting.js * Update [example].js * Update [example].js * Update [example].js * added correct file * linting * Update site/examples/code-highlighting.js Co-authored-by: Tim Buckley <timothypbuckley@gmail.com> * Update site/examples/code-highlighting.js Co-authored-by: Tim Buckley <timothypbuckley@gmail.com> * Update site/examples/code-highlighting.js Co-authored-by: Tim Buckley <timothypbuckley@gmail.com> * updated pull request with some linting * moved getLength fcn Co-authored-by: Tim Buckley <timothypbuckley@gmail.com>
This commit is contained in:
237
site/examples/code-highlighting.js
Normal file
237
site/examples/code-highlighting.js
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
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 } from 'slate'
|
||||||
|
import { withHistory } from 'slate-history'
|
||||||
|
import { css } from 'emotion'
|
||||||
|
|
||||||
|
const CodeHighlightingExample = () => {
|
||||||
|
const [value, setValue] = useState(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 = [
|
||||||
|
{
|
||||||
|
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
|
@@ -24,6 +24,7 @@ import PlainText from '../../examples/plaintext'
|
|||||||
import ReadOnly from '../../examples/read-only'
|
import ReadOnly from '../../examples/read-only'
|
||||||
import RichText from '../../examples/richtext'
|
import RichText from '../../examples/richtext'
|
||||||
import SearchHighlighting from '../../examples/search-highlighting'
|
import SearchHighlighting from '../../examples/search-highlighting'
|
||||||
|
import CodeHighlighting from '../../examples/code-highlighting'
|
||||||
import Tables from '../../examples/tables'
|
import Tables from '../../examples/tables'
|
||||||
|
|
||||||
const EXAMPLES = [
|
const EXAMPLES = [
|
||||||
@@ -43,6 +44,7 @@ const EXAMPLES = [
|
|||||||
['Read-only', ReadOnly, 'read-only'],
|
['Read-only', ReadOnly, 'read-only'],
|
||||||
['Rich Text', RichText, 'richtext'],
|
['Rich Text', RichText, 'richtext'],
|
||||||
['Search Highlighting', SearchHighlighting, 'search-highlighting'],
|
['Search Highlighting', SearchHighlighting, 'search-highlighting'],
|
||||||
|
['Code Highlighting', CodeHighlighting, 'code-highlighting'],
|
||||||
['Tables', Tables, 'tables'],
|
['Tables', Tables, 'tables'],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user