瀏覽代碼

HUE-5097 [editor] Plugin the new SQL formatter in the editor

Romain Rigaux 9 年之前
父節點
當前提交
0c66cc9992

+ 11 - 8
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -3287,15 +3287,18 @@
         bindKey: {win: "Ctrl-i", mac: "Command-i|Ctrl-i"},
         exec: function () {
           if (['ace/mode/hive', 'ace/mode/impala', 'ace/mode/sql', 'ace/mode/mysql', 'ace/mode/pgsql', 'ace/mode/sqlite', 'ace/mode/oracle'].indexOf(snippet.getAceMode()) > -1) {
-            if (vkbeautify) {
-              if (editor.getSelectedText() != '') {
-                editor.session.replace(editor.session.selection.getRange(), vkbeautify.sql(editor.getSelectedText(), 2));
-              }
-              else {
-                editor.setValue(vkbeautify.sql(editor.getValue(), 2), 1);
-                snippet.statement_raw(removeUnicodes(editor.getValue()));
+            $.post("/notebook/api/format", {
+              statements: editor.getSelectedText() != '' ? editor.getSelectedText() : editor.getValue()
+            }, function(data) {
+              if (data.status == 0) {
+                 if (editor.getSelectedText() != '') {
+                  editor.session.replace(editor.session.selection.getRange(), data.formatted_statements);
+                } else {
+                  editor.setValue(data.formatted_statements);
+                  snippet.statement_raw(removeUnicodes(editor.getValue()));
+                }
               }
-            }
+            });
           }
         }
       });

+ 0 - 378
desktop/core/src/desktop/static/desktop/js/vkbeautify.js

