| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 | 
							- import { $isAtNodeEnd } from '@lexical/selection'
 
- import type {
 
-   ElementNode,
 
-   Klass,
 
-   LexicalEditor,
 
-   LexicalNode,
 
-   RangeSelection,
 
-   TextNode,
 
- } from 'lexical'
 
- import {
 
-   $createTextNode,
 
-   $isTextNode,
 
- } from 'lexical'
 
- import type { EntityMatch } from '@lexical/text'
 
- import { CustomTextNode } from './plugins/custom-text/node'
 
- export function getSelectedNode(
 
-   selection: RangeSelection,
 
- ): TextNode | ElementNode {
 
-   const anchor = selection.anchor
 
-   const focus = selection.focus
 
-   const anchorNode = selection.anchor.getNode()
 
-   const focusNode = selection.focus.getNode()
 
-   if (anchorNode === focusNode)
 
-     return anchorNode
 
-   const isBackward = selection.isBackward()
 
-   if (isBackward)
 
-     return $isAtNodeEnd(focus) ? anchorNode : focusNode
 
-   else
 
-     return $isAtNodeEnd(anchor) ? anchorNode : focusNode
 
- }
 
- export function registerLexicalTextEntity<T extends TextNode>(
 
-   editor: LexicalEditor,
 
-   getMatch: (text: string) => null | EntityMatch,
 
-   targetNode: Klass<T>,
 
-   createNode: (textNode: TextNode) => T,
 
- ) {
 
-   const isTargetNode = (node: LexicalNode | null | undefined): node is T => {
 
-     return node instanceof targetNode
 
-   }
 
-   const replaceWithSimpleText = (node: TextNode): void => {
 
-     const textNode = $createTextNode(node.getTextContent())
 
-     textNode.setFormat(node.getFormat())
 
-     node.replace(textNode)
 
-   }
 
-   const getMode = (node: TextNode): number => {
 
-     return node.getLatest().__mode
 
-   }
 
-   const textNodeTransform = (node: TextNode) => {
 
-     if (!node.isSimpleText())
 
-       return
 
-     const prevSibling = node.getPreviousSibling()
 
-     let text = node.getTextContent()
 
-     let currentNode = node
 
-     let match
 
-     if ($isTextNode(prevSibling)) {
 
-       const previousText = prevSibling.getTextContent()
 
-       const combinedText = previousText + text
 
-       const prevMatch = getMatch(combinedText)
 
-       if (isTargetNode(prevSibling)) {
 
-         if (prevMatch === null || getMode(prevSibling) !== 0) {
 
-           replaceWithSimpleText(prevSibling)
 
-           return
 
-         }
 
-         else {
 
-           const diff = prevMatch.end - previousText.length
 
-           if (diff > 0) {
 
-             const concatText = text.slice(0, diff)
 
-             const newTextContent = previousText + concatText
 
-             prevSibling.select()
 
-             prevSibling.setTextContent(newTextContent)
 
-             if (diff === text.length) {
 
-               node.remove()
 
-             }
 
-             else {
 
-               const remainingText = text.slice(diff)
 
-               node.setTextContent(remainingText)
 
-             }
 
-             return
 
-           }
 
-         }
 
-       }
 
-       else if (prevMatch === null || prevMatch.start < previousText.length) {
 
-         return
 
-       }
 
-     }
 
-     while (true) {
 
-       match = getMatch(text)
 
-       let nextText = match === null ? '' : text.slice(match.end)
 
-       text = nextText
 
-       if (nextText === '') {
 
-         const nextSibling = currentNode.getNextSibling()
 
-         if ($isTextNode(nextSibling)) {
 
-           nextText = currentNode.getTextContent() + nextSibling.getTextContent()
 
-           const nextMatch = getMatch(nextText)
 
-           if (nextMatch === null) {
 
-             if (isTargetNode(nextSibling))
 
-               replaceWithSimpleText(nextSibling)
 
-             else
 
-               nextSibling.markDirty()
 
-             return
 
-           }
 
-           else if (nextMatch.start !== 0) {
 
-             return
 
-           }
 
-         }
 
-       }
 
-       else {
 
-         const nextMatch = getMatch(nextText)
 
-         if (nextMatch !== null && nextMatch.start === 0)
 
-           return
 
-       }
 
-       if (match === null)
 
-         return
 
-       if (match.start === 0 && $isTextNode(prevSibling) && prevSibling.isTextEntity())
 
-         continue
 
-       let nodeToReplace
 
-       if (match.start === 0)
 
-         [nodeToReplace, currentNode] = currentNode.splitText(match.end)
 
-       else
 
-         [, nodeToReplace, currentNode] = currentNode.splitText(match.start, match.end)
 
-       const replacementNode = createNode(nodeToReplace)
 
-       replacementNode.setFormat(nodeToReplace.getFormat())
 
-       nodeToReplace.replace(replacementNode)
 
-       if (currentNode == null)
 
-         return
 
-     }
 
-   }
 
-   const reverseNodeTransform = (node: T) => {
 
-     const text = node.getTextContent()
 
-     const match = getMatch(text)
 
-     if (match === null || match.start !== 0) {
 
-       replaceWithSimpleText(node)
 
-       return
 
-     }
 
-     if (text.length > match.end) {
 
-       // This will split out the rest of the text as simple text
 
-       node.splitText(match.end)
 
-       return
 
-     }
 
-     const prevSibling = node.getPreviousSibling()
 
-     if ($isTextNode(prevSibling) && prevSibling.isTextEntity()) {
 
-       replaceWithSimpleText(prevSibling)
 
-       replaceWithSimpleText(node)
 
-     }
 
-     const nextSibling = node.getNextSibling()
 
-     if ($isTextNode(nextSibling) && nextSibling.isTextEntity()) {
 
-       replaceWithSimpleText(nextSibling) // This may have already been converted in the previous block
 
-       if (isTargetNode(node))
 
-         replaceWithSimpleText(node)
 
-     }
 
-   }
 
-   const removePlainTextTransform = editor.registerNodeTransform(CustomTextNode, textNodeTransform)
 
-   const removeReverseNodeTransform = editor.registerNodeTransform(targetNode, reverseNodeTransform)
 
-   return [removePlainTextTransform, removeReverseNodeTransform]
 
- }
 
