Hi,
I’ve a CodeMirror area, where (when I type every char) it match some patterns, and in some case, add a specific text that I want to hide to user.
In the specific case, when there is a {someText}(), it adds a [{guid}] placeholder.
So if I have muFunction and I type “(”, it automatically add “)” with autocompletition (native codeMirror), than check if there is the [{guid}] placeholder, if not add it, otherwise do nothing.
Basically, heres the html using autocompletiton, decorations and type “(”):
.myFunction
<span class="cm-matchingBracket">(</span>
<span class="cm-hidden-guid">[e8a5617e-264a-4019-a205-aeffa5cd3bc3]</span>
<span class="cm-matchingBracket">)</span>
Now, here’s the tricky part. What I want to do is that every position a user set the cursor, it must position always after the [{guid}] placeholder, and start editing the text after it.
At the same moment, if someone “delete” the (, the placeholder must vanish.
Here’s my attempt:
function skipTypingInsideGuid(tr: Transaction): Transaction | [Transaction, TransactionSpec] {
// Only handle user text input
if (!tr.docChanged || tr.annotation(Transaction.userEvent) !== "input.type") return tr;
const doc = tr.startState.doc.toString();
const sel = tr.startState.selection.main;
const pos = sel.head;
const guidRegex = /\([\[{]?[0-9a-fA-F-]{36}[\]}]?\)/g;
let m;
while ((m = guidRegex.exec(doc))) {
const start = m.index;
const end = start + m[0].length;
if (pos > start && pos < end) {
// Redirect insertion to just after the GUID block (before closing bracket)
const newPos = end - 1;
return [
tr,
{
changes: { from: pos, to: pos, insert: tr.newDoc.sliceString(pos, tr.newDoc.length) },
selection: EditorSelection.cursor(newPos + 1)
}
];
}
}
return tr;
}
function deleteGuidGroup(tr: Transaction): Transaction | [Transaction, TransactionSpec] {
if (!tr.docChanged) return tr
if (!tr.isUserEvent("delete.backward") && !tr.isUserEvent("delete.forward")) return tr
const doc = tr.startState.doc.toString()
const sel = tr.startState.selection.main
const pos = sel.head
// Check if deletion hits inside guid pattern
const guidRegex = /\([\[{]?[0-9a-fA-F-]{36}[\]}]?\)/g
let m
while ((m = guidRegex.exec(doc))) {
const start = m.index
const end = start + m[0].length
if (pos > start && pos < end) {
// Replace entire group with empty string
return [
tr,
{
changes: { from: start, to: end, insert: "" },
selection: EditorSelection.cursor(start)
}
]
}
}
return tr
}
StateField.define<DecorationSet>({
create() {
return Decoration.none;
},
update(highlights, tr) {
highlights = highlights.map(tr.changes);
const decorations: Range<Decoration>[] = [];
const text = tr.state.doc.toString();
// paths colors (Root/Childs)
const pathsColorsRootNodeSelector = Decoration.mark({ class: "cm-rootNodeSelector" });
const pathsColorsChildsNodeSelector = Decoration.mark({ class: "cm-childsNodeSelector" });
const pathColorsRegex = /\$\(['"](.*?)['"]\)/g;
let match;
while ((match = pathColorsRegex.exec(text)) !== null) {
const start = match.index;
const end = match.index + match[0].length;
decorations.push(pathsColorsRootNodeSelector.range(start, end));
let currentPos = end;
const methodStartRegex = /\.(\w+)\(/g;
methodStartRegex.lastIndex = currentPos;
const methodMatch = methodStartRegex.exec(text);
if (methodMatch && methodMatch.index === currentPos) {
const methodStart = methodMatch.index;
const methodEnd = CustomTextArea.findNodeEnd(text, methodMatch.index + methodMatch[0].length - 1);
decorations.push(pathsColorsChildsNodeSelector.range(methodStart, methodEnd));
}
}
// hide nodes guid
const hideNodesGuidSelector = Decoration.mark({ class: "cm-hidden-guid" });
const hideNodesGuidRegex = /\[\{?[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}?\]/g;
let gmatch;
while ((gmatch = hideNodesGuidRegex.exec(text)) !== null) {
const gstart = gmatch.index;
const gend = gstart + gmatch[0].length;
decorations.push(hideNodesGuidSelector.range(gstart, gend));
}
decorations.sort((a, b) => a.from - b.from);
return RangeSet.of(decorations);
},
provide: f => EditorView.decorations.from(f)
}),
EditorState.transactionFilter.of(skipTypingInsideGuid),
EditorState.transactionFilter.of(deleteGuidGroup)
but it a mess, doesnt works. What’s wrong? What’s the best practice on doing this?
Thanks