2012-09-09 48 views
15

Tôi muốn sử dụng chức năng của CodeMirror (chẳng hạn như linenumbering, gói, tìm kiếm, v.v.) cho văn bản thuần túy, mà không cần mã làm nổi bật mà thay vào đó là trình kiểm tra chính tả của Google Chrome hoặc một số ngôn ngữ tự nhiên khác (đặc biệt là tiếng Anh) kiểm tra chính tả kích hoạt (tôi không cần phải có nó hoạt động trên các trình duyệt khác). Tôi có thể làm cái này như thế nào? Có thể viết một add-on chế độ văn bản thuần túy cho phép kiểm tra chính tả không?CodeMirror với trình kiểm tra chính tả

Trả lời

26

Tôi thực sự tích hợp typo.js với CodeMirror trong khi mã hóa cho NoTex.ch; bạn có thể xem tại đây CodeMirror.rest.js; Tôi cần một cách để kiểm tra chính tả đánh dấu reStructuredText và vì tôi sử dụng khả năng làm nổi bật cú pháp tuyệt vời của CodeMirror, nên nó khá là thẳng về phía trước để thực hiện.

Bạn có thể kiểm tra mã tại liên kết được cung cấp, nhưng tôi sẽ tóm tắt, những gì tôi đã làm:

  1. Khởi tạo thư viện typo.js; xem thêm của tác giả blog/tài liệu:

    var typo = new Typo ("en_US", AFF_DATA, DIC_DATA, { 
        platform: 'any' 
    }); 
    
  2. Xác định một biểu thức chính quy cho tách từ của bạn:

    var rx_word = "!\"#$%&()*+,-./:;<=>[email protected][\\\\\\]^_`{|}~"; 
    
  3. Xác định một chế độ overlay cho 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. Sử dụng lớp phủ với CodeMirror; xem hướng dẫn sử dụng để tìm ra chính xác bạn làm điều này như thế nào. Tôi đã thực hiện nó trong mã của tôi để bạn có thể kiểm tra xem nó ra có quá, nhưng tôi khuyên bạn nên hướng dẫn sử dụng.

  5. Xác định lớp CSS:

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

Cách tiếp cận này hoạt động tuyệt vời cho Đức, tiếng Anh và tiếng Tây Ban Nha. Với từ điển tiếng Pháp typo.js dường như có một số vấn đề và ngôn ngữ như tiếng Do Thái, tiếng Hungari và tiếng Ý - nơi số lượng tệp dài hoặc từ điển khá rộng - nó không hoạt động thực sự, kể từ typo.js tại triển khai hiện tại của nó sử dụng quá nhiều bộ nhớ và quá chậm.

Với Đức (và Tây Ban Nha) typo.js có thể chặn VM JavaScript cho một vài trăm mili giây (nhưng chỉ trong quá trình khởi!), Vì vậy bạn có thể muốn xem xét đề nền với người lao web HTML5 (xem CodeMirror. typo.worker.js để biết ví dụ). Hơn nữa typo.js dường như không hỗ trợ Unicode (do hạn chế JavaScript): Ít nhất, tôi đã không quản lý để có được nó để làm việc với các ngôn ngữ phi Latin như Nga, Hy Lạp, Tiếng Hin-ddi, vv

tôi đã không refactored các giải pháp được mô tả vào một dự án riêng biệt đẹp ngoài (bây giờ khá lớn) NoTex.ch, nhưng tôi có thể làm điều đó khá sớm; cho đến lúc đó bạn đã vá giải pháp của riêng mình dựa trên mô tả ở trên hoặc mã được gợi ý. Tôi hi vọng cái này giúp được.

+0

Thật tuyệt. Tôi chưa thử hết, nhưng tôi tin tưởng nó. – sawa

+3

Điều này thật tuyệt vời! Bạn cũng có thể muốn sử dụng tính năng 'addOverlay' mới (http://codemirror.net/doc/manual.html#addOverlay), cung cấp cách thêm hiệu quả và ít xâm lấn hơn để thêm lớp phủ tiện ích. – Marijn

+1

liên kết trong câu trả lời này không còn giải quyết, tệp đã được di chuyển chưa, nó có bị xóa không? –

1

CodeMirror không dựa trên một textarea HTML, vì vậy bạn can't use the built-in spell check

Bạn có thể thực hiện kiểm tra chính tả của riêng bạn cho CodeMirror với một cái gì đó giống như typo.js

Tôi không tin rằng bất cứ ai đã làm điều này được nêu ra.

+0

OK, hãy quên nhận xét trước của tôi. Các dòng bạn đề xuất có vẻ tổng quát hơn, và tôi sẽ xem xét nó. – sawa

+0

CodeMirror được định hướng theo mã, chứ không phải văn xuôi. Vì vậy, nếu bạn không cần đánh dấu cú pháp, nó có thể không phải là công cụ thích hợp. Bạn có thể xem [MarkItUp] (http://markitup.jaysalvat.com/home/) có [bằng chứng kiểm tra chính tả khái niệm] (http://spellchecker.jquery.badsyntax.co.uk/ markitup.html) – Doug

+1

Câu trả lời của bạn đã giúp tìm đúng hướng, nhưng hsk81 đã đưa ra một câu trả lời chi tiết hơn, vì vậy tôi di chuyển dấu kiểm đến câu trả lời đó. Cảm ơn. – sawa

1

Tôi đã viết một trình kiểm tra chính tả kiểu gạch dưới nguệch ngoạc cách đây một thời gian. Nó cần một viết lại thành thật mà nói tôi đã rất mới với JavaScript sau đó. Nhưng các nguyên tắc là tất cả ở đó.

https://github.com/jameswestgate/SpellAsYouType

+0

Tôi sẽ xem xét nó. Cảm ơn. – sawa

3

Đây là phiên bản hoạt động của câu trả lời của hsk81. Nó sử dụng chế độ che phủ của CodeMirror và tìm kiếm bất kỳ từ nào bên trong dấu ngoặc kép, thẻ html, v.v. Nó có một mẫu typo.check cần được thay thế bằng một cái gì đó như Typo.js. Nó gạch chân các từ không rõ với một đường thẳng màu đỏ.

Điều này đã được thử nghiệm bằng cách sử dụng ô %% html của 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

Tôi tạo ra một kiểm tra chính tả với lỗi chính tả gợi ý/chỉnh sửa:

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

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

Dưới đây là phần có liên quan của mã:

Đầu tiên tôi thực hiện một lời hứa để tải các từ điển. Tôi sử dụng typo.js cho từ điển, tải có thể mất một thời gian nếu không được tổ chức tại địa phương, vì vậy nó là tốt hơn để bắt đầu tải ngay sau khi bắt đầu của bạn trước khi đăng nhập/CM khởi vv:

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

Second tôi thêm lớp phủ để phát hiện và đánh dấu lỗi chính tả như thế này:

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

thứ ba tôi sử dụng một hộp danh sách để hiển thị gợi ý và sửa chữa lỗi chính tả:

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

Hope this helps!

Các vấn đề liên quan