- export const decoratorTransform = (
 
-   node: CustomTextNode,
 
-   getMatch: (text: string) => null | EntityMatch,
 
-   createNode: () => LexicalNode,
 
- ) => {
 
-   if (!node.isSimpleText())
 
-     return
 
-   const prevSibling = node.getPreviousSibling()
 
-   let text = node.getTextContent()
 
-   let currentNode = node
 
-   let match
 
-   while (true) {
 
-     match = getMatch(text)
 
-     let nextText = match === null ? '' : text.slice(match.end)
 
-     text = nextText
 
-     if (nextText === '') {
 
-       const nextSibling = currentNode.getNextSibling()
 
-       if ($isTextNode(nextSibling)) {
 
-         nextText = currentNode.getTextContent() + nextSibling.getTextContent()
 
-         const nextMatch = getMatch(nextText)
 
-         if (nextMatch === null) {
 
-           nextSibling.markDirty()
 
-           return
 
-         }
 
-         else if (nextMatch.start !== 0) {
 
-           return
 
-         }
 
-       }
 
-     }
 
-     else {
 
-       const nextMatch = getMatch(nextText)
 
-       if (nextMatch !== null && nextMatch.start === 0)
 
-         return
 
-     }
 
-     if (match === null)
 
-       return
 
-     if (match.start === 0 && $isTextNode(prevSibling) && prevSibling.isTextEntity())
 
-       continue
 
-     let nodeToReplace
 
-     if (match.start === 0)
 
-       [nodeToReplace, currentNode] = currentNode.splitText(match.end)
 
-     else
 
-       [, nodeToReplace, currentNode] = currentNode.splitText(match.start, match.end)
 
-     const replacementNode = createNode()
 
-     nodeToReplace.replace(replacementNode)
 
-     if (currentNode == null)
 
-       return
 
-   }
 
- }
 
- export function textToEditorState(text: string) {
 
-   const paragraph = text.split('\n')
 
-   return JSON.stringify({
 
-     root: {
 
-       children: paragraph.map((p) => {
 
-         return {
 
-           children: [{
 
-             detail: 0,
 
-             format: 0,
 
-             mode: 'normal',
 
-             style: '',
 
-             text: p,
 
-             type: 'custom-text',
 
-             version: 1,
 
-           }],
 
-           direction: 'ltr',
 
-           format: '',
 
-           indent: 0,
 
-           type: 'paragraph',
 
-           version: 1,
 
-         }
 
-       }),
 
-       direction: 'ltr',
 
-       format: '',
 
-       indent: 0,
 
-       type: 'root',
 
-       version: 1,
 
-     },
 
-   })
 
- }
 
 
  |