mirror of
https://github.com/discourse/discourse.git
synced 2025-04-26 08:34:28 +08:00
FIX: Do not autocomplete categories or emojis in code blocks (#8459)
This reapplies commit b643526d9a407b8abb826dba78a954cdfe6d6133 after being reverted in commit f65c4535556eeff24944369d6f262ef6be147eec. 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,
|
tinyAvatar,
|
||||||
formatUsername,
|
formatUsername,
|
||||||
clipboardData,
|
clipboardData,
|
||||||
safariHacksDisabled
|
safariHacksDisabled,
|
||||||
|
caretPosition,
|
||||||
|
inCodeBlock
|
||||||
} from "discourse/lib/utilities";
|
} from "discourse/lib/utilities";
|
||||||
import {
|
import {
|
||||||
validateUploadedFiles,
|
validateUploadedFiles,
|
||||||
@ -192,7 +194,9 @@ export default Component.extend({
|
|||||||
afterComplete() {
|
afterComplete() {
|
||||||
// ensures textarea scroll position is correct
|
// ensures textarea scroll position is correct
|
||||||
scheduleOnce("afterRender", () => $input.blur().focus());
|
scheduleOnce("afterRender", () => $input.blur().focus());
|
||||||
}
|
},
|
||||||
|
triggerRule: textarea =>
|
||||||
|
!inCodeBlock(textarea.value, caretPosition(textarea))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,9 @@ import { siteDir } from "discourse/lib/text-direction";
|
|||||||
import {
|
import {
|
||||||
determinePostReplaceSelection,
|
determinePostReplaceSelection,
|
||||||
clipboardData,
|
clipboardData,
|
||||||
safariHacksDisabled
|
safariHacksDisabled,
|
||||||
|
caretPosition,
|
||||||
|
inCodeBlock
|
||||||
} from "discourse/lib/utilities";
|
} from "discourse/lib/utilities";
|
||||||
import toMarkdown from "discourse/lib/to-markdown";
|
import toMarkdown from "discourse/lib/to-markdown";
|
||||||
import deprecated from "discourse-common/lib/deprecated";
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
@ -420,6 +422,10 @@ export default Component.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onKeyUp: (text, cp) => {
|
onKeyUp: (text, cp) => {
|
||||||
|
if (inCodeBlock(text, cp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const matches = /(?:^|[^a-z])(:(?!:).?[\w-]*:?(?!:)(?:t\d?)?:?) ?$/gi.exec(
|
const matches = /(?:^|[^a-z])(:(?!:).?[\w-]*:?(?!:)(?:t\d?)?:?) ?$/gi.exec(
|
||||||
text.substring(0, cp)
|
text.substring(0, cp)
|
||||||
);
|
);
|
||||||
@ -511,7 +517,10 @@ export default Component.extend({
|
|||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
|
triggerRule: textarea =>
|
||||||
|
!inCodeBlock(textarea.value, caretPosition(textarea))
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
export const SEPARATOR = ":";
|
export const SEPARATOR = ":";
|
||||||
import { caretRowCol } from "discourse/lib/utilities";
|
import {
|
||||||
|
caretRowCol,
|
||||||
|
caretPosition,
|
||||||
|
inCodeBlock
|
||||||
|
} from "discourse/lib/utilities";
|
||||||
|
|
||||||
export function replaceSpan($elem, categorySlug, categoryLink) {
|
export function replaceSpan($elem, categorySlug, categoryLink) {
|
||||||
$elem.replaceWith(
|
$elem.replaceWith(
|
||||||
@ -21,10 +25,14 @@ export function categoryHashtagTriggerRule(textarea, opts) {
|
|||||||
if (/^#{1}\w+/.test(line)) return false;
|
if (/^#{1}\w+/.test(line)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (col < 6) {
|
// Don't trigger autocomplete when ATX-style headers are used
|
||||||
// Don't trigger autocomplete when ATX-style headers are used
|
if (col < 6 && line.slice(0, col) === "#".repeat(col)) {
|
||||||
return line.slice(0, col) !== "#".repeat(col);
|
return false;
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inCodeBlock(textarea.value, caretPosition(textarea))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -410,5 +410,30 @@ export function rescueThemeError(name, error, api) {
|
|||||||
document.body.prepend(alertDiv);
|
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
|
// This prevents a mini racer crash
|
||||||
export default {};
|
export default {};
|
||||||
|
@ -9,7 +9,8 @@ import {
|
|||||||
setDefaultHomepage,
|
setDefaultHomepage,
|
||||||
caretRowCol,
|
caretRowCol,
|
||||||
setCaretPosition,
|
setCaretPosition,
|
||||||
fillMissingDates
|
fillMissingDates,
|
||||||
|
inCodeBlock
|
||||||
} from "discourse/lib/utilities";
|
} from "discourse/lib/utilities";
|
||||||
|
|
||||||
QUnit.module("lib:utilities");
|
QUnit.module("lib:utilities");
|
||||||
@ -186,3 +187,32 @@ QUnit.test("fillMissingDates", assert => {
|
|||||||
"it returns a JSON array with 31 dates"
|
"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…
x
Reference in New Issue
Block a user