mirror of
https://github.com/ueberdosis/tiptap.git
synced 2025-01-18 22:36:14 +08:00
add basic implementation of hightlight.js
This commit is contained in:
parent
64843dd5a6
commit
feb79e0648
@ -37,9 +37,62 @@
|
||||
background: $color-black;
|
||||
color: $color-white;
|
||||
font-size: 0.8rem;
|
||||
overflow-x: auto;
|
||||
|
||||
code {
|
||||
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">
|
||||
Markdown Shortcuts
|
||||
</router-link>
|
||||
<router-link class="subnavigation__link" to="/code-highlighting">
|
||||
Code Highlighting
|
||||
</router-link>
|
||||
<router-link class="subnavigation__link" to="/read-only">
|
||||
Read-Only
|
||||
</router-link>
|
||||
|
@ -10,6 +10,7 @@ import RouteImages from 'Components/Routes/Images'
|
||||
import RouteHidingMenuBar from 'Components/Routes/HidingMenuBar'
|
||||
import RouteTodoList from 'Components/Routes/TodoList'
|
||||
import RouteMarkdownShortcuts from 'Components/Routes/MarkdownShortcuts'
|
||||
import RouteCodeHighlighting from 'Components/Routes/CodeHighlighting'
|
||||
import RouteReadOnly from 'Components/Routes/ReadOnly'
|
||||
import RouteEmbeds from 'Components/Routes/Embeds'
|
||||
import RouteExport from 'Components/Routes/Export'
|
||||
@ -71,6 +72,13 @@ const routes = [
|
||||
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',
|
||||
component: RouteReadOnly,
|
||||
|
@ -20,6 +20,7 @@
|
||||
"url": "https://github.com/heyscrumpy/tiptap/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"lowlight": "^1.10.0",
|
||||
"prosemirror-history": "^1.0.2",
|
||||
"tiptap": "^0.8.0",
|
||||
"tiptap-commands": "^0.2.4"
|
||||
|
@ -1,6 +1,7 @@
|
||||
export { default as BlockquoteNode } from './nodes/Blockquote'
|
||||
export { default as BulletListNode } from './nodes/BulletList'
|
||||
export { default as CodeBlockNode } from './nodes/CodeBlock'
|
||||
export { default as CodeBlockHighlightNode } from './nodes/CodeBlockHighlight'
|
||||
export { default as HardBreakNode } from './nodes/HardBreak'
|
||||
export { default as HeadingNode } from './nodes/Heading'
|
||||
export { default as ImageNode } from './nodes/Image'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Node } from 'tiptap'
|
||||
import { wrappingInputRule, setBlockType, wrapIn } from 'tiptap-commands'
|
||||
import { Node, Plugin } from 'tiptap'
|
||||
import { wrappingInputRule, wrapIn } from 'tiptap-commands'
|
||||
|
||||
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"
|
||||
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:
|
||||
version "1.1.0"
|
||||
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"
|
||||
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:
|
||||
version "0.2.1"
|
||||
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"
|
||||
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:
|
||||
version "1.0.1"
|
||||
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"
|
||||
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:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lpad-align/-/lpad-align-1.1.2.tgz#21f600ac1c3095c3c6e497ee67271ee08481fe9e"
|
||||
|
Loading…
Reference in New Issue
Block a user