How to ignore some part of text while typing

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