add basic implementation of hightlight.js
This commit is contained in:
@@ -37,9 +37,62 @@
|
|||||||
background: $color-black;
|
background: $color-black;
|
||||||
color: $color-white;
|
color: $color-white;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
code {
|
code {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-quote {
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable,
|
||||||
|
.hljs-attribute,
|
||||||
|
.hljs-tag,
|
||||||
|
.hljs-name,
|
||||||
|
.hljs-regexp,
|
||||||
|
.hljs-link,
|
||||||
|
.hljs-name,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-selector-class {
|
||||||
|
color: #f2777a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-number,
|
||||||
|
.hljs-meta,
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-builtin-name,
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-type,
|
||||||
|
.hljs-params {
|
||||||
|
color: #f99157;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-bullet {
|
||||||
|
color: #99cc99;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-title,
|
||||||
|
.hljs-section {
|
||||||
|
color: #ffcc66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-selector-tag {
|
||||||
|
color: #6699cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
81
examples/Components/Routes/CodeHighlighting/examples.js
Normal file
81
examples/Components/Routes/CodeHighlighting/examples.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
export const javascript = `function $initHighlight(block, flags) {
|
||||||
|
try {
|
||||||
|
if (block.className.search(/\bno\-highlight\b/) != -1)
|
||||||
|
return processBlock(block, true, 0x0F) + ' class=""';
|
||||||
|
} catch (e) {
|
||||||
|
/* handle exception */
|
||||||
|
}
|
||||||
|
for (var i = 0 / 2; i < classes.length; i++) { // "0 / 2" should not be parsed as regexp
|
||||||
|
if (checkCondition(classes[i]) === undefined)
|
||||||
|
return /\d+/g;
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
export const css = `@font-face {
|
||||||
|
font-family: Chunkfive; src: url('Chunkfive.otf');
|
||||||
|
}
|
||||||
|
|
||||||
|
body, .usertext {
|
||||||
|
color: #F0F0F0; background: #600;
|
||||||
|
font-family: Chunkfive, sans;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import url(print.css);
|
||||||
|
@media print {
|
||||||
|
a[href^=http]::after {
|
||||||
|
content: attr(href)
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
export const php = `require_once 'Zend/Uri/Http.php';
|
||||||
|
|
||||||
|
namespace Location\Web;
|
||||||
|
|
||||||
|
interface Factory
|
||||||
|
{
|
||||||
|
static function _factory();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class URI extends BaseURI implements Factory
|
||||||
|
{
|
||||||
|
abstract function test();
|
||||||
|
|
||||||
|
public static $st1 = 1;
|
||||||
|
const ME = "Yo";
|
||||||
|
var $list = NULL;
|
||||||
|
private $var;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a URI
|
||||||
|
*
|
||||||
|
* @return URI
|
||||||
|
*/
|
||||||
|
static public function _factory($stats = array(), $uri = 'http')
|
||||||
|
{
|
||||||
|
echo __METHOD__;
|
||||||
|
$uri = explode(':', $uri, 0b10);
|
||||||
|
$schemeSpecific = isset($uri[1]) ? $uri[1] : '';
|
||||||
|
$desc = 'Multi
|
||||||
|
line description';
|
||||||
|
|
||||||
|
// Security check
|
||||||
|
if (!ctype_alnum($scheme)) {
|
||||||
|
throw new Zend_Uri_Exception('Illegal scheme');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->var = 0 - self::$st;
|
||||||
|
$this->list = list(Array("1"=> 2, 2=>self::ME, 3 => \Location\Web\URI::class));
|
||||||
|
|
||||||
|
return [
|
||||||
|
'uri' => $uri,
|
||||||
|
'value' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo URI::ME . URI::$st1;
|
||||||
|
|
||||||
|
__halt_compiler () ; datahere
|
||||||
|
datahere
|
||||||
|
datahere */
|
||||||
|
datahere`
|
||||||
72
examples/Components/Routes/CodeHighlighting/index.vue
Normal file
72
examples/Components/Routes/CodeHighlighting/index.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<editor class="editor" :extensions="extensions">
|
||||||
|
|
||||||
|
<div class="editor__content" slot="content" slot-scope="props">
|
||||||
|
<h2>
|
||||||
|
Code Highlighting
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
These are code blocks with <strong>automatic syntax highlighting</strong> based on highlight.js.
|
||||||
|
</p>
|
||||||
|
<pre><code v-html="javascript"></code></pre>
|
||||||
|
<pre><code v-html="css"></code></pre>
|
||||||
|
<pre><code v-html="php"></code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</editor>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Icon from 'Components/Icon'
|
||||||
|
import { Editor } from 'tiptap'
|
||||||
|
import {
|
||||||
|
BlockquoteNode,
|
||||||
|
BulletListNode,
|
||||||
|
CodeBlockHighlightNode,
|
||||||
|
HardBreakNode,
|
||||||
|
HeadingNode,
|
||||||
|
ListItemNode,
|
||||||
|
OrderedListNode,
|
||||||
|
TodoItemNode,
|
||||||
|
TodoListNode,
|
||||||
|
BoldMark,
|
||||||
|
CodeMark,
|
||||||
|
ItalicMark,
|
||||||
|
LinkMark,
|
||||||
|
HistoryExtension,
|
||||||
|
} from 'tiptap-extensions'
|
||||||
|
|
||||||
|
import { javascript, css, php } from './examples'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Editor,
|
||||||
|
Icon,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
extensions: [
|
||||||
|
new BlockquoteNode(),
|
||||||
|
new BulletListNode(),
|
||||||
|
new CodeBlockHighlightNode(),
|
||||||
|
new HardBreakNode(),
|
||||||
|
new HeadingNode({ maxLevel: 3 }),
|
||||||
|
new ListItemNode(),
|
||||||
|
new OrderedListNode(),
|
||||||
|
new TodoItemNode(),
|
||||||
|
new TodoListNode(),
|
||||||
|
new BoldMark(),
|
||||||
|
new CodeMark(),
|
||||||
|
new ItalicMark(),
|
||||||
|
new LinkMark(),
|
||||||
|
new HistoryExtension(),
|
||||||
|
],
|
||||||
|
javascript,
|
||||||
|
css,
|
||||||
|
php,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -21,6 +21,9 @@
|
|||||||
<router-link class="subnavigation__link" to="/markdown-shortcuts">
|
<router-link class="subnavigation__link" to="/markdown-shortcuts">
|
||||||
Markdown Shortcuts
|
Markdown Shortcuts
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<router-link class="subnavigation__link" to="/code-highlighting">
|
||||||
|
Code Highlighting
|
||||||
|
</router-link>
|
||||||
<router-link class="subnavigation__link" to="/read-only">
|
<router-link class="subnavigation__link" to="/read-only">
|
||||||
Read-Only
|
Read-Only
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import RouteImages from 'Components/Routes/Images'
|
|||||||
import RouteHidingMenuBar from 'Components/Routes/HidingMenuBar'
|
import RouteHidingMenuBar from 'Components/Routes/HidingMenuBar'
|
||||||
import RouteTodoList from 'Components/Routes/TodoList'
|
import RouteTodoList from 'Components/Routes/TodoList'
|
||||||
import RouteMarkdownShortcuts from 'Components/Routes/MarkdownShortcuts'
|
import RouteMarkdownShortcuts from 'Components/Routes/MarkdownShortcuts'
|
||||||
|
import RouteCodeHighlighting from 'Components/Routes/CodeHighlighting'
|
||||||
import RouteReadOnly from 'Components/Routes/ReadOnly'
|
import RouteReadOnly from 'Components/Routes/ReadOnly'
|
||||||
import RouteEmbeds from 'Components/Routes/Embeds'
|
import RouteEmbeds from 'Components/Routes/Embeds'
|
||||||
import RouteExport from 'Components/Routes/Export'
|
import RouteExport from 'Components/Routes/Export'
|
||||||
@@ -71,6 +72,13 @@ const routes = [
|
|||||||
githubUrl: 'https://github.com/heyscrumpy/tiptap/tree/master/examples/Components/Routes/MarkdownShortcuts',
|
githubUrl: 'https://github.com/heyscrumpy/tiptap/tree/master/examples/Components/Routes/MarkdownShortcuts',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/code-highlighting',
|
||||||
|
component: RouteCodeHighlighting,
|
||||||
|
meta: {
|
||||||
|
githubUrl: 'https://github.com/heyscrumpy/tiptap/tree/master/examples/Components/Routes/CodeHighlighting',
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/read-only',
|
path: '/read-only',
|
||||||
component: RouteReadOnly,
|
component: RouteReadOnly,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"url": "https://github.com/heyscrumpy/tiptap/issues"
|
"url": "https://github.com/heyscrumpy/tiptap/issues"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"lowlight": "^1.10.0",
|
||||||
"prosemirror-history": "^1.0.2",
|
"prosemirror-history": "^1.0.2",
|
||||||
"tiptap": "^0.8.0",
|
"tiptap": "^0.8.0",
|
||||||
"tiptap-commands": "^0.2.4"
|
"tiptap-commands": "^0.2.4"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export { default as BlockquoteNode } from './nodes/Blockquote'
|
export { default as BlockquoteNode } from './nodes/Blockquote'
|
||||||
export { default as BulletListNode } from './nodes/BulletList'
|
export { default as BulletListNode } from './nodes/BulletList'
|
||||||
export { default as CodeBlockNode } from './nodes/CodeBlock'
|
export { default as CodeBlockNode } from './nodes/CodeBlock'
|
||||||
|
export { default as CodeBlockHighlightNode } from './nodes/CodeBlockHighlight'
|
||||||
export { default as HardBreakNode } from './nodes/HardBreak'
|
export { default as HardBreakNode } from './nodes/HardBreak'
|
||||||
export { default as HeadingNode } from './nodes/Heading'
|
export { default as HeadingNode } from './nodes/Heading'
|
||||||
export { default as ImageNode } from './nodes/Image'
|
export { default as ImageNode } from './nodes/Image'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Node } from 'tiptap'
|
import { Node, Plugin } from 'tiptap'
|
||||||
import { wrappingInputRule, setBlockType, wrapIn } from 'tiptap-commands'
|
import { wrappingInputRule, wrapIn } from 'tiptap-commands'
|
||||||
|
|
||||||
export default class BlockquoteNode extends Node {
|
export default class BlockquoteNode extends Node {
|
||||||
|
|
||||||
|
|||||||
109
packages/tiptap-extensions/src/nodes/CodeBlockHighlight.js
Normal file
109
packages/tiptap-extensions/src/nodes/CodeBlockHighlight.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import { Node, Plugin } from 'tiptap'
|
||||||
|
import { Decoration, DecorationSet } from 'prosemirror-view'
|
||||||
|
import { toggleBlockType, setBlockType, textblockTypeInputRule } from 'tiptap-commands'
|
||||||
|
import { findBlockNodes } from 'prosemirror-utils'
|
||||||
|
import low from 'lowlight'
|
||||||
|
|
||||||
|
export default class CodeBlockHighlightNode extends Node {
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return 'code_block'
|
||||||
|
}
|
||||||
|
|
||||||
|
get schema() {
|
||||||
|
return {
|
||||||
|
content: 'text*',
|
||||||
|
marks: '',
|
||||||
|
group: 'block',
|
||||||
|
code: true,
|
||||||
|
defining: true,
|
||||||
|
draggable: false,
|
||||||
|
parseDOM: [
|
||||||
|
{ tag: 'pre', preserveWhitespace: 'full' },
|
||||||
|
],
|
||||||
|
toDOM: () => ['pre', ['code', 0]],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
command({ type, schema }) {
|
||||||
|
return toggleBlockType(type, schema.nodes.paragraph)
|
||||||
|
}
|
||||||
|
|
||||||
|
keys({ type }) {
|
||||||
|
return {
|
||||||
|
'Shift-Ctrl-\\': setBlockType(type),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputRules({ type }) {
|
||||||
|
return [
|
||||||
|
textblockTypeInputRule(/^```$/, type),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
get plugins() {
|
||||||
|
return [
|
||||||
|
new Plugin({
|
||||||
|
props: {
|
||||||
|
decorations({ doc }) {
|
||||||
|
const decorations = []
|
||||||
|
|
||||||
|
const blocks = findBlockNodes(doc)
|
||||||
|
.filter(item => item.node.type.name === 'code_block')
|
||||||
|
|
||||||
|
const flatten = list => list.reduce(
|
||||||
|
(a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
|
||||||
|
)
|
||||||
|
|
||||||
|
function parseNodes(nodes, className = []) {
|
||||||
|
return nodes.map(node => {
|
||||||
|
|
||||||
|
const classes = [
|
||||||
|
...className,
|
||||||
|
...node.properties ? node.properties.className : [],
|
||||||
|
]
|
||||||
|
|
||||||
|
if (node.children) {
|
||||||
|
return parseNodes(node.children, classes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: node.value,
|
||||||
|
classes,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks.forEach(block => {
|
||||||
|
let startPos = block.pos + 1
|
||||||
|
const nodes = low.highlightAuto(block.node.textContent).value
|
||||||
|
|
||||||
|
flatten(parseNodes(nodes))
|
||||||
|
.map(node => {
|
||||||
|
const from = startPos
|
||||||
|
const to = from + node.text.length
|
||||||
|
|
||||||
|
startPos = to
|
||||||
|
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.forEach(node => {
|
||||||
|
const decoration = Decoration.inline(node.from, node.to, {
|
||||||
|
class: node.classes.join(' '),
|
||||||
|
})
|
||||||
|
decorations.push(decoration)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return DecorationSet.create(doc, decorations)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
21
yarn.lock
21
yarn.lock
@@ -4068,6 +4068,12 @@ fastparse@^1.1.1:
|
|||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
|
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
|
||||||
|
|
||||||
|
fault@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.2.tgz#c3d0fec202f172a3a4d414042ad2bb5e2a3ffbaa"
|
||||||
|
dependencies:
|
||||||
|
format "^0.2.2"
|
||||||
|
|
||||||
fd-slicer@~1.1.0:
|
fd-slicer@~1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||||
@@ -4285,6 +4291,10 @@ form-data@~2.3.1, form-data@~2.3.2:
|
|||||||
combined-stream "1.0.6"
|
combined-stream "1.0.6"
|
||||||
mime-types "^2.1.12"
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
|
format@^0.2.2:
|
||||||
|
version "0.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b"
|
||||||
|
|
||||||
fragment-cache@^0.2.1:
|
fragment-cache@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
|
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
|
||||||
@@ -4865,6 +4875,10 @@ hex-color-regex@^1.1.0:
|
|||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
|
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
|
||||||
|
|
||||||
|
highlight.js@~9.12.0:
|
||||||
|
version "9.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
|
||||||
|
|
||||||
hmac-drbg@^1.0.0:
|
hmac-drbg@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||||
@@ -6125,6 +6139,13 @@ lowercase-keys@^1.0.0:
|
|||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
||||||
|
|
||||||
|
lowlight@^1.10.0:
|
||||||
|
version "1.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.10.0.tgz#fbbd9158240d9c6c07c44b055ff0c32f1a2c9b99"
|
||||||
|
dependencies:
|
||||||
|
fault "^1.0.2"
|
||||||
|
highlight.js "~9.12.0"
|
||||||
|
|
||||||
lpad-align@^1.0.1:
|
lpad-align@^1.0.1:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/lpad-align/-/lpad-align-1.1.2.tgz#21f600ac1c3095c3c6e497ee67271ee08481fe9e"
|
resolved "https://registry.yarnpkg.com/lpad-align/-/lpad-align-1.1.2.tgz#21f600ac1c3095c3c6e497ee67271ee08481fe9e"
|
||||||
|
|||||||
Reference in New Issue
Block a user