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

十度 JQuery 2016年01月16日 收藏

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

  1.  <link rel="stylesheet" href="../lib/codemirror.css">
  2.     <script src="../lib/codemirror.js"></script>
  3.     <script src="../addon/hint/show-hint.js"></script>
  4.     <script src="../addon/hint/javascript-hint.js"></script>
  5.     <script src="../mode/javascript/javascript.js"></script>
  6.     <link rel="stylesheet" href="../addon/hint/show-hint.css">
  7.     <link rel="stylesheet" href="../doc/docs.css">

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

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

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

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

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

  1.  function getCoffeeScriptToken(editor, cur) {
  2.   // This getToken, it is for coffeescript, imitates the behavior of
  3.   // getTokenAt method in javascript.js, that is, returning "property"
  4.   // type and treat "." as indepenent token.
  5.     var token = editor.getTokenAt(cur);
  6.     if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
  7.       token.end = token.start;
  8.       token.string = '.';
  9.       token.type = "property";
  10.     }
  11.     else if (/^\.[\w$_]*$/.test(token.string)) {
  12.       token.type = "property";
  13.       token.start++;
  14.       token.string = token.string.replace(/\./, '');
  15.     }
  16.     return token;
  17.   }
  18.   CodeMirror.coffeescriptHint = function(editor, options) {
  19.     return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
  20.   };
  21.   var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
  22.                      "toUpperCase toLowerCase split concat match replace search").split(" ");
  23.   var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
  24.                     "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
  25.   var funcProps = "prototype apply call bind".split(" ");
  1.    var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
  2.                   "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为例子的,我们如果想改成自己的实例,就还需要修改一些地方:把

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

修改成我自己的需求

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

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

  1. if (context) {
  2.       // If this is a property, see if it belongs to some object we can
  3.       // find in the current environment.
  4.       var obj = context.pop(), base;
  5.       if (obj.type.indexOf("variable") === 0) {
  6.         if (options && options.additionalContext)
  7.           base = options.additionalContext[obj.string];
  8.         base = base || window[obj.string];
  9.       } else if (obj.type == "string") {
  10.         base = "";
  11.       } else if (obj.type == "atom") {
  12.         base = 1;
  13.       } else if (obj.type == "function") {
  14.         if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
  15.             (typeof window.jQuery == 'function'))
  16.           base = window.jQuery();
  17.         else if (window.!= null && (obj.string == '_') && (typeof window.== 'function'))
  18.           base = window._();
  19.       }
  20.       while (base != null && context.length)
  21.         base = base[context.pop().string];
  22.       if (base != null) gatherCompletions(base);
  23.     }
  24.     else {
  25.       // If not, just look in the window object and any local scope
  26.       // (reading into JS mode internals to get at the local and global variables)
  27.       for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
  28.       for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
  29.       gatherCompletions(window);
  30.       forEach(keywords, maybeAdd);
  31.     }

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

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

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

  1.  protected string GetDefaultValue()
  2.         {
  3.             DataTable dt = BDAContext.GetObject<ICNPRStructureConfigBLL>().GetAllObjectPropertyName();
  4.             string s = string.Empty;
  5.             if (dt.Rows.Count>0)
  6.             {
  7.                 foreach (DataRow dr in dt.Rows)
  8.                 {
  9.                     s += dr["Name"] + " ";
  10.                 }
  11.             }
  12.             return s;
  13.         }

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

  1.  <script type="text/javascript">
  2.                 $(document).ready(function () {
  3.                     CodeMirror.javascriptKeywords = ('<%=GetDefaultValue()%>').split(" ");
  4.                     CodeMirror.commands.autocomplete = function (cm) {
  5.                         CodeMirror.showHint(cm, CodeMirror.javascriptHint);
  6.                     }
  7.                     var editor = CodeMirror.fromTextArea(document.getElementById("txt_FunExp"), {
  8.                         textWrapping: true,
  9.                         lineWrapping: true,
  10.                         lineNumbers: false,
  11.                         extraKeys: { "Ctrl-Space": "autocomplete" }
  12.                     });
  13.                 });
  14.             </script>

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

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

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

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

 经本人修改后,成这样:

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