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