DEV: avoid rich editor input rule if preceded by backtick (#35528)

Adds an input rules wrapper to skip applying input rules whenever
there's a backtick ` preceding the regex match.
This commit is contained in:
Renato Atilio
2025-10-22 08:58:53 -03:00
committed by GitHub
parent 818ba9c941
commit 6cf72804ee
2 changed files with 52 additions and 5 deletions
@@ -20,7 +20,7 @@ export function buildInputRules(extensions, params, includeDefault = true) {
start: match[1].length,
});
rules.push(
const defaultRules = [
// TODO(renato) smartQuotes should respect `markdown_typographer_quotation_marks`
...smartQuotes,
...[
@@ -42,8 +42,10 @@ export function buildInputRules(extensions, params, includeDefault = true) {
/^(\u2013-|\u2014-|___\s|\*\*\*\s)$/,
horizontalRuleHandler,
{ inCodeMark: false }
)
);
),
];
rules.push(...defaultRules.map((rule) => processInputRule(rule, params)));
}
rules.push(...extractInputRules(extensions, params));
@@ -67,7 +69,11 @@ function processInputRule(inputRule, params) {
}
if (inputRule instanceof InputRule) {
return inputRule;
return new InputRule(
inputRule.match,
wrapHandlerWithBacktickCheck(inputRule.handler),
inputRule.options
);
}
if (
@@ -77,12 +83,41 @@ function processInputRule(inputRule, params) {
// Default to NOT applying input rules when inCodeMark
const options = inputRule.options || {};
options.inCodeMark ??= options.inCode || false;
return new InputRule(inputRule.match, inputRule.handler, options);
const handler = !options.inCodeMark
? wrapHandlerWithBacktickCheck(inputRule.handler)
: inputRule.handler;
return new InputRule(inputRule.match, handler, options);
}
throw new Error("Input rule must have a match regex and a handler function");
}
function hasBacktickBefore(state, pos) {
return pos > 0 && state.doc.textBetween(pos - 1, pos, "\n", "\n") === "`";
}
function wrapHandlerWithBacktickCheck(handler) {
return (state, match, start, end) => {
if (hasBacktickBefore(state, start)) {
return null;
}
// For two capturing group patterns like (^|\W)(:emoji:) or (^|\W)(@mention),
// also check for backtick before the actual content (after the boundary group)
if (
match[1] &&
match[2] &&
hasBacktickBefore(state, start + match[1].length)
) {
return null;
}
return handler(state, match, start, end);
};
}
function orderedListRule(nodeType) {
return wrappingInputRule(
/^(\d+)\.\s$/,
@@ -344,6 +344,18 @@ describe "Composer - ProseMirror editor", type: :system do
expect(rich).to have_css("code", text: "not code")
expect(rich).to have_no_css("code", text: "and this, not code")
end
it "doesn't apply input rules immediately after a single backtick" do
open_composer
composer.type_content("`**not bold**\n`:tada:")
expect(rich).to have_no_css("strong")
expect(rich).to have_no_css("img.emoji")
composer.toggle_rich_editor
expect(composer).to have_value("\\`\\*\\*not bold\\*\\*\n\n\\`:tada:")
end
end
context "with oneboxing" do