CodeMirror z sprawdzaniem pisowni

Chciałbym skorzystać z funkcjonalności CodeMirror (np. linenumbering, wrapping, search, etc.) dla zwykłego tekstu, bez szczególnej potrzeby rozświetlania kodu, ale zamiast tego z aktywowanym sprawdzaniem pisowni Google Chrome lub innym naturalnym językiem (zwłaszcza angielskim) (nie muszę mieć tego na innych przeglądarkach). Jak mogę to zrobić? Czy można napisać dodatek w trybie tekstowym, który umożliwia sprawdzanie pisowni?

Author: sawa, 2012-09-10

5 answers

Faktycznie zintegrowałem literówka.js z CodeMirror podczas kodowania dla NoTex.ch ; możesz rzucić na niego okiem tutaj CodeMirror.odpoczywaj.JS; potrzebowałem sposobu na sprawdzenie pisowni reStructuredText, a ponieważ korzystam z doskonałych możliwości podświetlania składni programu CodeMirror, było to całkiem proste.

Możesz sprawdzić kod pod podanym linkiem, ale podsumuję to, co mam zrobione:

  1. Zainicjalizuj literówkę.biblioteka js; Zobacz także blog/dokumentację autora:

    var typo = new Typo ("en_US", AFF_DATA, DIC_DATA, {
        platform: 'any'
    });
    
  2. Zdefiniuj Wyrażenie regularne dla separatorów wyrazów:

    var rx_word = "!\"#$%&()*+,-./:;<=>?@[\\\\\\]^_`{|}~";
    
  3. Zdefiniuj tryb nakładki dla CodeMirror:

    CodeMirror.defineMode ("myoverlay", function (config, parserConfig) {
        var overlay = {
            token: function (stream, state) {
    
                if (stream.match (rx_word) &&
                    typo && !typo.check (stream.current ()))
    
                    return "spell-error"; //CSS class: cm-spell-error
    
                while (stream.next () != null) {
                    if (stream.match (rx_word, false)) return null;
                }
    
                return null;
            }
        };
    
        var mode = CodeMirror.getMode (
            config, parserConfig.backdrop || "text/x-myoverlay"
        );
    
        return CodeMirror.overlayMode (mode, overlay);
    });
    
  4. Użyj nakładki z CodeMirror; zobacz instrukcję obsługi, aby dowiedzieć się, jak dokładnie to zrobić. Zrobilem to w kodzie wiec mozna tam tez sprawdzic, ale polecam uzytkownika Instrukcja obsługi.

  5. Define CSS class:

    .CodeMirror .cm-spell-error {
         background: url(images/red-wavy-underline.gif) bottom repeat-x;
    }
    

To podejście sprawdza się doskonale w języku niemieckim, angielskim i hiszpańskim. Ze słownikiem francuskim literówka.js wydaje się mieć pewne problemy (akcent), a języki takie jak hebrajski, węgierski i włoski-gdzie liczba afiksów jest długa lub słownik jest dość obszerny - to nie działa naprawdę, ponieważ literówka.js w swojej obecnej implementacji zużywa zbyt dużo pamięci i jest zbyt wolny.

Z Niemieckim literówka.js może zablokować maszynę wirtualną JavaScript na kilkaset milisekund (ale tylko podczas inicjalizacji!), więc warto rozważyć wątki w tle z robotnikami sieciowymi HTML5 (zobacz CodeMirror.literówka.pracownik.js dla przykładu). Dalej literówka.js nie wydaje się obsługiwać Unicode (ze względu na ograniczenia JavaScript): przynajmniej nie udało mi się go uruchomić z językami niełacińskimi, takimi jak Rosyjski, Grecki, Hindi itp.

Nie refakturowałem opisane rozwiązanie do ładnego osobnego projektu oprócz (teraz dość dużego) NoTex.ch, ale mogę to zrobić dość szybko; do tego czasu trzeba łatać własne rozwiązanie oparte na powyższym opisie lub kodzie podpowiedzi. Mam nadzieję, że to pomoże.

 26
Author: hsk81,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-20 16:39:13

To jest robocza wersja odpowiedzi hsk81. Używa trybu nakładki CodeMirror i szuka dowolnego słowa wewnątrz cudzysłowów, znaczników html itp. Ma przykładową literówkę.sprawdź, że powinno być zastąpione czymś w rodzaju literówki.js. Podkreśla nieznane słowa czerwoną, skośną kreską.