@@ -1,378 +0,0 @@
-/**
- * Modified version from https://github.com/enricoberti/vkBeautify
- *
- * vkBeautify - javascript plugin to pretty-print or minify text in XML, JSON, CSS and SQL formats.
- *
- * Version - 0.99.00.beta
- * Copyright (c) 2012 Vadim Kiryukhin
- * vkiryukhin @ gmail.com
- * http://www.eslinstructor.net/vkbeautify/
- *
- * Dual licensed under the MIT and GPL licenses:
- *   http://www.opensource.org/licenses/mit-license.php
- *   http://www.gnu.org/licenses/gpl.html
- *
- *   Pretty print
- *
- *        vkbeautify.xml(text [,indent_pattern]);
- *        vkbeautify.json(text [,indent_pattern]);
- *        vkbeautify.css(text [,indent_pattern]);
- *        vkbeautify.sql(text [,indent_pattern]);
- *
- *        @text - String; text to beatufy;
- *        @indent_pattern - Integer | String;
- *                Integer:  number of white spaces;
- *                String:   character string to visualize indentation ( can also be a set of white spaces )
- *   Minify
- *
- *        vkbeautify.xmlmin(text [,preserve_comments]);
- *        vkbeautify.jsonmin(text);
- *        vkbeautify.cssmin(text [,preserve_comments]);
- *        vkbeautify.sqlmin(text);
- *
- *        @text - String; text to minify;
- *        @preserve_comments - Bool; [optional];
- *                Set this flag to true to prevent removing comments from @text ( minxml and mincss functions only. )
- *
- *   Examples:
- *        vkbeautify.xml(text); // pretty print XML
- *        vkbeautify.json(text, 4 ); // pretty print JSON
- *        vkbeautify.css(text, '. . . .'); // pretty print CSS
- *        vkbeautify.sql(text, '----'); // pretty print SQL
- *
- *        vkbeautify.xmlmin(text, true);// minify XML, preserve comments
- *        vkbeautify.jsonmin(text);// minify JSON
- *        vkbeautify.cssmin(text);// minify CSS, remove comments ( default )
- *        vkbeautify.sqlmin(text);// minify SQL
- *
- */
-
-(function () {
-
-  function createShiftArr(step) {
-
-    var space = '    ';
-
-    if (isNaN(parseInt(step))) {  // argument is string
-      space = step;
-    } else { // argument is integer
-      switch (step) {
-        case 1:
-          space = ' ';
-          break;
-        case 2:
-          space = '  ';
-          break;
-        case 3:
-          space = '   ';
-          break;
-        case 4:
-          space = '    ';
-          break;
-        case 5:
-          space = '     ';
-          break;
-        case 6:
-          space = '      ';
-          break;
-        case 7:
-          space = '       ';
-          break;
-        case 8:
-          space = '        ';
-          break;
-        case 9:
-          space = '         ';
-          break;
-        case 10:
-          space = '          ';
-          break;
-        case 11:
-          space = '           ';
-          break;
-        case 12:
-          space = '            ';
-          break;
-      }
-    }
-
-    var shift = ['\n']; // array of shifts
-    for (ix = 0; ix < 100; ix++) {
-      shift.push(shift[ix] + space);
-    }
-    return shift;
-  }
-
-  function vkbeautify() {
-    this.step = '    '; // 4 spaces
-    this.shift = createShiftArr(this.step);
-  };
-
-  vkbeautify.prototype.xml = function (text, step) {
-
-    var ar = text.replace(/>\s{0,}</g, "><")
-        .replace(/</g, "~::~<")
-        .replace(/\s*xmlns\:/g, "~::~xmlns:")
-        .replace(/\s*xmlns\=/g, "~::~xmlns=")
-        .split('~::~'),
-      len = ar.length,
-      inComment = false,
-      deep = 0,
-      str = '',
-      ix = 0,
-      shift = step ? createShiftArr(step) : this.shift;
-
-    for (ix = 0; ix < len; ix++) {
-      // start comment or <![CDATA[...]]> or <!DOCTYPE //
-      if (ar[ix].search(/<!/) > -1) {
-        str += shift[deep] + ar[ix];
-        inComment = true;
-        // end comment  or <![CDATA[...]]> //
-        if (ar[ix].search(/-->/) > -1 || ar[ix].search(/\]>/) > -1 || ar[ix].search(/!DOCTYPE/) > -1) {
-          inComment = false;
-        }
-      } else
-      // end comment  or <![CDATA[...]]> //
-      if (ar[ix].search(/-->/) > -1 || ar[ix].search(/\]>/) > -1) {
-        str += ar[ix];
-        inComment = false;
-      } else
-      // <elm></elm> //
-      if (/^<\w/.exec(ar[ix - 1]) && /^<\/\w/.exec(ar[ix]) &&
-        /^<[\w:\-\.\,]+/.exec(ar[ix - 1]) == /^<\/[\w:\-\.\,]+/.exec(ar[ix])[0].replace('/', '')) {
-        str += ar[ix];
-        if (!inComment) deep--;
-      } else
-      // <elm> //
-      if (ar[ix].search(/<\w/) > -1 && ar[ix].search(/<\//) == -1 && ar[ix].search(/\/>/) == -1) {
-        str = !inComment ? str += shift[deep++] + ar[ix] : str += ar[ix];
-      } else
-      // <elm>...</elm> //
-      if (ar[ix].search(/<\w/) > -1 && ar[ix].search(/<\//) > -1) {
-        str = !inComment ? str += shift[deep] + ar[ix] : str += ar[ix];
-      } else
-      // </elm> //
-      if (ar[ix].search(/<\//) > -1) {
-        str = !inComment ? str += shift[--deep] + ar[ix] : str += ar[ix];
-      } else
-      // <elm/> //
-      if (ar[ix].search(/\/>/) > -1) {
-        str = !inComment ? str += shift[deep] + ar[ix] : str += ar[ix];
-      } else
-      // <? xml ... ?> //
-      if (ar[ix].search(/<\?/) > -1) {
-        str += shift[deep] + ar[ix];
-      } else
-      // xmlns //
-      if (ar[ix].search(/xmlns\:/) > -1 || ar[ix].search(/xmlns\=/) > -1) {
-        str += shift[deep] + ar[ix];
-      }
-
-      else {
-        str += ar[ix];
-      }
-    }
-
-    return (str[0] == '\n') ? str.slice(1) : str;
-  }
-
-  vkbeautify.prototype.json = function (text, step) {
-
-    var step = step ? step : this.step;
-
-    if (typeof JSON === 'undefined') return text;
-
-    if (typeof text === "string") return JSON.stringify(JSON.parse(text), null, step);
-    if (typeof text === "object") return JSON.stringify(text, null, step);
-
-    return text; // text is not string nor object
-  }
-
-  vkbeautify.prototype.css = function (text, step) {
-
-    var ar = text.replace(/\s{1,}/g, ' ')
-        .replace(/\{/g, "{~::~")
-        .replace(/\}/g, "~::~}~::~")
-        .replace(/\;/g, ";~::~")
-        .replace(/\/\*/g, "~::~/*")
-        .replace(/\*\//g, "*/~::~")
-        .replace(/~::~\s{0,}~::~/g, "~::~")
-        .split('~::~'),
-      len = ar.length,
-      deep = 0,
-      str = '',
-      ix = 0,
-      shift = step ? createShiftArr(step) : this.shift;
-
-    for (ix = 0; ix < len; ix++) {
-
-      if (/\{/.exec(ar[ix])) {
-        str += shift[deep++] + ar[ix];
-      } else if (/\}/.exec(ar[ix])) {
-        str += shift[--deep] + ar[ix];
-      } else if (/\*\\/.exec(ar[ix])) {
-        str += shift[deep] + ar[ix];
-      }
-      else {
-        str += shift[deep] + ar[ix];
-      }
-    }
-    return str.replace(/^\n{1,}/, '');
-  }
-
-//----------------------------------------------------------------------------
-
-  function isSubquery(str, parenthesisLevel) {
-    return parenthesisLevel - (str.replace(/\(/g, '').length - str.replace(/\)/g, '').length )
-  }
-
-  function split_sql(str, tab) {
-
-    return str.replace(/\s{1,}/g, " ")
-
-      .replace(/ AND /ig, "~::~" + tab + tab + "AND ")
-      .replace(/ BETWEEN /ig, "~::~" + tab + "BETWEEN ")
-      .replace(/ CASE /ig, "~::~" + tab + "CASE ")
-      .replace(/ ELSE /ig, "~::~" + tab + "ELSE ")
-      .replace(/ END /ig, "~::~" + tab + "END ")
-      .replace(/ FROM /ig, "~::~FROM ")
-      .replace(/ GROUP\s{1,}BY /ig, "~::~GROUP BY ")
-      .replace(/ HAVING /ig, "~::~HAVING ")
-      .replace(/ IN /ig, " IN ")
-
-      .replace(/ JOIN /ig, "~::~JOIN ")
-      .replace(/ CROSS~::~{1,}JOIN /ig, "~::~CROSS JOIN ")
-      .replace(/ INNER~::~{1,}JOIN /ig, "~::~INNER JOIN ")
-      .replace(/ LEFT~::~{1,}JOIN /ig, "~::~LEFT JOIN ")
-      .replace(/ RIGHT~::~{1,}JOIN /ig, "~::~RIGHT JOIN ")
-
-      .replace(/ ON /ig, "~::~" + tab + "ON ")
-      .replace(/ OR /ig, "~::~" + tab + tab + "OR ")
-      .replace(/ ORDER\s{1,}BY /ig, "~::~ORDER BY ")
-      .replace(/ OVER /ig, "~::~" + tab + "OVER ")
-
-      .replace(/\(\s{0,}SELECT /ig, "~::~(SELECT ")
-      .replace(/\)\s{0,}SELECT /ig, ")~::~SELECT ")
-
-      .replace(/ THEN /ig, " THEN~::~" + tab + "")
-      .replace(/ UNION /ig, "~::~UNION~::~")
-      .replace(/ USING /ig, "~::~USING ")
-      .replace(/ WHEN /ig, "~::~" + tab + "WHEN ")
-      .replace(/ WHERE /ig, "~::~WHERE ")
-      .replace(/ WITH /ig, "~::~WITH ")
-      .replace(/ LIMIT /ig, "~::~LIMIT ")
-
-      .replace(/ ALL /ig, " ALL ")
-      .replace(/ AS /ig, " AS ")
-      .replace(/ ASC /ig, " ASC ")
-      .replace(/ DESC /ig, " DESC ")
-      .replace(/ DISTINCT /ig, " DISTINCT ")
-      .replace(/ EXISTS /ig, " EXISTS ")
-      .replace(/ NOT /ig, " NOT ")
-      .replace(/ NULL /ig, " NULL ")
-      .replace(/ LIKE /ig, " LIKE ")
-      .replace(/ SET /ig, " SET ")
-
-      .replace(/~::~{1,}/g, "~::~")
-      .split('~::~');
-  }
-
-  vkbeautify.prototype.sql = function (text, step) {
-
-    var ar_by_quote = text.replace(/\/\*.*?\*\/|--.*?\n/g, '$&~::~')
-        .replace(/\s{1,}/g, " ")
-        .replace(/\'/ig, "~::~\'")
-        .split('~::~'),
-      len = ar_by_quote.length,
-      ar = [],
-      deep = 0,
-      tab = this.step,//+this.step,
-      inComment = true,
-      inQuote = false,
-      parenthesisLevel = 0,
-      str = '',
-      ix = 0,
-      shift = step ? createShiftArr(step) : this.shift;
-    ;
-
-    for (ix = 0; ix < len; ix++) {
-      if (ix % 2) {
-        ar = ar.concat(ar_by_quote[ix]);
-      } else {
-        ar = ar.concat(split_sql(ar_by_quote[ix], tab));
-      }
-    }
-
-    len = ar.length;
-    for (ix = 0; ix < len; ix++) {
-
-      parenthesisLevel = isSubquery(ar[ix], parenthesisLevel);
-
-      if (/\s{0,}\s{0,}SELECT\s{0,}/.exec(ar[ix])) {
-        ar[ix] = ar[ix].replace(/\,/g, ",\n" + tab + tab + "")
-      }
-
-      if (/\s{0,}\s{0,}SET\s{0,}/.exec(ar[ix])) {
-        ar[ix] = ar[ix].replace(/\,/g, ",\n" + tab + tab + "")
-      }
-
-      if (/\s{0,}\(\s{0,}SELECT\s{0,}/.exec(ar[ix])) {
-        deep++;
-        str += shift[deep] + ar[ix];
-      } else if (/\'/.exec(ar[ix])) {
-        if (parenthesisLevel < 1 && deep) {
-          deep--;
-        }
-        str += ar[ix];
-      }
-      else {
-        str += shift[deep] + ar[ix];
-        if (parenthesisLevel < 1 && deep) {
-          deep--;
-        }
-      }
-      var junk = 0;
-    }
-
-    str = str.replace(/^\n{1,}/, '').replace(/\n{1,}/g, "\n").replace(/;/gi, ";\n\n");
-    return str;
-  }
-
-
-  vkbeautify.prototype.xmlmin = function (text, preserveComments) {
-
-    var str = preserveComments ? text
-      : text.replace(/\<![ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t]*)\>/g, "")
-      .replace(/[ \r\n\t]{1,}xmlns/g, ' xmlns');
-    return str.replace(/>\s{0,}</g, "><");
-  }
-
-  vkbeautify.prototype.jsonmin = function (text) {
-
-    if (typeof JSON === 'undefined') return text;
-
-    return JSON.stringify(JSON.parse(text), null, 0);
-
-  }
-
-  vkbeautify.prototype.cssmin = function (text, preserveComments) {
-
-    var str = preserveComments ? text
-      : text.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\//g, "");
-
-    return str.replace(/\s{1,}/g, ' ')
-      .replace(/\{\s{1,}/g, "{")
-      .replace(/\}\s{1,}/g, "}")
-      .replace(/\;\s{1,}/g, ";")
-      .replace(/\/\*\s{1,}/g, "/*")
-      .replace(/\*\/\s{1,}/g, "*/");
-  }
-
-  vkbeautify.prototype.sqlmin = function (text) {
-    return text.replace(/\s{1,}/g, " ").replace(/\s{1,}\(/, "(").replace(/\s{1,}\)/, ")");
-  }
-
-  window.vkbeautify = new vkbeautify();
-
-})();
-

+ 13 - 0
desktop/libs/notebook/src/notebook/api.py

@@ -18,6 +18,8 @@
 import json
 import logging
 
+import sqlparse
+
 from django.core.urlresolvers import reverse
 from django.db.models import Q
 from django.http import HttpResponseBadRequest, HttpResponseRedirect
@@ -587,6 +589,17 @@ def explain(request):
   return JsonResponse(response)
 
 
+@require_POST
+@api_error_handler
+def format(request):
+  response = {'status': 0}
+
+  statements = request.POST.get('statements', '')
+  response['formatted_statements'] = sqlparse.format(statements, reindent=True, keyword_case='upper') # SQL only currently
+
+  return JsonResponse(response)
+
+
 @require_GET
 @api_error_handler
 def github_fetch(request):

+ 15 - 8
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -939,14 +939,21 @@ var EditorViewModel = (function() {
     });
 
     self.format = function () {
-      if (self.isSqlDialect() && vkbeautify) {
-        if (self.ace().getSelectedText() != '') {
-          self.ace().session.replace(self.ace().session.selection.getRange(), vkbeautify.sql(self.ace().getSelectedText(), 2));
-        }
-        else {
-          self.statement_raw(vkbeautify.sql(self.statement_raw(), 2));
-          self.ace().setValue(self.statement_raw(), 1);
-        }
+      if (self.isSqlDialect()) {
+        $.post("/notebook/api/format", {
+          statements: self.ace().getSelectedText() != '' ? self.ace().getSelectedText() : self.statement_raw()
+        }, function(data) {
+          if (data.status == 0) {
+            if (self.ace().getSelectedText() != '') {
+              self.ace().session.replace(self.ace().session.selection.getRange(), data.formatted_statements);
+            } else {
+              self.statement_raw(data.formatted_statements);
+              self.ace().setValue(self.statement_raw(), 1);
+            }
+          } else {
+            self._ajaxError(data);
+          }
+        });
       }
       logGA('format');
     };

+ 0 - 1
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -122,7 +122,6 @@ from notebook.conf import ENABLE_QUERY_BUILDER, ENABLE_QUERY_SCHEDULING, ENABLE_
 
 <script src="${ static('desktop/ext/js/bootstrap-editable.min.js') }" type="text/javascript" charset="utf-8"></script>
 <script src="${ static('desktop/ext/chosen/chosen.jquery.min.js') }" type="text/javascript" charset="utf-8"></script>
-<script src="${ static('desktop/js/vkbeautify.js') }" type="text/javascript" charset="utf-8"></script>
 
 <script src="${ static('desktop/js/hue.geo.js') }" type="text/javascript" charset="utf-8"></script>
 <script src="${ static('desktop/js/hue.colors.js') }" type="text/javascript" charset="utf-8"></script>

+ 1 - 0
desktop/libs/notebook/src/notebook/urls.py

@@ -60,6 +60,7 @@ urlpatterns += patterns('notebook.api',
   url(r'^api/get_logs/?$', 'get_logs', name='get_logs'),
 
   url(r'^api/explain/?$', 'explain', name='explain'),
+  url(r'^api/format/?$', 'format', name='format'),
 
   url(r'^api/get_history/?', 'get_history', name='get_history'),
   url(r'^api/clear_history/?', 'clear_history', name='clear_history'),