2012-09-09 19 views
15

Vorrei utilizzare la funzionalità di CodeMirror (come il lino, il wrapping, la ricerca, ecc.) Per testo normale, senza particolare necessità di evidenziazione del codice, ma con il correttore ortografico di Google Chrome o qualche altro linguaggio naturale (specialmente inglese) controllo ortografico attivato (non ho bisogno di farlo funzionare su altri browser). Come posso fare questo? È possibile scrivere un componente aggiuntivo in modalità testo semplice che consente il controllo ortografico?CodeMirror con correttore ortografico

risposta

26

Io in realtà integrato con typo.jsCodeMirror mentre codifica per NoTex.ch; puoi dare un'occhiata qui CodeMirror.rest.js; Avevo bisogno di un modo per ottenere l'ortografia markup reStructuredText, e poiché utilizzo le eccellenti capacità di evidenziazione della sintassi di CodeMirror, era abbastanza semplice da fare.

È possibile controllare il codice al link fornito, ma cercherò di riassumere, quello che ho fatto:

  1. inizializzare la libreria typo.js; si veda anche l'autore del blog/documentazione:

    var typo = new Typo ("en_US", AFF_DATA, DIC_DATA, { 
        platform: 'any' 
    }); 
    
  2. definire un'espressione regolare per i vostri separatori di parole:

    var rx_word = "!\"#$%&()*+,-./:;<=>[email protected][\\\\\\]^_`{|}~"; 
    
  3. definire una modalità di sovrapposizione per 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. Utilizzare il sovrapposizione con CodeMirror; guarda il manuale utente per capire come lo fai esattamente. L'ho fatto nel mio codice in modo da poter controllare anche lì, ma raccomando il manuale utente.

  5. Definire classe CSS:

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

Questo approccio funziona alla grande per il tedesco, inglese e spagnolo. Con il dizionario francese typo.js sembra avere alcuni problemi (accentati) e lingue come ebraico, ungherese e italiano - dove il numero di affissi è lungo o il dizionario è piuttosto esteso - non funziona davvero, dal typo.js all'attuale implementazione utilizza troppa memoria ed è troppo lento.

con il tedesco (e spagnolo) typo.js possono bloccare la VM JavaScript per poche centinaia di millisecondi (ma solo durante l'inizializzazione!), Così si potrebbe prendere in considerazione thread in background con i lavoratori web HTML5 (vedi CodeMirror. typo.worker.js per un esempio). Ulteriori typo.js non sembra supportare Unicode (a causa di restrizioni JavaScript): Almeno, io non sono riuscito a farlo funzionare con lingue non latine come il russo, greco, hindi ecc

I' Non ho rifatto la soluzione descritta in un bel progetto separato a parte (ora piuttosto grande) NoTex.ch, ma potrei farlo abbastanza presto; fino ad allora devi correggere la tua soluzione in base alla descrizione precedente o al codice suggerito. Spero che aiuti.

+0

È grandioso. Non ho ancora provato completamente, ma mi fido di esso. – sawa

+3

Questo è fantastico! Si potrebbe anche voler usare la nuova funzione 'addOverlay' (http://codemirror.net/doc/manual.html#addOverlay), che fornisce un modo più efficiente e meno invasivo di aggiungere overlay di utilità. – Marijn

+1

il collegamento in questa risposta non si risolve più, il file è stato spostato, è stato eliminato? –

1

CodeMirror non si basa su una textarea HTML, in modo da can't use the built-in spell check

Si potrebbe implementare il proprio controllo ortografico per CodeMirror con qualcosa di simile typo.js

Non credo nessuno abbia ancora fatto.

+0

Ok, dimentica il mio commento precedente. Le linee che hai suggerito sembrano più generiche, e lo esaminerò. – sawa

+0

CodeMirror è orientato verso il codice, non la prosa. Quindi, se non hai bisogno di evidenziazione della sintassi, potrebbe non essere lo strumento giusto. Si consiglia di consultare [MarkItUp] (http://markitup.jaysalvat.com/home/) che ha un [correttore ortografico di proof of concept] (http://spellchecker.jquery.badsyntax.co.uk/ markitup.html) – Doug

+1

La tua risposta ha aiutato a guardare nella giusta direzione, ma hsk81 ha dato una risposta ancora più dettagliata, quindi sposto il segno di spunta su tale risposta. Grazie. – sawa

1

Ho scritto un tipo di controllo ortografico a forma di sottotaglio poco fa. Ha bisogno di una riscrittura, per essere onesto, allora ero molto nuovo su JavaScript. Ma i principi sono tutti lì.

https://github.com/jameswestgate/SpellAsYouType

+0

Lo esaminerò. Grazie. – sawa

3

Questa è una versione funzionante della risposta di hsk81. Usa la modalità di sovrapposizione di CodeMirror e cerca qualsiasi parola tra virgolette, tag html, ecc. Ha un esempio typo.check che dovrebbe essere sostituito con qualcosa come Typo.js. Sottolinea le parole sconosciute con una linea ondulata rossa.

Questo è stato testato utilizzando una cella %% html di IPython.

<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> 
1

Ho creato un correttore ortografico con errori di battitura e suggerimenti/correzioni:

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

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

Qui di seguito sono parti rilevanti del codice:

prima cosa fare una promessa per caricare i dizionari. Io uso typo.js per il dizionario, il caricamento può richiedere un po 'se non sono ospitati a livello locale, quindi è meglio iniziare il caricamento non appena l'avviamenti prima login/CM inizializzazione ecc:

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); 
    }); 
} 

seconda ho aggiungere un overlay per individuare e marcare errori di battitura in questo modo:

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); 

Terzo io uso una casella di riepilogo per mostrare suggerimenti e correggere errori di battitura:

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; 
} 

Spero che questo aiuti!

Problemi correlati