UNPKG

prism-react-editor

Version:

Lightweight, extensible code editor component for React apps

155 lines (154 loc) 5.16 kB
"use client"; import { useLayoutEffect } from "react"; import { n as getClosestToken, a as addTextareaListener, v as voidlessLangs, o as voidTags } from "../local-Cq-4Fajb.js"; const useTagMatcher = (editor) => { useLayoutEffect(() => { let code; let tagIndex; let sp; const stack = []; const pairMap = []; const tags = []; const matchTags = (tokens, language, value) => { code = value; tags.length = pairMap.length = sp = tagIndex = 0; matchTagsRecursive(tokens, language, 0); }; const matchTagsRecursive = (tokens, language, position) => { let noVoidTags = voidlessLangs.has(language); let i = 0; let l = tokens.length; for (; i < l; ) { const token = tokens[i++]; const content = token.content; const length = token.length; if (Array.isArray(content)) { if (token.type == "tag" && code[position] == "<") { const openLen = content[0].length; const tagName = content[2] ? code.substr(position + openLen, content[1].length) : ""; const notSelfClosing = content[content.length - 1].length < 2 && (noVoidTags || !voidTags.test(tagName)); if (content[2] && noVoidTags) matchTagsRecursive(content, language, position); if (notSelfClosing) { if (openLen > 1) { for (let i2 = sp; i2; ) { if (tagName == stack[--i2][1]) { pairMap[pairMap[tagIndex] = stack[sp = i2][0]] = tagIndex; i2 = 0; } } } else { stack[sp++] = [tagIndex, tagName]; } } tags[tagIndex++] = [ token, position, position + length, tagName, openLen > 1, notSelfClosing ]; } else { let lang = token.alias || token.type; matchTagsRecursive( content, lang.slice(0, 9) == "language-" ? lang.slice(9) : language, position ); } } position += length; } }; const cleanup = editor.on("tokenize", matchTags); matchTags(editor.tokens, editor.props.language, editor.value); editor.extensions.matchTags = { tags, pairs: pairMap }; return () => { delete editor.extensions.matchTags; cleanup(); }; }, []); }; const getClosestTagIndex = (pos, tags) => { for (let i = 0, l = tags.length; i < l; i++) if (tags[i][1] <= pos && tags[i][2] >= pos) return i; }; const useHighlightMatchingTags = (editor) => { useLayoutEffect(() => { let openEl, closeEl; const highlight = (remove) => [openEl, closeEl].forEach((el) => { el && el.classList.toggle("active-tagname", !remove); }); const cleanup = editor.on("selectionChange", ([start, end]) => { let newEl1; let newEl2; let index; let matcher = editor.extensions.matchTags; if (start == end && matcher && editor.focused) { index = getClosestTagIndex(start, matcher.tags); if (index + 1) { index = matcher.pairs[index]; if (index + 1 && (newEl1 = getClosestToken(editor, ".tag>.tag"))) { newEl2 = getClosestToken(editor, ".tag>.tag", 2, 0, matcher.tags[index][1]); } } } if (openEl != newEl1) { highlight(true); openEl = newEl1; closeEl = newEl2; highlight(); } }); return () => { cleanup(); highlight(true); }; }, []); }; const useHighlightTagPunctuation = (editor, className, alwaysHighlight) => { useLayoutEffect(() => { let openEl, closeEl; let getPunctuation = (pos) => getClosestToken(editor, ".tag>.punctuation", 0, 0, pos); let highlight = (remove) => [openEl, closeEl].forEach((el) => { el && el.classList.toggle(className, !remove); }); let selectionChange = () => { let [start, end] = editor.getSelection(); let matcher = editor.extensions.matchTags; let newEl1; let newEl2; if (editor.focused && matcher && start == end) { let tag = matcher.tags[getClosestTagIndex(start, matcher.tags)]; if (tag && (alwaysHighlight || !getClosestToken(editor, ".tag>.tag") && getPunctuation())) { newEl1 = getPunctuation(tag[1]); newEl2 = getPunctuation(tag[2] - 1); } } if (openEl != newEl1 || closeEl != newEl2) { highlight(true); openEl = newEl1; closeEl = newEl2; highlight(); } }; let cleanup1 = editor.on("selectionChange", selectionChange); let cleanup2 = addTextareaListener(editor, "focus", selectionChange); let cleanup3 = addTextareaListener(editor, "blur", selectionChange); selectionChange(); return () => { cleanup1(); cleanup2(); cleanup3(); highlight(true); }; }, [className, alwaysHighlight]); }; export { useHighlightMatchingTags, useHighlightTagPunctuation, useTagMatcher }; //# sourceMappingURL=match-tags.js.map