To zostało przetestowane przy użyciu komórki IPython % % html.

<style>
.CodeMirror .cm-spell-error {
     background: url("https://raw.githubusercontent.com/jwulf/typojs-project/master/public/images/red-wavy-underline.gif") bottom repeat-x;
}
</style>

<h2>Overlay Parser Demo</h2>
<form><textarea id="code" name="code">
</textarea></form>

<script>
var typo = { check: function(current) {
                var dictionary = {"apple": 1, "banana":1, "can't":1, "this":1, "that":1, "the":1};
                return current.toLowerCase() in dictionary;
            }
}

CodeMirror.defineMode("spell-check", function(config, parserConfig) {
    var rx_word = new RegExp("[^\!\"\#\$\%\&\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~\ ]");
    var spellOverlay = {
        token: function (stream, state) {
          var ch;
          if (stream.match(rx_word)) { 
            while ((ch = stream.peek()) != null) {
                  if (!ch.match(rx_word)) {
                    break;
                  }
                  stream.next();
            }
            if (!typo.check(stream.current()))
                return "spell-error";
            return null;
          }
          while (stream.next() != null && !stream.match(rx_word, false)) {}
          return null;
        }
    };

  return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "text/html"), spellOverlay);
});

var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "spell-check"});
</script>
 3
Author: Doug Blank,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-16 12:16:15

CodeMirror nie jest oparty na textarea HTML, więc nie można użyć wbudowanego sprawdzania pisowni

Możesz zaimplementować własne sprawdzanie pisowni dla CodeMirror za pomocą czegoś w rodzaju literówka.js

Chyba nikt jeszcze tego nie zrobił.

 1
Author: Doug,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-14 03:06:58

Jakiś czas temu napisałem kontroler pisowni typu squiggly podkreślenie. To wymaga przepisania, aby być szczerym, byłem wtedy bardzo nowy w JavaScript. Ale zasady są tam wszystkie.

Https://github.com/jameswestgate/SpellAsYouType

 1
Author: James Westgate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-17 10:46:53

Stworzyłem spellchecker z sugestiami literówek/poprawkami:

Https://gist.github.com/kofifus/4b2f79cadc871a29439d919692099406

Demo: https://plnkr.co/edit/0y1wCHXx3k3mZaHFOpHT

Poniżej znajdują się odpowiednie części kodu:

Najpierw obiecuję załadować słowniki. Używam literówek.js dla słownika, ładowanie może chwilę potrwać, jeśli nie są one hostowane lokalnie, więc lepiej jest rozpocząć ładowanie tak szybko, jak zaczyna się przed login / cm inicjalizacja etc:

function loadTypo() {
    // hosting the dicts on your local domain will give much faster results
    const affDict='https://rawgit.com/ropensci/hunspell/master/inst/dict/en_US.aff';
    const dicDict='https://rawgit.com/ropensci/hunspell/master/inst/dict/en_US.dic';

    return new Promise(function(resolve, reject) {
        var xhr_aff = new XMLHttpRequest();
        xhr_aff.open('GET', affDict, true);
        xhr_aff.onload = function() {
            if (xhr_aff.readyState === 4 && xhr_aff.status === 200) {
                //console.log('aff loaded');
                var xhr_dic = new XMLHttpRequest();
                xhr_dic.open('GET', dicDict, true);
                xhr_dic.onload = function() {
                    if (xhr_dic.readyState === 4 && xhr_dic.status === 200) {
                        //console.log('dic loaded');
                        resolve(new Typo('en_US', xhr_aff.responseText, xhr_dic.responseText, { platform: 'any' }));
                    } else {
                        console.log('failed loading aff');
                        reject();
                    }
                };
                //console.log('loading dic');
                xhr_dic.send(null);
            } else {
                console.log('failed loading aff');
                reject();
            }
        };
        //console.log('loading aff');
        xhr_aff.send(null);
    });
}

Po Drugie dodaję nakładkę do wykrywania i oznaczania literówek w ten sposób:

