文本域的CodeMirror编辑器的自动联想功能

十度 JQuery 2016年01月16日 收藏

对于网站中需要用到代码编写功能的童鞋,不放注意下CodeMirror,支持多重语言的自动联想,自动补全,很像我们熟悉的VS编辑器等。而且我们还能定义自己的联想关键字,这几天因为项目的需要,需要在文本域中实现联想补全功能。特意研究了CodeMirror。
     对于网上CodeMirror的介绍很少,官网的英文看得让人头大,这两天项目中需要用到自动补全联想功能,就研究研究了CodeMirror,顺便记下来,让更多的人收益学习。
 CodeMirror官网上下载压缩文件后,解压,看到好多文件,还有好多Deom有兴趣的可以多研究研究DEOM,而我们所需要的自动补全功能实际上只需要以下7个文件。

 <link rel="stylesheet" href="../lib/codemirror.css">
    <script src="../lib/codemirror.js"></script>
    <script src="../addon/hint/show-hint.js"></script>
    <script src="../addon/hint/javascript-hint.js"></script>
    <script src="../mode/javascript/javascript.js"></script>
    <link rel="stylesheet" href="../addon/hint/show-hint.css">
    <link rel="stylesheet" href="../doc/docs.css">

(以JS代码的自动补全功能为例)引入的这些文件,只需要把这7个文件引入项目就行了,没必要把整个解压的文件引入。

 <script src="../lib/codemirror.js"></script>这个是主文件,要想使用自动补全功能,就要首先引入这个文件
<script src="../addon/hint/javascript-hint.js"></script>这个文件是提示显示所有JS关键字,
 <script src="../mode/javascript/javascript.js"></script>

  如果只需要文本域编辑器的主动补全功能, 这个文件是根据用户输入的内容,只搜索这个以用户输入的内容为开头的关键字显示出来,如果不引入这个文件,智能提示就不会提示以用户输入的内容为开头的关键字,而是系统所预设的全部关键字。所以要引入这个文件。
 现在的问题:

1:在/addon/hint/javascript-hint.js文件中找出在哪里显示这些JS关键字。   

2:在/mode/javascript/javascript.js文件中找到程序是怎样根据用户输入的内容,只搜索这个以用户输入的内容为开头的关键字显示出来
对于第一个问题,其实可以把这个/addon/hint/javascript-hint.js文件中的

 function getCoffeeScriptToken(editor, cur) {
  // This getToken, it is for coffeescript, imitates the behavior of
  // getTokenAt method in javascript.js, that is, returning "property"
  // type and treat "." as indepenent token.
    var token = editor.getTokenAt(cur);
    if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
      token.end = token.start;
      token.string = '.';
      token.type = "property";
    }
    else if (/^\.[\w$_]*$/.test(token.string)) {
      token.type = "property";
      token.start++;
      token.string = token.string.replace(/\./, '');
    }
    return token;
  }
  CodeMirror.coffeescriptHint = function(editor, options) {
    return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
  };
  var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
                     "toUpperCase toLowerCase split concat match replace search").split(" ");
  var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
                    "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
  var funcProps = "prototype apply call bind".split(" ");
   var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
                  "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");

删除,没特殊的需求是不需要,这个默认的deom是以补全javascript为例子的,我们如果想改成自己的实例,就还需要修改一些地方:把

var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " 
+"if in instanceof new null return switch throw true try typeof var void while with").split(" ");

修改成我自己的需求

var javascriptKeywords = ("薪资计算 社保计算 上月平均工资 养老保险 失业保险 sum abs qeens qees qwsa qxvc").split(" ");

但是保存运行之后还是会出现JS中的关键字,默认函数之类的东西,又研究了研究,原来是:

if (context) {
      // If this is a property, see if it belongs to some object we can
      // find in the current environment.
      var obj = context.pop(), base;
      if (obj.type.indexOf("variable") === 0) {
        if (options && options.additionalContext)
          base = options.additionalContext[obj.string];
        base = base || window[obj.string];
      } else if (obj.type == "string") {
        base = "";
      } else if (obj.type == "atom") {
        base = 1;
      } else if (obj.type == "function") {
        if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
            (typeof window.jQuery == 'function'))
          base = window.jQuery();
        else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
          base = window._();
      }
      while (base != null && context.length)
        base = base[context.pop().string];
      if (base != null) gatherCompletions(base);
    }
    else {
      // If not, just look in the window object and any local scope
      // (reading into JS mode internals to get at the local and global variables)
      for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
      for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
      gatherCompletions(window);
      forEach(keywords, maybeAdd);
    }

    这一段代码在做鬼,我就来个更直接,更暴力的办法:
    直接改成这样:

 if (context) {
            // If this is a property, see if it belongs to some object we can
            // find in the current environment.
            var obj = context.pop(), base;
          
            gatherCompletions(null);
        }
        else {
            // If not, just look in the window object and any local scope
            // (reading into JS mode internals to get at the local and global variables)
            for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
            for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
            gatherCompletions(null);
            forEach(keywords, maybeAdd);
        }

    研究了一天也研究烦了,不要怪我哦!这样一试,果然是行了。
    但是如果想要使用自己数据库中的关键字该怎么办呢?研究了一下想了想,也想到了一个解决办法:
    在后台写一个方法也让数据库中的字段变成var javascriptKeywords = ("薪资计算 社保计算 上月平均工资 养老保险 失业保险 sum abs qeens qees qwsa qxvc").split(" ");即可,我的方法:

 protected string GetDefaultValue()
        {
            DataTable dt = BDAContext.GetObject<ICNPRStructureConfigBLL>().GetAllObjectPropertyName();
            string s = string.Empty;
            if (dt.Rows.Count>0)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    s += dr["Name"] + " ";
                }
            }
            return s;
        }

        在前台开始就写上  CodeMirror.javascriptKeywords = ('<%=GetDefaultValue()%>').split(" ");
但是所有的javascriptKeywords需要加上CodeMirror.,及我上面所写的。
所以我的aspx页面就是这样的:

 <script type="text/javascript">
                $(document).ready(function () {
                    CodeMirror.javascriptKeywords = ('<%=GetDefaultValue()%>').split(" ");
                    CodeMirror.commands.autocomplete = function (cm) {
                        CodeMirror.showHint(cm, CodeMirror.javascriptHint);
                    }
                    var editor = CodeMirror.fromTextArea(document.getElementById("txt_FunExp"), {
                        textWrapping: true,
                        lineWrapping: true,
                        lineNumbers: false,
                        extraKeys: { "Ctrl-Space": "autocomplete" }
                    });
                });
            </script>

   但是又出现一个问题:输入中文时,不会智能提示出那个中文开头的短语或词组,也就是说不支持中文只能提示。又是个纠结的问题,还需要研究修改
 /mode/javascript/javascript.js    文件的内容,一看,比刚才那个JS文件代码还多,我瞬间比这个文件的代码更凌乱了,没办法,慢慢研究研究吧!

 也欢迎大牛们给出知道意见,看看怎么支持中文!不胜感激!

附上贴图:这是原DEOM中的实例,按下Ctrl-Space键之后,就会提示所有的JS关键字等,

任意按下一个字母,如m,再按Ctrl-Space键,就会提示出所有以m开头的单词

 经本人修改后,成这样:

  另外,需要提醒的是,使用快捷键显示的话注意最好不要跟其他应用程序的的热键冲突。