2012-09-09 34 views
15

Me gustaría utilizar la funcionalidad CodeMirror (como lino, envolver, buscar, etc.) para texto sin formato, sin necesidad de resaltar el código en particular, pero con el corrector ortográfico Google Chrome o algún otro lenguaje natural (especialmente en inglés) activado el corrector ortográfico (no es necesario que funcione en otros navegadores). ¿Cómo puedo hacer esto? ¿Es posible escribir un complemento en modo de texto sin formato que permita la corrección ortográfica?CodeMirror con corrector ortográfico

Respuesta

26

realidad integré typo.js con CodeMirror mientras que la codificación de NoTex.ch; puedes echarle un vistazo aquí CodeMirror.rest.js; Necesitaba una forma de obtener el reStructuredText markup hechizo revisado, y dado que uso las excelentes capacidades de resaltado de sintaxis de CodeMirror, fue bastante sencillo de hacer.

Puede comprobar el código en el enlace proporcionado, pero voy a resumir, lo que he hecho:

  1. inicializar la biblioteca typo.js; véase también el autor del blog/documentación:

    var typo = new Typo ("en_US", AFF_DATA, DIC_DATA, { 
        platform: 'any' 
    }); 
    
  2. definir una expresión regular para sus separadores palabra:

    var rx_word = "!\"#$%&()*+,-./:;<=>[email protected][\\\\\\]^_`{|}~"; 
    
  3. definir un modo de superposición de 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. uso del superposición con CodeMirror; mira el manual del usuario para descubrir cómo exactamente haces esto. Lo hice en mi código para que pueda verlo también, pero recomiendo el manual del usuario.

  5. Definir clase CSS:

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

Este enfoque funciona muy bien para el alemán, Inglés y Español. Con el diccionario francés typo.js parece tener algunos problemas (acento), y los idiomas como el hebreo, húngaro e italiano, donde el número de afijos es largo o el diccionario es bastante extenso, realmente no funciona, ya que typo.js en su implementación actual usa demasiada memoria y es demasiado lenta.

con el alemán (y español) typo.js pueden bloquear el JavaScript VM para unos pocos cientos de milisegundos (pero sólo durante la inicialización!), Así que sería bueno tener en cuenta las discusiones de fondo con los trabajadores web HTML5 (véase CodeMirror. typo.worker.js para un ejemplo). Otras typo.js no parece ser compatible con Unicode (debido a las restricciones de JavaScript): Por lo menos, no he podido conseguirlo para trabajar con idiomas no latinos como el ruso, griego, hindi, etc.

I' No he refabricado la solución descrita en un buen proyecto aparte de (ahora bastante grande) NoTex.ch, pero podría hacerlo bastante pronto; Hasta entonces, debe parchar su propia solución según la descripción anterior o el código sugerido. Espero que esto ayude.

+0

Eso es genial. Todavía no lo he probado completamente, pero confío en ello. – sawa

+3

¡Esto es asombroso! También es posible que desee utilizar la nueva función 'addOverlay' (http://codemirror.net/doc/manual.html#addOverlay), que proporciona una forma más eficiente y menos invasiva de agregar superposiciones de servicios. – Marijn

+1

el enlace en esta respuesta ya no se resuelve, ¿se movió el archivo, se eliminó? –

1

CodeMirror no se basa en un área de texto HTML, por lo que can't use the built-in spell check

Se podría implementar su propio corrector ortográfico para CodeMirror con algo como typo.js

Yo no creo que nadie lo ha hecho todavía.

+0

Bueno, olvídate de mi comentario anterior. Las líneas que sugirió parecen más generales, y las investigaré. – sawa

+0

CodeMirror está orientado al código, no a la prosa. Entonces, si no necesita resaltar la sintaxis, puede que no sea la herramienta correcta. Es posible que desee consultar [MarkItUp] (http://markitup.jaysalvat.com/home/) que tiene un [corrector ortográfico de prueba de concepto] (http://spellchecker.jquery.badsyntax.co.uk/ markitup.html) – Doug

+1

Su respuesta ayudó a mirar en la dirección correcta, pero hsk81 dio una respuesta aún más detallada, así que muevo la marca de verificación a esa respuesta. Gracias. – sawa

1

Escribí un corrector ortográfico tipo subrayado ondulante hace un tiempo. Necesita una reescritura para ser honesto, entonces era muy nuevo en JavaScript. Pero los principios están todos allí.

https://github.com/jameswestgate/SpellAsYouType

+0

Lo investigaré. Gracias. – sawa

3

Esta es una versión funcional de la respuesta de hsk81. Utiliza el modo de superposición de CodeMirror y busca cualquier palabra dentro de comillas, etiquetas html, etc. Tiene una muestra typo.check que debería reemplazarse con algo así como Typo.js. Subraya palabras desconocidas con una línea roja ondulada.

Esto se probó utilizando una celda %% html de 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

creé un corrector ortográfico con errores tipográficos sugerencias/correcciones:

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

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

A continuación se presentan las partes pertinentes del código:

Primero hago una promesa para cargar los diccionarios Yo uso typo.js para el diccionario, la carga puede tomar un tiempo si no están organizadas a nivel local, por lo que es mejor empezar la carga tan pronto como sus comienza antes de conectar/CM inicializar 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); 
    }); 
} 

Segundo I añadir una superposición de detectar y marcar los errores tipográficos de esta manera:

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

Tercer utilizo un cuadro de lista para mostrar sugerencias y corregir faltas de ortografía:

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

Espero que esto ayude!

Cuestiones relacionadas