FIX: Do not autocomplete categories or emojis in code blocks (#8459)

This reapplies commit b643526d9a after
being reverted in commit f65c453555.

Unlike the original commit, this does a single pass and does not take
into account unfinished code blocks.
This commit is contained in:
Dan Ungureanu 2019-12-09 15:07:15 +02:00 committed by GitHub
parent 192ada0067
commit f62b8990ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 11 deletions

View File

@ -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))
});
}

View File

@ -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))
});
},

View File

@ -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;
}

View File

@ -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 {};

View File

@ -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);
});