FIX: Do not autocomplete categories or emojis in code blocks (#8459)
This reapplies commitb643526d9a
after being reverted in commitf65c453555
. Unlike the original commit, this does a single pass and does not take into account unfinished code blocks.
This commit is contained in:
parent
192ada0067
commit
f62b8990ac
|
@ -33,7 +33,9 @@ import {
|
|||
tinyAvatar,
|
||||
formatUsername,
|
||||
clipboardData,
|
||||
safariHacksDisabled
|
||||
safariHacksDisabled,
|
||||
caretPosition,
|
||||
inCodeBlock
|
||||
} from "discourse/lib/utilities";
|
||||
import {
|
||||
validateUploadedFiles,
|
||||
|
@ -192,7 +194,9 @@ export default Component.extend({
|
|||
afterComplete() {
|
||||
// ensures textarea scroll position is correct
|
||||
scheduleOnce("afterRender", () => $input.blur().focus());
|
||||
}
|
||||
},
|
||||
triggerRule: textarea =>
|
||||
!inCodeBlock(textarea.value, caretPosition(textarea))
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,9 @@ import { siteDir } from "discourse/lib/text-direction";
|
|||
import {
|
||||
determinePostReplaceSelection,
|
||||
clipboardData,
|
||||
safariHacksDisabled
|
||||
safariHacksDisabled,
|
||||
caretPosition,
|
||||
inCodeBlock
|
||||
} from "discourse/lib/utilities";
|
||||
import toMarkdown from "discourse/lib/to-markdown";
|
||||
import deprecated from "discourse-common/lib/deprecated";
|
||||
|
@ -420,6 +422,10 @@ export default Component.extend({
|
|||
},
|
||||
|
||||
onKeyUp: (text, cp) => {
|
||||
if (inCodeBlock(text, cp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const matches = /(?:^|[^a-z])(:(?!:).?[\w-]*:?(?!:)(?:t\d?)?:?) ?$/gi.exec(
|
||||
text.substring(0, cp)
|
||||
);
|
||||
|
@ -511,7 +517,10 @@ export default Component.extend({
|
|||
}
|
||||
return list;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
triggerRule: textarea =>
|
||||
!inCodeBlock(textarea.value, caretPosition(textarea))
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
export const SEPARATOR = ":";
|
||||
import { caretRowCol } from "discourse/lib/utilities";
|
||||
import {
|
||||
caretRowCol,
|
||||
caretPosition,
|
||||
inCodeBlock
|
||||
} from "discourse/lib/utilities";
|
||||
|
||||
export function replaceSpan($elem, categorySlug, categoryLink) {
|
||||
$elem.replaceWith(
|
||||
|
@ -21,10 +25,14 @@ export function categoryHashtagTriggerRule(textarea, opts) {
|
|||
if (/^#{1}\w+/.test(line)) return false;
|
||||
}
|
||||
|
||||
if (col < 6) {
|
||||
// Don't trigger autocomplete when ATX-style headers are used
|
||||
return line.slice(0, col) !== "#".repeat(col);
|
||||
} else {
|
||||
return true;
|
||||
// Don't trigger autocomplete when ATX-style headers are used
|
||||
if (col < 6 && line.slice(0, col) === "#".repeat(col)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inCodeBlock(textarea.value, caretPosition(textarea))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -410,5 +410,30 @@ export function rescueThemeError(name, error, api) {
|
|||
document.body.prepend(alertDiv);
|
||||
}
|
||||
|
||||
const CODE_BLOCKS_REGEX = /^( |\t).*|`[^`]+`|^```[^]*?^```|\[code\][^]*?\[\/code\]/gm;
|
||||
// | ^ | ^ | ^ | ^ |
|
||||
// | | | |
|
||||
// | | | code blocks between [code]
|
||||
// | | |
|
||||
// | | +--- code blocks between three backquote
|
||||
// | |
|
||||
// | +----- inline code between backquotes
|
||||
// |
|
||||
// +------- paragraphs starting with 4 spaces or tab
|
||||
|
||||
export function inCodeBlock(text, pos) {
|
||||
const matches = text.matchAll(CODE_BLOCKS_REGEX);
|
||||
|
||||
for (const match of matches) {
|
||||
const begin = match.index;
|
||||
const end = match.index + match[0].length;
|
||||
if (begin <= pos && pos <= end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// This prevents a mini racer crash
|
||||
export default {};
|
||||
|
|
|
@ -9,7 +9,8 @@ import {
|
|||
setDefaultHomepage,
|
||||
caretRowCol,
|
||||
setCaretPosition,
|
||||
fillMissingDates
|
||||
fillMissingDates,
|
||||
inCodeBlock
|
||||
} from "discourse/lib/utilities";
|
||||
|
||||
QUnit.module("lib:utilities");
|
||||
|
@ -186,3 +187,32 @@ QUnit.test("fillMissingDates", assert => {
|
|||
"it returns a JSON array with 31 dates"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("inCodeBlock", assert => {
|
||||
const text =
|
||||
"000\n\n```\n111\n```\n\n000\n\n`111 111`\n\n000\n\n[code]\n111\n[/code]\n\n 111\n\t111\n\n000`000";
|
||||
for (let i = 0; i < text.length; ++i) {
|
||||
if (text[i] === "0") {
|
||||
assert.notOk(inCodeBlock(text, i), `position ${i} is not in code block`);
|
||||
} else if (text[i] === "1") {
|
||||
assert.ok(inCodeBlock(text, i), `position ${i} is in code block`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.skip("inCodeBlock - runs fast", assert => {
|
||||
const phrase = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
|
||||
const text = `${phrase}\n\n\`\`\`\n${phrase}\n\`\`\`\n\n${phrase}\n\n\`${phrase}\n${phrase}\n\n${phrase}\n\n[code]\n${phrase}\n[/code]\n\n${phrase}\n\n ${phrase}\n\n\`${phrase}\`\n\n${phrase}`;
|
||||
|
||||
let time = Number.MAX_VALUE;
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
const start = performance.now();
|
||||
inCodeBlock(text, text.length);
|
||||
const end = performance.now();
|
||||
time = Math.min(time, end - start);
|
||||
}
|
||||
|
||||
// This runs in 'keyUp' event handler so it should run as fast as
|
||||
// possible. It should take less than 1ms for the test text.
|
||||
assert.ok(time < 10);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue