Try adding the following into a script tag before the footer (by enqueueing it according to the correct procedure):
var codeAreas = document.querySelectorAll('code.block-editor-rich-text__editable');
if (codeAreas) {
for (area of codeAreas) {
area.addEventListener('keydown', (e) => {
var TABKEY = 9;
if (e.keyCode == TABKEY) {
if (e.preventDefault) {
e.preventDefault();
}
var text = "\t";
var selection = document.getSelection();
var range = selection.getRangeAt(0);
var startPos = range.startOffset;
var endPos = range.endOffset;
area.innerText = area.innerText.substring(0, startPos)
+ text
+ area.innerText.substring(endPos, area.innerText.length);
range = document.createRange();
range.setStart(area.firstChild, endPos + text.length);
selection.removeAllRanges();
selection.addRange(range);
return false;
}
}
, false);
}
}
I'll try to break down what each part does.
First, of course we find any editable code tags if they exist and add a keydown event.
var codeAreas = document.querySelectorAll('code.block-editor-rich-text__editable');
if (codeAreas) {
for (area of codeAreas) {
area.addEventListener('keydown', (e) => {
...
We check if it's a tab
...
var TABKEY = 9;
if(e.keyCode == TABKEY) {
...
if it is, first we prevent the default behavior of the keydown event (selecting the next element)
...
if (e.preventDefault) {
e.preventDefault();
}
...
then set the text we want inserted – in this case a tab, but could be four spaces or anything else – and also get the position of the current selection. If nothing is highlighted, the start and end of the selection range will be the same
...
var text = " ";
var selection = document.getSelection();
var range = selection.getRangeAt(0);
var startPos = range.startOffset;
var endPos = range.endOffset;
...
Now we insert our text at the cursor,
...
area.innerText = area.innerText.substring(0, startPos)
+ text
+ area.innerText.substring(endPos, area.innerText.length);
...
then we reset our selection to the end of the inserted text
...
range = document.createRange();
range.setStart(area.firstChild, endPos + text.length);
selection.removeAllRanges();
selection.addRange(range);
...
then we return false just in case, so the event surely doesn't fire other handlers