File
Blob: src/client/components/editor/extensions/code-block/extension.ts
| 1 | import { textblockTypeInputRule } from "@tiptap/core"; |
| 2 | import { Plugin, TextSelection } from "@tiptap/pm/state"; |
| 3 | import { ReactNodeViewRenderer } from "@tiptap/react"; |
| 4 | import { SharedCodeBlock } from "@/shared/editor/schema"; |
| 5 | import { resolveLanguage } from "@/shared/editor/schema/code-block"; |
| 6 | import { resolveCodeBlockLineRange } from "./selection"; |
| 7 | import { CodeBlockView } from "./view"; |
| 8 | import { createLazyHighlightPlugin } from "./lazy-highlight"; |
| 9 | |
| 10 | function createCodeBlockTripleClickPlugin(codeBlockName: string) { |
| 11 | return new Plugin({ |
| 12 | props: { |
| 13 | handleTripleClick(view, pos, event) { |
| 14 | if (event.button !== 0) return false; |
| 15 | |
| 16 | const $pos = view.state.doc.resolve(pos); |
| 17 | if ($pos.parent.type.name !== codeBlockName) return false; |
| 18 | |
| 19 | const range = resolveCodeBlockLineRange($pos.parent.textContent, $pos.parentOffset); |
| 20 | const lineSelection = TextSelection.create(view.state.doc, $pos.start() + range.from, $pos.start() + range.to); |
| 21 | |
| 22 | if (!view.state.selection.eq(lineSelection)) { |
| 23 | view.dispatch(view.state.tr.setSelection(lineSelection).setMeta("pointer", true)); |
| 24 | } else if (!view.hasFocus()) { |
| 25 | view.focus(); |
| 26 | } |
| 27 | |
| 28 | event.preventDefault(); |
| 29 | return true; |
| 30 | }, |
| 31 | }, |
| 32 | }); |
| 33 | } |
| 34 | |
| 35 | export const HighlightedCodeBlock = SharedCodeBlock.extend({ |
| 36 | addNodeView() { |
| 37 | return ReactNodeViewRenderer(CodeBlockView); |
| 38 | }, |
| 39 | |
| 40 | addProseMirrorPlugins() { |
| 41 | return [ |
| 42 | ...(this.parent?.() ?? []), |
| 43 | createCodeBlockTripleClickPlugin(this.name), |
| 44 | createLazyHighlightPlugin(this.name, this.options.defaultLanguage), |
| 45 | ]; |
| 46 | }, |
| 47 | |
| 48 | addInputRules() { |
| 49 | return [ |
| 50 | textblockTypeInputRule({ |
| 51 | find: /^```([a-z]+)?[\s\n]$/, |
| 52 | type: this.type, |
| 53 | getAttributes: (match) => ({ language: resolveLanguage(match[1]) }), |
| 54 | }), |
| 55 | textblockTypeInputRule({ |
| 56 | find: /^~~~([a-z]+)?[\s\n]$/, |
| 57 | type: this.type, |
| 58 | getAttributes: (match) => ({ language: resolveLanguage(match[1]) }), |
| 59 | }), |
| 60 | ]; |
| 61 | }, |
| 62 | }); |