prism-code-editor
Version:
Lightweight, extensible code editor component for the web using Prism
133 lines (132 loc) • 4.4 kB
JavaScript
import { n as getClosestToken, a as addTextareaListener, v as voidlessLangs, o as voidTags } from "../index-CxiLA9IO.js";
const createTagMatcher = (editor) => {
let pairMap = [];
let code;
let tags = [];
let tagIndex;
let sp;
let stack = [];
let matchTags2 = (tokens, language, value) => {
code = value;
tags.length = pairMap.length = tagIndex = sp = 0;
matchTagsRecursive(tokens, language, 0);
};
let 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 name = content[2] ? code.substr(position + openLen, content[1].length) : "";
const tagName = noVoidTags ? name : name.toLowerCase();
const notSelfClosing = content[content.length - 1].length < 2 && (noVoidTags || !voidTags.test(tagName));
if (content[2]) 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;
}
};
editor.on("tokenize", matchTags2);
matchTags2(editor.tokens, editor.options.language, editor.value);
return {
tags,
pairs: pairMap
};
};
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 matchTags = () => (editor) => {
let openEl, closeEl;
const { tags, pairs } = editor.extensions.matchTags ||= createTagMatcher(editor);
const highlight = (remove) => [openEl, closeEl].forEach((el) => {
el && el.classList.toggle("active-tagname", !remove);
});
editor.on("selectionChange", ([start, end]) => {
let newEl1;
let newEl2;
let index;
if (start == end && editor.focused) {
index = getClosestTagIndex(start, tags);
if (index + 1) {
index = pairs[index];
if (index + 1 && (newEl1 = getClosestToken(editor, ".tag>.tag"))) {
newEl2 = getClosestToken(editor, ".tag>.tag", 2, 0, tags[index][1]);
}
}
}
if (openEl != newEl1) {
highlight(true);
openEl = newEl1;
closeEl = newEl2;
highlight();
}
});
};
const highlightTagPunctuation = (className, alwaysHighlight) => (editor) => {
let openEl, closeEl;
const { tags } = editor.extensions.matchTags ||= createTagMatcher(editor);
const getPunctuation = (pos) => getClosestToken(editor, ".tag>.punctuation", 0, 0, pos);
const highlight = (remove) => [openEl, closeEl].forEach((el) => {
el && el.classList.toggle(className, !remove);
});
const selectionChange = () => {
let [start, end] = editor.getSelection();
let newEl1;
let newEl2;
if (start == end && editor.focused) {
let tag = tags[getClosestTagIndex(start, 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();
}
};
editor.on("selectionChange", selectionChange);
addTextareaListener(editor, "focus", selectionChange);
addTextareaListener(editor, "blur", selectionChange);
};
export {
createTagMatcher,
highlightTagPunctuation,
matchTags
};
//# sourceMappingURL=matchTags.js.map