99 lines
3 KiB
JavaScript
99 lines
3 KiB
JavaScript
/* eslint-disable class-methods-use-this */
|
|
|
|
import { CodeBlock as BaseCodeBlock } from 'tiptap-extensions';
|
|
|
|
const PLAINTEXT_LANG = 'plaintext';
|
|
|
|
// Transforms generated HTML back to GFM for:
|
|
// - Banzai::Filter::SyntaxHighlightFilter
|
|
// - Banzai::Filter::MathFilter
|
|
// - Banzai::Filter::MermaidFilter
|
|
// - Banzai::Filter::SuggestionFilter
|
|
export default class CodeBlock extends BaseCodeBlock {
|
|
get schema() {
|
|
return {
|
|
content: 'text*',
|
|
marks: '',
|
|
group: 'block',
|
|
code: true,
|
|
defining: true,
|
|
attrs: {
|
|
lang: { default: PLAINTEXT_LANG },
|
|
},
|
|
parseDOM: [
|
|
// Matches HTML generated by Banzai::Filter::SyntaxHighlightFilter, Banzai::Filter::MathFilter, Banzai::Filter::MermaidFilter, or Banzai::Filter::SuggestionFilter
|
|
{
|
|
tag: 'pre.code.highlight',
|
|
preserveWhitespace: 'full',
|
|
getAttrs: el => {
|
|
const lang = el.getAttribute('lang');
|
|
if (!lang || lang === '') return {};
|
|
|
|
return { lang };
|
|
},
|
|
},
|
|
// Matches HTML generated by Banzai::Filter::MathFilter,
|
|
// after being transformed by app/assets/javascripts/behaviors/markdown/render_math.js
|
|
{
|
|
tag: 'span.katex-display',
|
|
preserveWhitespace: 'full',
|
|
contentElement: 'annotation[encoding="application/x-tex"]',
|
|
attrs: { lang: 'math' },
|
|
},
|
|
// Matches HTML generated by Banzai::Filter::MermaidFilter,
|
|
// after being transformed by app/assets/javascripts/behaviors/markdown/render_mermaid.js
|
|
{
|
|
tag: 'svg.mermaid',
|
|
preserveWhitespace: 'full',
|
|
contentElement: 'text.source',
|
|
attrs: { lang: 'mermaid' },
|
|
},
|
|
// Matches HTML generated by Banzai::Filter::SuggestionFilter,
|
|
// after being transformed by app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
|
|
{
|
|
tag: '.md-suggestion',
|
|
skip: true,
|
|
},
|
|
{
|
|
tag: '.md-suggestion-header',
|
|
ignore: true,
|
|
},
|
|
{
|
|
tag: '.md-suggestion-diff',
|
|
preserveWhitespace: 'full',
|
|
getContent: (el, schema) =>
|
|
[...el.querySelectorAll('.line_content.new span')].map(span =>
|
|
schema.text(span.innerText),
|
|
),
|
|
attrs: { lang: 'suggestion' },
|
|
},
|
|
],
|
|
toDOM: node => ['pre', { class: 'code highlight', lang: node.attrs.lang }, ['code', 0]],
|
|
};
|
|
}
|
|
|
|
toMarkdown(state, node) {
|
|
if (!node.childCount) return;
|
|
|
|
const {
|
|
textContent: text,
|
|
attrs: { lang },
|
|
} = node;
|
|
|
|
// Prefixes lines with 4 spaces if the code contains a line that starts with triple backticks
|
|
if (lang === PLAINTEXT_LANG && text.match(/^```/gm)) {
|
|
state.wrapBlock(' ', null, node, () => state.text(text, false));
|
|
return;
|
|
}
|
|
|
|
state.write('```');
|
|
if (lang !== PLAINTEXT_LANG) state.write(lang);
|
|
|
|
state.ensureNewLine();
|
|
state.text(text, false);
|
|
state.ensureNewLine();
|
|
|
|
state.write('```');
|
|
state.closeBlock(node);
|
|
}
|
|
}
|