[FIX] web_editor: handle backwards backtick conversion to inline code
When inserting a backtick in text that has another backtick before (case 1) or after (case 2) it, we convert the text contained between said backticks into inline code. Case 2 was not tested, and didn't work properly. closes odoo/odoo#110784 X-original-commit: cc2a4a960d6d83362e35abc8a253e95105fd07f9 Signed-off-by: David Monjoie (dmo) <dmo@odoo.com> Signed-off-by: Antoine Guenet (age) <age@odoo.com>
This commit is contained in:
@@ -3186,7 +3186,7 @@ export class OdooEditor extends EventTarget {
|
||||
}
|
||||
if (ev.data === '`' && !closestElement(selection.anchorNode, 'code')) {
|
||||
// We just inserted a backtick, check if there was another
|
||||
// one before.
|
||||
// one in the text.
|
||||
const range = getDeepRange(this.editable);
|
||||
let textNode = range.startContainer;
|
||||
let offset = range.startOffset;
|
||||
@@ -3205,16 +3205,30 @@ export class OdooEditor extends EventTarget {
|
||||
sibling = textNode.nextSibling;
|
||||
}
|
||||
setSelection(textNode, offset);
|
||||
const textHasOpeningTick = /`.*`/.test(textNode.textContent);
|
||||
if (textHasOpeningTick) {
|
||||
const textHasTwoTicks = /`.*`/.test(textNode.textContent);
|
||||
if (textHasTwoTicks) {
|
||||
this.historyStep();
|
||||
if (offset !== textNode.textContent.length) {
|
||||
splitTextNode(textNode, offset);
|
||||
textNode = textNode.previousSibling;
|
||||
const insertedBacktickIndex = offset - 1;
|
||||
const textBeforeInsertedBacktick = textNode.textContent.substring(0, insertedBacktickIndex - 1);
|
||||
let startOffset, endOffset;
|
||||
const isClosingForward = textBeforeInsertedBacktick.includes('`');
|
||||
if (isClosingForward) {
|
||||
// There is a backtick before the new backtick.
|
||||
startOffset = textBeforeInsertedBacktick.lastIndexOf('`');
|
||||
endOffset = insertedBacktickIndex;
|
||||
} else {
|
||||
// There is a backtick after the new backtick.
|
||||
const textAfterInsertedBacktick = textNode.textContent.substring(offset);
|
||||
startOffset = insertedBacktickIndex;
|
||||
endOffset = offset + textAfterInsertedBacktick.indexOf('`');
|
||||
}
|
||||
const openingTickOffset = textNode.textContent.substring(0, textNode.textContent.length - 1).lastIndexOf('`');
|
||||
if (openingTickOffset) {
|
||||
splitTextNode(textNode, openingTickOffset);
|
||||
// Split around the backticks if needed so text starts
|
||||
// and ends with a backtick.
|
||||
if (endOffset && endOffset < textNode.textContent.length) {
|
||||
splitTextNode(textNode, endOffset + 1, DIRECTIONS.LEFT);
|
||||
}
|
||||
if (startOffset) {
|
||||
splitTextNode(textNode, startOffset);
|
||||
}
|
||||
// Remove ticks.
|
||||
textNode.textContent = textNode.textContent.substring(1, textNode.textContent.length - 1);
|
||||
@@ -3226,9 +3240,13 @@ export class OdooEditor extends EventTarget {
|
||||
if (!codeElement.previousSibling || codeElement.previousSibling.nodeType !== Node.TEXT_NODE) {
|
||||
codeElement.before(document.createTextNode('\u200B'));
|
||||
}
|
||||
// Move selection out of code element.
|
||||
codeElement.after(document.createTextNode('\u200B'));
|
||||
setSelection(codeElement.nextSibling, 1);
|
||||
if (isClosingForward) {
|
||||
// Move selection out of code element.
|
||||
codeElement.after(document.createTextNode('\u200B'));
|
||||
setSelection(codeElement.nextSibling, 1);
|
||||
} else {
|
||||
setSelection(codeElement.firstChild, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.historyStep();
|
||||
|
||||
@@ -5684,6 +5684,12 @@ X[]
|
||||
stepFunction: async editor => insertText(editor, '`'),
|
||||
contentAfter: '<p>\u200B<code class="o_inline_code">ab</code>\u200B[]cd</p>',
|
||||
});
|
||||
// BACKWARDS
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>[]ab`cd</p>',
|
||||
stepFunction: async editor => insertText(editor, '`'),
|
||||
contentAfter: '<p>\u200B<code class="o_inline_code">[]ab</code>cd</p>',
|
||||
});
|
||||
});
|
||||
it('should convert text into inline code (middle)', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
@@ -5691,6 +5697,12 @@ X[]
|
||||
stepFunction: async editor => insertText(editor, '`'),
|
||||
contentAfter: '<p>ab<code class="o_inline_code">cd</code>\u200B[]ef</p>',
|
||||
});
|
||||
// BACKWARDS
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>ab[]cd`ef</p>',
|
||||
stepFunction: async editor => insertText(editor, '`'),
|
||||
contentAfter: '<p>ab<code class="o_inline_code">[]cd</code>ef</p>',
|
||||
});
|
||||
});
|
||||
it('should convert text into inline code (end)', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
@@ -5698,13 +5710,26 @@ X[]
|
||||
stepFunction: async editor => insertText(editor, '`'),
|
||||
contentAfter: '<p>ab<code class="o_inline_code">cd</code>\u200B[]</p>',
|
||||
});
|
||||
// BACKWARDS
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>ab[]cd`</p>',
|
||||
stepFunction: async editor => insertText(editor, '`'),
|
||||
contentAfter: '<p>ab<code class="o_inline_code">[]cd</code></p>',
|
||||
});
|
||||
});
|
||||
it('should convert text into inline code and leave an earlier and a later backtick alone', async () => {
|
||||
it('should convert text into inline code, with parasite backticks', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>a`b`cd[]e`f</p>',
|
||||
stepFunction: async editor => insertText(editor, '`'),
|
||||
// The closest PREVIOUS backtick is prioritary
|
||||
contentAfter: '<p>a`b<code class="o_inline_code">cd</code>\u200B[]e`f</p>',
|
||||
});
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>ab[]cd`e`f</p>',
|
||||
stepFunction: async editor => insertText(editor, '`'),
|
||||
// If there is no previous backtick, use the closest NEXT backtick.
|
||||
contentAfter: '<p>ab<code class="o_inline_code">[]cd</code>e`f</p>',
|
||||
});
|
||||
});
|
||||
it('should not convert text into inline code when traversing HTMLElements', async () => {
|
||||
await testEditor(BasicEditor, {
|
||||
@@ -5758,6 +5783,7 @@ X[]
|
||||
});
|
||||
});
|
||||
it('should convert text into inline code even when the other backtick is in a separate text node', async () => {
|
||||
// BACKTICK IS PREVIOUS SIBLING
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>ab[]c</p>',
|
||||
stepFunction: async editor => {
|
||||
@@ -5766,6 +5792,15 @@ X[]
|
||||
},
|
||||
contentAfter: '<p>\u200B<code class="o_inline_code">ab</code>\u200B[]c</p>',
|
||||
});
|
||||
// BACKTICK IS NEXT SIBLING
|
||||
await testEditor(BasicEditor, {
|
||||
contentBefore: '<p>ab[]c</p>',
|
||||
stepFunction: async editor => {
|
||||
editor.document.getSelection().anchorNode.after(document.createTextNode('`'));
|
||||
insertText(editor, '`');
|
||||
},
|
||||
contentAfter: '<p>ab<code class="o_inline_code">[]c</code></p>',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user