123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- import Delta from 'quill-delta';
- import { ClassAttributor, Scope } from 'parchment';
- import Inline from '../blots/inline.js';
- import Quill from '../core/quill.js';
- import Module from '../core/module.js';
- import { blockDelta } from '../blots/block.js';
- import BreakBlot from '../blots/break.js';
- import CursorBlot from '../blots/cursor.js';
- import TextBlot, { escapeText } from '../blots/text.js';
- import CodeBlock, { CodeBlockContainer } from '../formats/code.js';
- import { traverse } from './clipboard.js';
- const TokenAttributor = new ClassAttributor('code-token', 'hljs', {
- scope: Scope.INLINE
- });
- class CodeToken extends Inline {
- static formats(node, scroll) {
- while (node != null && node !== scroll.domNode) {
- if (node.classList && node.classList.contains(CodeBlock.className)) {
- // @ts-expect-error
- return super.formats(node, scroll);
- }
- // @ts-expect-error
- node = node.parentNode;
- }
- return undefined;
- }
- constructor(scroll, domNode, value) {
- // @ts-expect-error
- super(scroll, domNode, value);
- TokenAttributor.add(this.domNode, value);
- }
- format(format, value) {
- if (format !== CodeToken.blotName) {
- super.format(format, value);
- } else if (value) {
- TokenAttributor.add(this.domNode, value);
- } else {
- TokenAttributor.remove(this.domNode);
- this.domNode.classList.remove(this.statics.className);
- }
- }
- optimize() {
- // @ts-expect-error
- super.optimize(...arguments);
- if (!TokenAttributor.value(this.domNode)) {
- this.unwrap();
- }
- }
- }
- CodeToken.blotName = 'code-token';
- CodeToken.className = 'ql-token';
- class SyntaxCodeBlock extends CodeBlock {
- static create(value) {
- const domNode = super.create(value);
- if (typeof value === 'string') {
- domNode.setAttribute('data-language', value);
- }
- return domNode;
- }
- static formats(domNode) {
- // @ts-expect-error
- return domNode.getAttribute('data-language') || 'plain';
- }
- static register() {} // Syntax module will register
- format(name, value) {
- if (name === this.statics.blotName && value) {
- // @ts-expect-error
- this.domNode.setAttribute('data-language', value);
- } else {
- super.format(name, value);
- }
- }
- replaceWith(name, value) {
- this.formatAt(0, this.length(), CodeToken.blotName, false);
- return super.replaceWith(name, value);
- }
- }
- class SyntaxCodeBlockContainer extends CodeBlockContainer {
- attach() {
- super.attach();
- this.forceNext = false;
- // @ts-expect-error
- this.scroll.emitMount(this);
- }
- format(name, value) {
- if (name === SyntaxCodeBlock.blotName) {
- this.forceNext = true;
- this.children.forEach(child => {
- // @ts-expect-error
- child.format(name, value);
- });
- }
- }
- formatAt(index, length, name, value) {
- if (name === SyntaxCodeBlock.blotName) {
- this.forceNext = true;
- }
- super.formatAt(index, length, name, value);
- }
- highlight(highlight) {
- let forced = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
- if (this.children.head == null) return;
- const nodes = Array.from(this.domNode.childNodes).filter(node => node !== this.uiNode);
- const text = `${nodes.map(node => node.textContent).join('\n')}\n`;
- const language = SyntaxCodeBlock.formats(this.children.head.domNode);
- if (forced || this.forceNext || this.cachedText !== text) {
- if (text.trim().length > 0 || this.cachedText == null) {
- const oldDelta = this.children.reduce((delta, child) => {
- // @ts-expect-error
- return delta.concat(blockDelta(child, false));
- }, new Delta());
- const delta = highlight(text, language);
- oldDelta.diff(delta).reduce((index, _ref) => {
- let {
- retain,
- attributes
- } = _ref;
- // Should be all retains
- if (!retain) return index;
- if (attributes) {
- Object.keys(attributes).forEach(format => {
- if ([SyntaxCodeBlock.blotName, CodeToken.blotName].includes(format)) {
- // @ts-expect-error
- this.formatAt(index, retain, format, attributes[format]);
- }
- });
- }
- // @ts-expect-error
- return index + retain;
- }, 0);
- }
- this.cachedText = text;
- this.forceNext = false;
- }
- }
- html(index, length) {
- const [codeBlock] = this.children.find(index);
- const language = codeBlock ? SyntaxCodeBlock.formats(codeBlock.domNode) : 'plain';
- return `<pre data-language="${language}">\n${escapeText(this.code(index, length))}\n</pre>`;
- }
- optimize(context) {
- super.optimize(context);
- if (this.parent != null && this.children.head != null && this.uiNode != null) {
- const language = SyntaxCodeBlock.formats(this.children.head.domNode);
- // @ts-expect-error
- if (language !== this.uiNode.value) {
- // @ts-expect-error
- this.uiNode.value = language;
- }
- }
- }
- }
- SyntaxCodeBlockContainer.allowedChildren = [SyntaxCodeBlock];
- SyntaxCodeBlock.requiredContainer = SyntaxCodeBlockContainer;
- SyntaxCodeBlock.allowedChildren = [CodeToken, CursorBlot, TextBlot, BreakBlot];
- const highlight = (lib, language, text) => {
- if (typeof lib.versionString === 'string') {
- const majorVersion = lib.versionString.split('.')[0];
- if (parseInt(majorVersion, 10) >= 11) {
- return lib.highlight(text, {
- language
- }).value;
- }
- }
- return lib.highlight(language, text).value;
- };
- class Syntax extends Module {
- static register() {
- Quill.register(CodeToken, true);
- Quill.register(SyntaxCodeBlock, true);
- Quill.register(SyntaxCodeBlockContainer, true);
- }
- constructor(quill, options) {
- super(quill, options);
- if (this.options.hljs == null) {
- throw new Error('Syntax module requires highlight.js. Please include the library on the page before Quill.');
- }
- // @ts-expect-error Fix me later
- this.languages = this.options.languages.reduce((memo, _ref2) => {
- let {
- key
- } = _ref2;
- memo[key] = true;
- return memo;
- }, {});
- this.highlightBlot = this.highlightBlot.bind(this);
- this.initListener();
- this.initTimer();
- }
- initListener() {
- this.quill.on(Quill.events.SCROLL_BLOT_MOUNT, blot => {
- if (!(blot instanceof SyntaxCodeBlockContainer)) return;
- const select = this.quill.root.ownerDocument.createElement('select');
- // @ts-expect-error Fix me later
- this.options.languages.forEach(_ref3 => {
- let {
- key,
- label
- } = _ref3;
- const option = select.ownerDocument.createElement('option');
- option.textContent = label;
- option.setAttribute('value', key);
- select.appendChild(option);
- });
- select.addEventListener('change', () => {
- blot.format(SyntaxCodeBlock.blotName, select.value);
- this.quill.root.focus(); // Prevent scrolling
- this.highlight(blot, true);
- });
- if (blot.uiNode == null) {
- blot.attachUI(select);
- if (blot.children.head) {
- select.value = SyntaxCodeBlock.formats(blot.children.head.domNode);
- }
- }
- });
- }
- initTimer() {
- let timer = null;
- this.quill.on(Quill.events.SCROLL_OPTIMIZE, () => {
- if (timer) {
- clearTimeout(timer);
- }
- timer = setTimeout(() => {
- this.highlight();
- timer = null;
- }, this.options.interval);
- });
- }
- highlight() {
- let blot = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
- let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
- if (this.quill.selection.composing) return;
- this.quill.update(Quill.sources.USER);
- const range = this.quill.getSelection();
- const blots = blot == null ? this.quill.scroll.descendants(SyntaxCodeBlockContainer) : [blot];
- blots.forEach(container => {
- container.highlight(this.highlightBlot, force);
- });
- this.quill.update(Quill.sources.SILENT);
- if (range != null) {
- this.quill.setSelection(range, Quill.sources.SILENT);
- }
- }
- highlightBlot(text) {
- let language = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'plain';
- language = this.languages[language] ? language : 'plain';
- if (language === 'plain') {
- return escapeText(text).split('\n').reduce((delta, line, i) => {
- if (i !== 0) {
- delta.insert('\n', {
- [CodeBlock.blotName]: language
- });
- }
- return delta.insert(line);
- }, new Delta());
- }
- const container = this.quill.root.ownerDocument.createElement('div');
- container.classList.add(CodeBlock.className);
- container.innerHTML = highlight(this.options.hljs, language, text);
- return traverse(this.quill.scroll, container, [(node, delta) => {
- // @ts-expect-error
- const value = TokenAttributor.value(node);
- if (value) {
- return delta.compose(new Delta().retain(delta.length(), {
- [CodeToken.blotName]: value
- }));
- }
- return delta;
- }], [(node, delta) => {
- // @ts-expect-error
- return node.data.split('\n').reduce((memo, nodeText, i) => {
- if (i !== 0) memo.insert('\n', {
- [CodeBlock.blotName]: language
- });
- return memo.insert(nodeText);
- }, delta);
- }], new WeakMap());
- }
- }
- Syntax.DEFAULTS = {
- hljs: (() => {
- return window.hljs;
- })(),
- interval: 1000,
- languages: [{
- key: 'plain',
- label: 'Plain'
- }, {
- key: 'bash',
- label: 'Bash'
- }, {
- key: 'cpp',
- label: 'C++'
- }, {
- key: 'cs',
- label: 'C#'
- }, {
- key: 'css',
- label: 'CSS'
- }, {
- key: 'diff',
- label: 'Diff'
- }, {
- key: 'xml',
- label: 'HTML/XML'
- }, {
- key: 'java',
- label: 'Java'
- }, {
- key: 'javascript',
- label: 'JavaScript'
- }, {
- key: 'markdown',
- label: 'Markdown'
- }, {
- key: 'php',
- label: 'PHP'
- }, {
- key: 'python',
- label: 'Python'
- }, {
- key: 'ruby',
- label: 'Ruby'
- }, {
- key: 'sql',
- label: 'SQL'
- }]
- };
- export { SyntaxCodeBlock as CodeBlock, CodeToken, Syntax as default };
- //# sourceMappingURL=syntax.js.map
|