* Update CodeMirror to version 5.49.0 * Update CodeMirror versions in librejs and VERSIONS
		
			
				
	
	
		
			305 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // CodeMirror, copyright (c) by Marijn Haverbeke and others
 | |
| // Distributed under an MIT license: https://codemirror.net/LICENSE
 | |
| 
 | |
| (function(mod) {
 | |
|   if (typeof exports == "object" && typeof module == "object") // CommonJS
 | |
|     mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
 | |
|   else if (typeof define == "function" && define.amd) // AMD
 | |
|     define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
 | |
|   else // Plain browser env
 | |
|     mod(CodeMirror);
 | |
| })(function(CodeMirror) {
 | |
|   "use strict";
 | |
| 
 | |
|   var tables;
 | |
|   var defaultTable;
 | |
|   var keywords;
 | |
|   var identifierQuote;
 | |
|   var CONS = {
 | |
|     QUERY_DIV: ";",
 | |
|     ALIAS_KEYWORD: "AS"
 | |
|   };
 | |
|   var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
 | |
| 
 | |
|   function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
 | |
| 
 | |
|   function getKeywords(editor) {
 | |
|     var mode = editor.doc.modeOption;
 | |
|     if (mode === "sql") mode = "text/x-sql";
 | |
|     return CodeMirror.resolveMode(mode).keywords;
 | |
|   }
 | |
| 
 | |
|   function getIdentifierQuote(editor) {
 | |
|     var mode = editor.doc.modeOption;
 | |
|     if (mode === "sql") mode = "text/x-sql";
 | |
|     return CodeMirror.resolveMode(mode).identifierQuote || "`";
 | |
|   }
 | |
| 
 | |
|   function getText(item) {
 | |
|     return typeof item == "string" ? item : item.text;
 | |
|   }
 | |
| 
 | |
|   function wrapTable(name, value) {
 | |
|     if (isArray(value)) value = {columns: value}
 | |
|     if (!value.text) value.text = name
 | |
|     return value
 | |
|   }
 | |
| 
 | |
|   function parseTables(input) {
 | |
|     var result = {}
 | |
|     if (isArray(input)) {
 | |
|       for (var i = input.length - 1; i >= 0; i--) {
 | |
|         var item = input[i]
 | |
|         result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
 | |
|       }
 | |
|     } else if (input) {
 | |
|       for (var name in input)
 | |
|         result[name.toUpperCase()] = wrapTable(name, input[name])
 | |
|     }
 | |
|     return result
 | |
|   }
 | |
| 
 | |
|   function getTable(name) {
 | |
|     return tables[name.toUpperCase()]
 | |
|   }
 | |
| 
 | |
|   function shallowClone(object) {
 | |
|     var result = {};
 | |
|     for (var key in object) if (object.hasOwnProperty(key))
 | |
|       result[key] = object[key];
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   function match(string, word) {
 | |
|     var len = string.length;
 | |
|     var sub = getText(word).substr(0, len);
 | |
|     return string.toUpperCase() === sub.toUpperCase();
 | |
|   }
 | |
| 
 | |
|   function addMatches(result, search, wordlist, formatter) {
 | |
|     if (isArray(wordlist)) {
 | |
|       for (var i = 0; i < wordlist.length; i++)
 | |
|         if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
 | |
|     } else {
 | |
|       for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
 | |
|         var val = wordlist[word]
 | |
|         if (!val || val === true)
 | |
|           val = word
 | |
|         else
 | |
|           val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
 | |
|         if (match(search, val)) result.push(formatter(val))
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function cleanName(name) {
 | |
|     // Get rid name from identifierQuote and preceding dot(.)
 | |
|     if (name.charAt(0) == ".") {
 | |
|       name = name.substr(1);
 | |
|     }
 | |
|     // replace doublicated identifierQuotes with single identifierQuotes
 | |
|     // and remove single identifierQuotes
 | |
|     var nameParts = name.split(identifierQuote+identifierQuote);
 | |
|     for (var i = 0; i < nameParts.length; i++)
 | |
|       nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
 | |
|     return nameParts.join(identifierQuote);
 | |
|   }
 | |
| 
 | |
|   function insertIdentifierQuotes(name) {
 | |
|     var nameParts = getText(name).split(".");
 | |
|     for (var i = 0; i < nameParts.length; i++)
 | |
|       nameParts[i] = identifierQuote +
 | |
|         // doublicate identifierQuotes
 | |
|         nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
 | |
|         identifierQuote;
 | |
|     var escaped = nameParts.join(".");
 | |
|     if (typeof name == "string") return escaped;
 | |
|     name = shallowClone(name);
 | |
|     name.text = escaped;
 | |
|     return name;
 | |
|   }
 | |
| 
 | |
|   function nameCompletion(cur, token, result, editor) {
 | |
|     // Try to complete table, column names and return start position of completion
 | |
|     var useIdentifierQuotes = false;
 | |
|     var nameParts = [];
 | |
|     var start = token.start;
 | |
|     var cont = true;
 | |
|     while (cont) {
 | |
|       cont = (token.string.charAt(0) == ".");
 | |
|       useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);
 | |
| 
 | |
|       start = token.start;
 | |
|       nameParts.unshift(cleanName(token.string));
 | |
| 
 | |
|       token = editor.getTokenAt(Pos(cur.line, token.start));
 | |
|       if (token.string == ".") {
 | |
|         cont = true;
 | |
|         token = editor.getTokenAt(Pos(cur.line, token.start));
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Try to complete table names
 | |
|     var string = nameParts.join(".");
 | |
|     addMatches(result, string, tables, function(w) {
 | |
|       return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
 | |
|     });
 | |
| 
 | |
|     // Try to complete columns from defaultTable
 | |
|     addMatches(result, string, defaultTable, function(w) {
 | |
|       return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
 | |
|     });
 | |
| 
 | |
|     // Try to complete columns
 | |
|     string = nameParts.pop();
 | |
|     var table = nameParts.join(".");
 | |
| 
 | |
|     var alias = false;
 | |
|     var aliasTable = table;
 | |
|     // Check if table is available. If not, find table by Alias
 | |
|     if (!getTable(table)) {
 | |
|       var oldTable = table;
 | |
|       table = findTableByAlias(table, editor);
 | |
|       if (table !== oldTable) alias = true;
 | |
|     }
 | |
| 
 | |
|     var columns = getTable(table);
 | |
|     if (columns && columns.columns)
 | |
|       columns = columns.columns;
 | |
| 
 | |
|     if (columns) {
 | |
|       addMatches(result, string, columns, function(w) {
 | |
|         var tableInsert = table;
 | |
|         if (alias == true) tableInsert = aliasTable;
 | |
|         if (typeof w == "string") {
 | |
|           w = tableInsert + "." + w;
 | |
|         } else {
 | |
|           w = shallowClone(w);
 | |
|           w.text = tableInsert + "." + w.text;
 | |
|         }
 | |
|         return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     return start;
 | |
|   }
 | |
| 
 | |
|   function eachWord(lineText, f) {
 | |
|     var words = lineText.split(/\s+/)
 | |
|     for (var i = 0; i < words.length; i++)
 | |
|       if (words[i]) f(words[i].replace(/[,;]/g, ''))
 | |
|   }
 | |
| 
 | |
|   function findTableByAlias(alias, editor) {
 | |
|     var doc = editor.doc;
 | |
|     var fullQuery = doc.getValue();
 | |
|     var aliasUpperCase = alias.toUpperCase();
 | |
|     var previousWord = "";
 | |
|     var table = "";
 | |
|     var separator = [];
 | |
|     var validRange = {
 | |
|       start: Pos(0, 0),
 | |
|       end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
 | |
|     };
 | |
| 
 | |
|     //add separator
 | |
|     var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
 | |
|     while(indexOfSeparator != -1) {
 | |
|       separator.push(doc.posFromIndex(indexOfSeparator));
 | |
|       indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
 | |
|     }
 | |
|     separator.unshift(Pos(0, 0));
 | |
|     separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
 | |
| 
 | |
|     //find valid range
 | |
|     var prevItem = null;
 | |
|     var current = editor.getCursor()
 | |
|     for (var i = 0; i < separator.length; i++) {
 | |
|       if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
 | |
|         validRange = {start: prevItem, end: separator[i]};
 | |
|         break;
 | |
|       }
 | |
|       prevItem = separator[i];
 | |
|     }
 | |
| 
 | |
|     if (validRange.start) {
 | |
|       var query = doc.getRange(validRange.start, validRange.end, false);
 | |
| 
 | |
|       for (var i = 0; i < query.length; i++) {
 | |
|         var lineText = query[i];
 | |
|         eachWord(lineText, function(word) {
 | |
|           var wordUpperCase = word.toUpperCase();
 | |
|           if (wordUpperCase === aliasUpperCase && getTable(previousWord))
 | |
|             table = previousWord;
 | |
|           if (wordUpperCase !== CONS.ALIAS_KEYWORD)
 | |
|             previousWord = word;
 | |
|         });
 | |
|         if (table) break;
 | |
|       }
 | |
|     }
 | |
|     return table;
 | |
|   }
 | |
| 
 | |
|   CodeMirror.registerHelper("hint", "sql", function(editor, options) {
 | |
|     tables = parseTables(options && options.tables)
 | |
|     var defaultTableName = options && options.defaultTable;
 | |
|     var disableKeywords = options && options.disableKeywords;
 | |
|     defaultTable = defaultTableName && getTable(defaultTableName);
 | |
|     keywords = getKeywords(editor);
 | |
|     identifierQuote = getIdentifierQuote(editor);
 | |
| 
 | |
|     if (defaultTableName && !defaultTable)
 | |
|       defaultTable = findTableByAlias(defaultTableName, editor);
 | |
| 
 | |
|     defaultTable = defaultTable || [];
 | |
| 
 | |
|     if (defaultTable.columns)
 | |
|       defaultTable = defaultTable.columns;
 | |
| 
 | |
|     var cur = editor.getCursor();
 | |
|     var result = [];
 | |
|     var token = editor.getTokenAt(cur), start, end, search;
 | |
|     if (token.end > cur.ch) {
 | |
|       token.end = cur.ch;
 | |
|       token.string = token.string.slice(0, cur.ch - token.start);
 | |
|     }
 | |
| 
 | |
|     if (token.string.match(/^[.`"\w@]\w*$/)) {
 | |
|       search = token.string;
 | |
|       start = token.start;
 | |
|       end = token.end;
 | |
|     } else {
 | |
|       start = end = cur.ch;
 | |
|       search = "";
 | |
|     }
 | |
|     if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) {
 | |
|       start = nameCompletion(cur, token, result, editor);
 | |
|     } else {
 | |
|       var objectOrClass = function(w, className) {
 | |
|         if (typeof w === "object") {
 | |
|           w.className = className;
 | |
|         } else {
 | |
|           w = { text: w, className: className };
 | |
|         }
 | |
|         return w;
 | |
|       };
 | |
|     addMatches(result, search, defaultTable, function(w) {
 | |
|         return objectOrClass(w, "CodeMirror-hint-table CodeMirror-hint-default-table");
 | |
|     });
 | |
|     addMatches(
 | |
|         result,
 | |
|         search,
 | |
|         tables, function(w) {
 | |
|           return objectOrClass(w, "CodeMirror-hint-table");
 | |
|         }
 | |
|     );
 | |
|     if (!disableKeywords)
 | |
|       addMatches(result, search, keywords, function(w) {
 | |
|           return objectOrClass(w.toUpperCase(), "CodeMirror-hint-keyword");
 | |
|       });
 | |
|   }
 | |
| 
 | |
|     return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
 | |
|   });
 | |
| });
 |