Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/466.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 带拼写检查器的代码镜像_Javascript_Textarea_Codemirror - Fatal编程技术网

Javascript 带拼写检查器的代码镜像

Javascript 带拼写检查器的代码镜像,javascript,textarea,codemirror,Javascript,Textarea,Codemirror,我想对纯文本使用的功能(如行号、换行、搜索等),不需要特别的代码高亮,而是使用Google Chrome拼写检查或其他自然语言(特别是英语)拼写检查激活(我不需要在其他浏览器上使用)。我该怎么做?是否可以编写一个纯文本模式加载项来启用拼写检查?CodeMirror不基于HTML文本区域,因此 您可以使用下面的东西实现自己的CodeMirror拼写检查 我相信还没有人这样做过。我不久前写了一个弯曲的下划线类型拼写检查程序。老实说,这需要重写,那时我对JavaScript非常陌生。但原则都在那里 我

我想对纯文本使用的功能(如行号、换行、搜索等),不需要特别的代码高亮,而是使用Google Chrome拼写检查或其他自然语言(特别是英语)拼写检查激活(我不需要在其他浏览器上使用)。我该怎么做?是否可以编写一个纯文本模式加载项来启用拼写检查?

CodeMirror不基于HTML文本区域,因此

您可以使用下面的东西实现自己的CodeMirror拼写检查


我相信还没有人这样做过。

我不久前写了一个弯曲的下划线类型拼写检查程序。老实说,这需要重写,那时我对JavaScript非常陌生。但原则都在那里

我实际上集成了while编码;你可以在这里看看;我需要一种方法来检查StructuredText标记的拼写,因为我使用了CodeMirror出色的语法突出显示功能,所以这是非常简单的

您可以在提供的链接中查看代码,但我将总结一下我所做的工作:

  • 初始化typo.js库;另请参见作者的博客/文档:

    var typo = new Typo ("en_US", AFF_DATA, DIC_DATA, {
        platform: 'any'
    });
    
  • 为分词符定义正则表达式:

    var rx_word = "!\"#$%&()*+,-./:;<=>?@[\\\\\\]^_`{|}~";
    
  • 使用覆盖与CodeMirror;请参阅用户手册,了解您是如何做到这一点的。我已经在我的代码中完成了,所以你也可以在那里查看它,但我推荐使用手册

  • 定义CSS类:

    .CodeMirror .cm-spell-error {
         background: url(images/red-wavy-underline.gif) bottom repeat-x;
    }
    
  • 这种方法适用于德语、英语和西班牙语。法语词典typo.js似乎有一些(口音)问题,像希伯来语、匈牙利语和意大利语这样的语言——词缀的数量很长或者词典相当广泛——它实际上不起作用,因为typo.js在其当前的实现中使用了太多的内存,速度太慢

    使用德语(和西班牙语)Typ.js可以将JavaScript VM拦截几百毫秒(但仅在初始化过程中),因此您可能需要考虑HTML5 Web工作者的后台线程(例如,见CodeMirror.typo.worker.js)。此外,typo.js似乎不支持Unicode(由于JavaScript限制):至少,我没有设法让它与非拉丁语言(如俄语、希腊语、印地语等)一起使用


    除了(现在相当大的)NoTex.ch之外,我还没有将所描述的解决方案重构成一个很好的单独项目,但我可能很快就会这样做;在此之前,您必须根据上述描述或提示代码修补您自己的解决方案。我希望这能有所帮助。

    这是hsk81答案的有效版本。它使用CodeMirror的覆盖模式,并在引号、html标记等中查找任何单词。它有一个示例typo.check,应该替换为类似typo.js的内容。它用一条红色的曲线在生词下面划线

    这是使用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>
    
    
    .CodeMirror.cm拼写错误{
    背景:url(“https://raw.githubusercontent.com/jwulf/typojs-project/master/public/images/red-wavy-underline.gif)底部重复-x;
    }
    覆盖解析器演示
    变量类型={检查:函数(当前){
    var字典={“苹果”:1,“香蕉”:1,“不能”:1,“这个”:1,“那个”:1,“那个”:1};
    返回字典中的current.toLowerCase();
    }
    }
    defineMode(“拼写检查”,函数(config,parserConfig){
    var rx\u word=new RegExp(“[^\!\”\\\$\%\&”(\)\*\+\,\-\.\/\:\;\?\@[\\\\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\];
    变量拼写覆盖={
    令牌:函数(流、状态){
    var-ch;
    if(stream.match(rx_word)){
    而((ch=stream.peek())!=null){
    如果(!ch.match(rx_单词)){
    打破
    }
    stream.next();
    }
    如果(!typo.check(stream.current()))
    返回“拼写错误”;
    返回null;
    }
    while(stream.next()!=null&&!stream.match(rx_-word,false)){}
    返回null;
    }
    };
    返回codemirr.overlyMode(codemirr.getMode(config,parserConfig.background | | |“text/html”),拼写覆盖);
    });
    var editor=codemirr.fromTextArea(document.getElementById(“code”),{mode:“拼写检查”});
    
    我创建了一个拼写检查器,其中包含拼写错误建议/更正:

    演示:

    以下是守则的相关部分:

    首先,我承诺加载字典。我使用typo.js加载字典,如果字典不是本地托管的,加载可能需要一段时间,因此最好在登录/CM初始化之前尽快开始加载,等等:

    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);
        });
    }
    
    第二,我添加了一个覆盖图,以检测和标记如下所示的打字错误:

    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);
    
    第三,我使用一个列表框来显示建议和修复打字错误:

    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;
    }
    
    函数getSuggestionBox(打字错误){
    函数sboxShow(cm、sbox、项、x、y){
    让selwidget=sbox.children[0];
    让选项=“”;
    如果(项目=='沙漏'){
    选项='⌛;'//沙漏
    }否则{
    items.forEach(s=>options+=''+s+'');
    选项+=‘全部忽略’;
    }
    selwidget.innerHTML=选项;
    selwidget.disabled=(items=='hourglass');
    selwidget.size=selwidget.length;
    selwidget.value=-1;
    //将小部件放置在cm中
    设cmrect=cm.getWrapperElement().getBoundingClientRect();
    sbox.style.left=x+'px';
    sbox.style.top=(y-sbox.offsetHeight/2)+'px';
    设widgetRect=sbox.getBoundingClientRect();
    if(widgetRect.topcmrect.right)sbox.style.left=(cmrect.right widgetRect.width-2)+'px';
    if(widgetRect.bottom>cmrect.bottom)sbox.style.top=(cmrect.bottom widgetRect.height-2)+'px';
    }
    函数sboxHide(sbox){
    sbox.style.top=sbox.style.left='-1000px';
    }
    //创建建议小部件
    设sbox=document.getElementById('suggestBox');
    if(!sbox){
    sbox=doc
    
    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;
    }