github.com/jancarloviray/community@v0.41.1-0.20170124221257-33a66c87cf2f/app/public/codemirror/addon/hint/sql-hint.js (about)

     1  // CodeMirror, copyright (c) by Marijn Haverbeke and others
     2  // Distributed under an MIT license: http://codemirror.net/LICENSE
     3  
     4  (function(mod) {
     5    if (typeof exports == "object" && typeof module == "object") // CommonJS
     6      mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
     7    else if (typeof define == "function" && define.amd) // AMD
     8      define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
     9    else // Plain browser env
    10      mod(CodeMirror);
    11  })(function(CodeMirror) {
    12    "use strict";
    13  
    14    var tables;
    15    var defaultTable;
    16    var keywords;
    17    var CONS = {
    18      QUERY_DIV: ";",
    19      ALIAS_KEYWORD: "AS"
    20    };
    21    var Pos = CodeMirror.Pos;
    22  
    23    function getKeywords(editor) {
    24      var mode = editor.doc.modeOption;
    25      if (mode === "sql") mode = "text/x-sql";
    26      return CodeMirror.resolveMode(mode).keywords;
    27    }
    28  
    29    function getText(item) {
    30      return typeof item == "string" ? item : item.text;
    31    }
    32  
    33    function getItem(list, item) {
    34      if (!list.slice) return list[item];
    35      for (var i = list.length - 1; i >= 0; i--) if (getText(list[i]) == item)
    36        return list[i];
    37    }
    38  
    39    function shallowClone(object) {
    40      var result = {};
    41      for (var key in object) if (object.hasOwnProperty(key))
    42        result[key] = object[key];
    43      return result;
    44    }
    45  
    46    function match(string, word) {
    47      var len = string.length;
    48      var sub = getText(word).substr(0, len);
    49      return string.toUpperCase() === sub.toUpperCase();
    50    }
    51  
    52    function addMatches(result, search, wordlist, formatter) {
    53      for (var word in wordlist) {
    54        if (!wordlist.hasOwnProperty(word)) continue;
    55        if (wordlist.slice) word = wordlist[word];
    56  
    57        if (match(search, word)) result.push(formatter(word));
    58      }
    59    }
    60  
    61    function cleanName(name) {
    62      // Get rid name from backticks(`) and preceding dot(.)
    63      if (name.charAt(0) == ".") {
    64        name = name.substr(1);
    65      }
    66      return name.replace(/`/g, "");
    67    }
    68  
    69    function insertBackticks(name) {
    70      var nameParts = getText(name).split(".");
    71      for (var i = 0; i < nameParts.length; i++)
    72        nameParts[i] = "`" + nameParts[i] + "`";
    73      var escaped = nameParts.join(".");
    74      if (typeof name == "string") return escaped;
    75      name = shallowClone(name);
    76      name.text = escaped;
    77      return name;
    78    }
    79  
    80    function nameCompletion(cur, token, result, editor) {
    81      // Try to complete table, colunm names and return start position of completion
    82      var useBacktick = false;
    83      var nameParts = [];
    84      var start = token.start;
    85      var cont = true;
    86      while (cont) {
    87        cont = (token.string.charAt(0) == ".");
    88        useBacktick = useBacktick || (token.string.charAt(0) == "`");
    89  
    90        start = token.start;
    91        nameParts.unshift(cleanName(token.string));
    92  
    93        token = editor.getTokenAt(Pos(cur.line, token.start));
    94        if (token.string == ".") {
    95          cont = true;
    96          token = editor.getTokenAt(Pos(cur.line, token.start));
    97        }
    98      }
    99  
   100      // Try to complete table names
   101      var string = nameParts.join(".");
   102      addMatches(result, string, tables, function(w) {
   103        return useBacktick ? insertBackticks(w) : w;
   104      });
   105  
   106      // Try to complete columns from defaultTable
   107      addMatches(result, string, defaultTable, function(w) {
   108        return useBacktick ? insertBackticks(w) : w;
   109      });
   110  
   111      // Try to complete columns
   112      string = nameParts.pop();
   113      var table = nameParts.join(".");
   114  
   115      var alias = false;
   116      var aliasTable = table;
   117      // Check if table is available. If not, find table by Alias
   118      if (!getItem(tables, table)) {
   119        var oldTable = table;
   120        table = findTableByAlias(table, editor);
   121        if (table !== oldTable) alias = true;
   122      }
   123  
   124      var columns = getItem(tables, table);
   125      if (columns && columns.columns)
   126        columns = columns.columns;
   127  
   128      if (columns) {
   129        addMatches(result, string, columns, function(w) {
   130          var tableInsert = table;
   131          if (alias == true) tableInsert = aliasTable;
   132          if (typeof w == "string") {
   133            w = tableInsert + "." + w;
   134          } else {
   135            w = shallowClone(w);
   136            w.text = tableInsert + "." + w.text;
   137          }
   138          return useBacktick ? insertBackticks(w) : w;
   139        });
   140      }
   141  
   142      return start;
   143    }
   144  
   145    function eachWord(lineText, f) {
   146      if (!lineText) return;
   147      var excepted = /[,;]/g;
   148      var words = lineText.split(" ");
   149      for (var i = 0; i < words.length; i++) {
   150        f(words[i]?words[i].replace(excepted, '') : '');
   151      }
   152    }
   153  
   154    function convertCurToNumber(cur) {
   155      // max characters of a line is 999,999.
   156      return cur.line + cur.ch / Math.pow(10, 6);
   157    }
   158  
   159    function convertNumberToCur(num) {
   160      return Pos(Math.floor(num), +num.toString().split('.').pop());
   161    }
   162  
   163    function findTableByAlias(alias, editor) {
   164      var doc = editor.doc;
   165      var fullQuery = doc.getValue();
   166      var aliasUpperCase = alias.toUpperCase();
   167      var previousWord = "";
   168      var table = "";
   169      var separator = [];
   170      var validRange = {
   171        start: Pos(0, 0),
   172        end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
   173      };
   174  
   175      //add separator
   176      var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
   177      while(indexOfSeparator != -1) {
   178        separator.push(doc.posFromIndex(indexOfSeparator));
   179        indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
   180      }
   181      separator.unshift(Pos(0, 0));
   182      separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
   183  
   184      //find valid range
   185      var prevItem = 0;
   186      var current = convertCurToNumber(editor.getCursor());
   187      for (var i=0; i< separator.length; i++) {
   188        var _v = convertCurToNumber(separator[i]);
   189        if (current > prevItem && current <= _v) {
   190          validRange = { start: convertNumberToCur(prevItem), end: convertNumberToCur(_v) };
   191          break;
   192        }
   193        prevItem = _v;
   194      }
   195  
   196      var query = doc.getRange(validRange.start, validRange.end, false);
   197  
   198      for (var i = 0; i < query.length; i++) {
   199        var lineText = query[i];
   200        eachWord(lineText, function(word) {
   201          var wordUpperCase = word.toUpperCase();
   202          if (wordUpperCase === aliasUpperCase && getItem(tables, previousWord))
   203            table = previousWord;
   204          if (wordUpperCase !== CONS.ALIAS_KEYWORD)
   205            previousWord = word;
   206        });
   207        if (table) break;
   208      }
   209      return table;
   210    }
   211  
   212    CodeMirror.registerHelper("hint", "sql", function(editor, options) {
   213      tables = (options && options.tables) || {};
   214      var defaultTableName = options && options.defaultTable;
   215      var disableKeywords = options && options.disableKeywords;
   216      defaultTable = defaultTableName && getItem(tables, defaultTableName);
   217      keywords = keywords || getKeywords(editor);
   218  
   219      if (defaultTableName && !defaultTable)
   220        defaultTable = findTableByAlias(defaultTableName, editor);
   221  
   222      defaultTable = defaultTable || [];
   223  
   224      if (defaultTable.columns)
   225        defaultTable = defaultTable.columns;
   226  
   227      var cur = editor.getCursor();
   228      var result = [];
   229      var token = editor.getTokenAt(cur), start, end, search;
   230      if (token.end > cur.ch) {
   231        token.end = cur.ch;
   232        token.string = token.string.slice(0, cur.ch - token.start);
   233      }
   234  
   235      if (token.string.match(/^[.`\w@]\w*$/)) {
   236        search = token.string;
   237        start = token.start;
   238        end = token.end;
   239      } else {
   240        start = end = cur.ch;
   241        search = "";
   242      }
   243      if (search.charAt(0) == "." || search.charAt(0) == "`") {
   244        start = nameCompletion(cur, token, result, editor);
   245      } else {
   246        addMatches(result, search, tables, function(w) {return w;});
   247        addMatches(result, search, defaultTable, function(w) {return w;});
   248        if (!disableKeywords)
   249          addMatches(result, search, keywords, function(w) {return w.toUpperCase();});
   250      }
   251  
   252      return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
   253    });
   254  });