|
|
@@ -1,123 +0,0 @@
|
|
|
-import React from 'react';
|
|
|
-import Prism from 'prismjs';
|
|
|
-
|
|
|
-const TOKEN_MARK = 'prism-token';
|
|
|
-
|
|
|
-export function setPrismTokens(language, field, values, alias = 'variable') {
|
|
|
- Prism.languages[language][field] = {
|
|
|
- alias,
|
|
|
- pattern: new RegExp(`(?:^|\\s)(${values.join('|')})(?:$|\\s)`),
|
|
|
- };
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Code-highlighting plugin based on Prism and
|
|
|
- * https://github.com/ianstormtaylor/slate/blob/master/examples/code-highlighting/index.js
|
|
|
- *
|
|
|
- * (Adapted to handle nested grammar definitions.)
|
|
|
- */
|
|
|
-
|
|
|
-export default function PrismPlugin({ definition, language }) {
|
|
|
- if (definition) {
|
|
|
- // Don't override exising modified definitions
|
|
|
- Prism.languages[language] = Prism.languages[language] || definition;
|
|
|
- }
|
|
|
-
|
|
|
- return {
|
|
|
- /**
|
|
|
- * Render a Slate mark with appropiate CSS class names
|
|
|
- *
|
|
|
- * @param {Object} props
|
|
|
- * @return {Element}
|
|
|
- */
|
|
|
-
|
|
|
- renderMark(props) {
|
|
|
- const { children, mark } = props;
|
|
|
- // Only apply spans to marks identified by this plugin
|
|
|
- if (mark.type !== TOKEN_MARK) {
|
|
|
- return undefined;
|
|
|
- }
|
|
|
- const className = `token ${mark.data.get('types')}`;
|
|
|
- return <span className={className}>{children}</span>;
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * Decorate code blocks with Prism.js highlighting.
|
|
|
- *
|
|
|
- * @param {Node} node
|
|
|
- * @return {Array}
|
|
|
- */
|
|
|
-
|
|
|
- decorateNode(node) {
|
|
|
- if (node.type !== 'paragraph') {
|
|
|
- return [];
|
|
|
- }
|
|
|
-
|
|
|
- const texts = node.getTexts().toArray();
|
|
|
- const tstring = texts.map(t => t.text).join('\n');
|
|
|
- const grammar = Prism.languages[language];
|
|
|
- const tokens = Prism.tokenize(tstring, grammar);
|
|
|
- const decorations = [];
|
|
|
- let startText = texts.shift();
|
|
|
- let endText = startText;
|
|
|
- let startOffset = 0;
|
|
|
- let endOffset = 0;
|
|
|
- let start = 0;
|
|
|
-
|
|
|
- function processToken(token, acc?) {
|
|
|
- // Accumulate token types down the tree
|
|
|
- const types = `${acc || ''} ${token.type || ''} ${token.alias || ''}`;
|
|
|
-
|
|
|
- // Add mark for token node
|
|
|
- if (typeof token === 'string' || typeof token.content === 'string') {
|
|
|
- startText = endText;
|
|
|
- startOffset = endOffset;
|
|
|
-
|
|
|
- const content = typeof token === 'string' ? token : token.content;
|
|
|
- const newlines = content.split('\n').length - 1;
|
|
|
- const length = content.length - newlines;
|
|
|
- const end = start + length;
|
|
|
-
|
|
|
- let available = startText.text.length - startOffset;
|
|
|
- let remaining = length;
|
|
|
-
|
|
|
- endOffset = startOffset + remaining;
|
|
|
-
|
|
|
- while (available < remaining) {
|
|
|
- endText = texts.shift();
|
|
|
- remaining = length - available;
|
|
|
- available = endText.text.length;
|
|
|
- endOffset = remaining;
|
|
|
- }
|
|
|
-
|
|
|
- // Inject marks from up the tree (acc) as well
|
|
|
- if (typeof token !== 'string' || acc) {
|
|
|
- const range = {
|
|
|
- anchorKey: startText.key,
|
|
|
- anchorOffset: startOffset,
|
|
|
- focusKey: endText.key,
|
|
|
- focusOffset: endOffset,
|
|
|
- marks: [{ type: TOKEN_MARK, data: { types } }],
|
|
|
- };
|
|
|
-
|
|
|
- decorations.push(range);
|
|
|
- }
|
|
|
-
|
|
|
- start = end;
|
|
|
- } else if (token.content && token.content.length) {
|
|
|
- // Tokens can be nested
|
|
|
- for (const subToken of token.content) {
|
|
|
- processToken(subToken, types);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Process top-level tokens
|
|
|
- for (const token of tokens) {
|
|
|
- processToken(token);
|
|
|
- }
|
|
|
-
|
|
|
- return decorations;
|
|
|
- },
|
|
|
- };
|
|
|
-}
|