sql-hint.js 4.8 KB

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