У подсветки синтаксиса проблемы с обновлением текста

#javascript #html

#javascript #HTML

Вопрос:

Я пытаюсь создать редактор кода с использованием HTML:

 <!DOCTYPE html>
<html lang='en'>
<head>
    <meta charset='UTF-8'>
    <meta http-equiv='X-UA-Compatible' content='IE=Edge'>
    <meta name='viewport' content='width=device-width, initial-scale=1'>
    <link rel='stylesheet' href='style.css'>
</head>

<body>
    <pre id='editor'><code contenteditable='true'></code></pre>
    
    <script type='module'>
        import { highlight } from './highlighter.js';
        import { Caret } from './caret.js';
        (() =>
        {
            const editor = document.querySelector('#editor code');
            const caret = new Caret(editor);
            highlight(editor);
            editor.addEventListener('input', e =>
            {
                highlight(editor);
                e.preventDefault();
            });
            editor.addEventListener('keydown', e =>
            {
                const TAB   = 9;
                const ENTER = 13;
                switch (e.keyCode)
                {
                    // ...
                }
            });
        })();
    </script>
</body>
</html>
 

highlighter.js:

 import { Caret } from './caret.js';
export function highlight(editor)
{
    // ...
    const NORM = '#E6E6FA';
    // ...
    const Highlighter = {
        source: editor.innerText,
        start: 0,
        curr: 0,
        // ...
        fin()
        {
            return this.curr >= this.source.length;
        },
        advance()
        {
            return this.source[this.curr  ];
        },
        // ...
        scan()
        {
            let result = '';
            this.start = this.curr;
            if (this.fin())
            {
                return null;
            }
            const char = this.advance();
            let color = NORM;
            switch (char)
            {
                // ...
            }
            return {
                color,
                text: this.source.substring(this.start, this.curr),
            };
        },
    };
    let result = '';
    const caret = new Caret(editor);
    const save = caret.getPos();
    for (;;)
    {
        const lexeme = Highlighter.scan();
        if (lexeme === null)
        {
            break;
        }
        const chars = lexeme.text.split('').map(
            x => `<span style='color: ${lexeme.color};'>${x}</span>`);
        result  = chars.join('');
    }
    editor.innerHTML = resu<
    caret.setPos(save);
};
 

<span><span>По сути, он берет текстовое содержимое пользовательского кода, сканирует его для создания лексем с данными о цвете, разбивает эти лексемы на символы, которые помещаются в теги с цветами, затем добавляет эти s к строке, для которой обновляется innerHTML редактора, и, наконец, курсор пользователя перемещаетсяверните его в правильное положение; это делается при вводе. Однако есть одна проблема: если пользователь вводит слишком быстро, введенный им текст может быть удвоен. Я пытался исправить это с помощью других типов прослушивателей событий и пытался просто использовать setInterval, но это не сработало вообще.

Ответ №1:

Из того, что вы описали

если пользователь вводит текст слишком быстро

и видя, что дорогостоящая в вычислительном highlight() отношении функция вызывается при каждом изменении input элемента

 editor.addEventListener('input', e => {
  highlight(editor); // <--
  e.preventDefault();
});
 

Я бы предложил отменить этот призыв, чтобы выделить его. Вот хорошее объяснение отмены.

Попробуйте что-то вроде этого:

 // Vanilla debounce: https://gist.github.com/peduarte/7ee475dd0fae1940f857582ecbb9dc5f

function debounce(func, wait = 100) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, args);
    }, wait);
  };
}

// ...

// adjust delay to find a balance between responsiveness and performance
const delay = 500; 

const runHighlight = () => highlight(editor);
const debouncedHighlight = debounce(runHighlight, delay);

editor.addEventListener('input', e => {
  e.preventDefault();
  debouncedHighlight();
});
 

Комментарии:

1. Спасибо, это решение помогло.