cm.spellcheckOverlay={
    token: function(stream) {
        var ch = stream.peek();
        var word = "";

        if (rx_word.includes(ch) || ch==='\uE000' || ch==='\uE001') {
            stream.next();
            return null;
        }

        while ((ch = stream.peek()) && !rx_word.includes(ch)) {
            word += ch;
            stream.next();
        }

        if (! /[a-z]/i.test(word)) return null; // no letters
        if (startSpellCheck.ignoreDict[word]) return null;
        if (!typo.check(word)) return "spell-error"; // CSS class: cm-spell-error
    }
}
cm.addOverlay(cm.spellcheckOverlay);

Po Trzecie używam listy do wyświetlania sugestii i poprawiania literówek:

function getSuggestionBox(typo) {
    function sboxShow(cm, sbox, items, x, y) {
        let selwidget=sbox.children[0];

        let options='';
        if (items==='hourglass') {
            options='<option>&#8987;</option>'; // hourglass
        } else {
            items.forEach(s => options += '<option value="' + s + '">' + s + '</option>');
            options+='<option value="##ignoreall##">ignore&nbsp;all</option>';
        }
        selwidget.innerHTML=options;
        selwidget.disabled=(items==='hourglass');
        selwidget.size = selwidget.length;
        selwidget.value=-1;

        // position widget inside cm
        let cmrect=cm.getWrapperElement().getBoundingClientRect();
        sbox.style.left=x+'px';  
        sbox.style.top=(y-sbox.offsetHeight/2)+'px'; 
        let widgetRect = sbox.getBoundingClientRect();
        if (widgetRect.top<cmrect.top) sbox.style.top=(cmrect.top+2)+'px';
        if (widgetRect.right>cmrect.right) sbox.style.left=(cmrect.right-widgetRect.width-2)+'px';
        if (widgetRect.bottom>cmrect.bottom) sbox.style.top=(cmrect.bottom-widgetRect.height-2)+'px';
    }

    function sboxHide(sbox) {
        sbox.style.top=sbox.style.left='-1000px';  
    }

    // create suggestions widget
    let sbox=document.getElementById('suggestBox');
    if (!sbox) {
        sbox=document.createElement('div');
        sbox.style.zIndex=100000;
        sbox.id='suggestBox';
        sbox.style.position='fixed';
        sboxHide(sbox);

        let selwidget=document.createElement('select');
        selwidget.multiple='yes';
        sbox.appendChild(selwidget);

        sbox.suggest=((cm, e) => { // e is the event from cm contextmenu event
            if (!e.target.classList.contains('cm-spell-error')) return false; // not on typo

            let token=e.target.innerText;
            if (!token) return false; // sanity

            // save cm instance, token, token coordinates in sbox
            sbox.codeMirror=cm;
            sbox.token=token;
            let tokenRect = e.target.getBoundingClientRect();
            let start=cm.coordsChar({left: tokenRect.left+1, top: tokenRect.top+1});
            let end=cm.coordsChar({left: tokenRect.right-1, top: tokenRect.top+1});
            sbox.cmpos={ line: start.line, start: start.ch, end: end.ch};

            // show hourglass
            sboxShow(cm, sbox, 'hourglass', e.pageX, e.pageY);

            // let  the ui refresh with the hourglass & show suggestions
            setTimeout(() => { 
                sboxShow(cm, sbox, typo.suggest(token), e.pageX, e.pageY); // typo.suggest takes a while
            }, 100);

            e.preventDefault();
            return false;
        });

        sbox.onmouseleave=(e => { 
            sboxHide(sbox)
        });

        selwidget.onchange=(e => {
            sboxHide(sbox)
            let cm=sbox.codeMirror, correction=e.target.value;
            if (correction=='##ignoreall##') {
                startSpellCheck.ignoreDict[sbox.token]=true;
                cm.setOption('maxHighlightLength', (--cm.options.maxHighlightLength) +1); // ugly hack to rerun overlays
            } else {
                cm.replaceRange(correction, { line: sbox.cmpos.line, ch: sbox.cmpos.start}, { line: sbox.cmpos.line, ch: sbox.cmpos.end});
                cm.focus();
                cm.setCursor({line: sbox.cmpos.line, ch: sbox.cmpos.start+correction.length});
            }
        });

        document.body.appendChild(sbox);
    }

    return sbox;
}
Mam nadzieję, że to pomoże !
 1
Author: kofifus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-24 22:43:54