Jeśli masz już [contenteditable], to nie dokładałbym do tego dodatkowo ukrytego textarea. Samo zrobienie dobrze synchronizacji między tymi dwoma elementami jest czymś ekstremalnie trudnym – na tyle, że powstaje całe nowe API, które to próbuje eliminować. Mimo wszystko szedłbym w stronę próby operowania bezpośrednio na tym div, bez przerzucania selekcji i focusu do innego, ukrytego elementu.
VSCode, co prawda, ma urkyty textarea, ale to, co widzi użytkownik, nie jest [contenteditable]. To jest całkowicie niestandardowa implementacja edytora, w której zarówno selekcja, jak i kursor, są tworzone przy pomocy kodu. Widać to choćby po tym, że jest możliwość wstawiania kilku kursorów naraz lub zaznaczania kilku fragmentów tekstu naraz – coś, na co przeglądarki nie pozwalają. Zamiast próbować wzorować się na aż tak skomplikowanym edytorze, raczej bym spojrzał, jak to robi choćby CodeMirror – zwłaszcza, że jego źródła są na GitHubie. I tak, jest to zdecydowanie mniej skomplikowana rzecz od VSCode, ale wciąż jest niezwykle skomplikowana.
Ogólnie tego typu edytory składają się z dwóch części: modelu (stanu) oraz widoku. Model to ich wewnętrzna struktura danych, która przechowuje treść, dodatkowe ustawienia, różne informacje o stylowaniu poszczególnych części itd. Widok z kolei jest odpowiedzialny za wyświetlanie modelu użytkownikowi. To on odpowiada za selekcję, generowanie HTML-a, który będzie odzwierciedlał informacje w modelu, itd. Do tego dochodzi cała warstwa czegoś, co można nazwać kontrolerem, która będzie dbała o to, by przechwytywać zdarzenia z widoku, mielić wpisywaną przez użytkownika treść do modelu oraz następnie aktualizować widok. Przykład:
- User wpisuje "function" w widoku.
- Kontroler (słuchający np. na zdarzeniu input) wyłapuje to i przesyła do modelu.
- Model to parsuje (np. filtruje usuwając zbędne stylowanie) i zapisuje w sensownej formie.
- Kontroler aktualizuje widok w oparciu o nową postać modelu, czyli np. "function" jest odpowiednio pokolorowane.
No i trzeba dbać przy tym, żeby nam się selekcja nie rozjechała po aktualizacji widoku, bo byłoby średnio, gdyby nagle po pokolorowaniu "function" wyleciała gdzieś w kosmos.
Jest też drugie podejście, starsze i (w teorii) prostsze: robienie wszystkiego bezpośrednio na DOM. W tym podejściu widok i model to jedno, bo jedynym źródłem prawdy jest zawartość naszego elementu [contenteditable]. I tutaj problem sprowadza się do wyszukiwania odpowiednich słów kluczowych w treści – co samo w sobie nie jest proste.
Samo kolorowanie składni to zupełnie inny problem. Są dwie szkoły: regexy i AST. W VSC używane są regexy – tyle że w składni TextMate'a. I ten sposób wydaje się prostszy, bo mamy do czynienia wyłącznie z tekstem przez cały czas. No ale właśnie: traktowanie kodu jako tekstu w niektórych brzegowych przypadkach może generować niepoprawne rezultaty.
Dlatego istnieje drugi sposób: parsowanie poszczególnych języków do AST (struktury drzewiastej podobnej do DOM), co pozwala na wykrycie poszczególnych typów tokenów (zmiennych, ciągów tekstowych, słów kluczowych itd.). CodeMirror ma do tego cały podprojekt, Lezer. I każdy język tak naprawdę ma swój własny parser, ponieważ każdy ma inną gramatykę, więc inne zasady parsowania, co ostatecznie przekłada się na osobne sposoby kolorowania składni.
I teraz wracamy do architektury takiego edytora: kolorowanie jest na tyle skomplikowanym zadaniem, że o wiele lepiej pasuje do edytora, w którym model i widok są od siebie oddzielone. Zwłaszcza, że przeglądarki lubią dodawać sporo swojego "fluffu" do [contenteditable] (np niełamliwe spacje w losowych miejscach czy br czające się na końcach linii/pustych elementów), który może przeszkadzać w sensownym parsowaniu i kolorowaniu składni. A model jest (powinien być) zaprojektowany tak, żeby zawsze trzymać czyste dane i to w poprawnej formie. I mając taką zawsze poprawną strukturę danych, możemy bez problemu parsować jej zawartość.
Pomijam tutaj już wszystkie kwestie wydajnościowe, czyli np. kolorowanie w workerze czy robienie tego fragmentami, żeby za każdym razem nie przekolorowywać całej zawartości edytora.
Ogólnie: to jest zabawa na długie miesiące, jak nie lata (zwłaszcza, jeśli to Twoje pierwsze zetknięcie się z [contenteditable] i ogólnie edytowaniem tekstu w przeglądarce).