sql-hint.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: http://codemirror.net/LICENSE
  3. (function(mod) {
  4. if (typeof exports == "object" && typeof module == "object") // CommonJS
  5. mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
  6. else if (typeof define == "function" && define.amd) // AMD
  7. define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
  8. else // Plain browser env
  9. mod(CodeMirror);
  10. })(function(CodeMirror) {
  11. "use strict";
  12. var tables;
  13. var keywords;
  14. var CONS = {
  15. QUERY_DIV: ";",
  16. ALIAS_KEYWORD: "AS"
  17. };
  18. var Pos = CodeMirror.Pos;
  19. function getKeywords(editor) {
  20. var mode = editor.doc.modeOption;
  21. if (mode === "sql") mode = "text/x-sql";
  22. return CodeMirror.resolveMode(mode).keywords;
  23. }
  24. function match(string, word) {
  25. var len = string.length;
  26. var sub = word.substr(0, len);
  27. return string.toUpperCase() === sub.toUpperCase();
  28. }
  29. function addMatches(result, search, wordlist, formatter) {
  30. for (var word in wordlist) {
  31. if (!wordlist.hasOwnProperty(word)) continue;
  32. if (Array.isArray(wordlist)) {
  33. word = wordlist[word];
  34. }
  35. if (match(search, word)) {
  36. result.push(formatter(word));
  37. }
  38. }
  39. }
  40. function columnCompletion(result, editor) {
  41. var cur = editor.getCursor();
  42. var token = editor.getTokenAt(cur);
  43. var string = token.string.substr(1);
  44. var prevCur = Pos(cur.line, token.start);
  45. var table = editor.getTokenAt(prevCur).string;
  46. if (!tables.hasOwnProperty(table))
  47. table = findTableByAlias(table, editor);
  48. var columns = tables[table];
  49. if (!columns) return;
  50. addMatches(result, string, columns, function(w) {return "." + w;});
  51. }
  52. function eachWord(lineText, f) {
  53. if (!lineText) return;
  54. var excepted = /[,;]/g;
  55. var words = lineText.split(" ");
  56. for (var i = 0; i < words.length; i++) {
  57. f(words[i]?words[i].replace(excepted, '') : '');
  58. }
  59. }
  60. function convertCurToNumber(cur) {
  61. // max characters of a line is 999,999.
  62. return cur.line + cur.ch / Math.pow(10, 6);
  63. }
  64. function convertNumberToCur(num) {
  65. return Pos(Math.floor(num), +num.toString().split('.').pop());
  66. }
  67. function findTableByAlias(alias, editor) {
  68. var doc = editor.doc;
  69. var fullQuery = doc.getValue();
  70. var aliasUpperCase = alias.toUpperCase();
  71. var previousWord = "";
  72. var table = "";
  73. var separator = [];
  74. var validRange = {
  75. start: Pos(0, 0),
  76. end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
  77. };
  78. //add separator
  79. var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
  80. while(indexOfSeparator != -1) {
  81. separator.push(doc.posFromIndex(indexOfSeparator));
  82. indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
  83. }
  84. separator.unshift(Pos(0, 0));
  85. separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
  86. //find valid range
  87. var prevItem = 0;
  88. var current = convertCurToNumber(editor.getCursor());
  89. for (var i=0; i< separator.length; i++) {
  90. var _v = convertCurToNumber(separator[i]);
  91. if (current > prevItem && current <= _v) {
  92. validRange = { start: convertNumberToCur(prevItem), end: convertNumberToCur(_v) };
  93. break;
  94. }
  95. prevItem = _v;
  96. }
  97. var query = doc.getRange(validRange.start, validRange.end, false);
  98. for (var i = 0; i < query.length; i++) {
  99. var lineText = query[i];
  100. eachWord(lineText, function(word) {
  101. var wordUpperCase = word.toUpperCase();
  102. if (wordUpperCase === aliasUpperCase && tables.hasOwnProperty(previousWord)) {
  103. table = previousWord;
  104. }
  105. if (wordUpperCase !== CONS.ALIAS_KEYWORD) {
  106. previousWord = word;
  107. }
  108. });
  109. if (table) break;
  110. }
  111. return table;
  112. }
  113. CodeMirror.registerHelper("hint", "sql", function(editor, options) {
  114. tables = (options && options.tables) || {};
  115. keywords = keywords || getKeywords(editor);
  116. var cur = editor.getCursor();
  117. var result = [];
  118. var token = editor.getTokenAt(cur), start, end, search;
  119. if (token.string.match(/^[.\w@]\w*$/)) {
  120. search = token.string;
  121. start = token.start;
  122. end = token.end;
  123. } else {
  124. start = end = cur.ch;
  125. search = "";
  126. }
  127. if (search.charAt(0) == ".") {
  128. columnCompletion(result, editor);
  129. if (!result.length) {
  130. while (start && search.charAt(0) == ".") {
  131. token = editor.getTokenAt(Pos(cur.line, token.start - 1));
  132. start = token.start;
  133. search = token.string + search;
  134. }
  135. addMatches(result, search, tables, function(w) {return w;});
  136. }
  137. } else {
  138. addMatches(result, search, tables, function(w) {return w;});
  139. addMatches(result, search, keywords, function(w) {return w.toUpperCase();});
  140. }
  141. return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
  142. });
  143. });