Selaa lähdekoodia

HUE-4771 [editor] Use parser locations for click to open tables in the metastore

Johan Ahlen 9 vuotta sitten
vanhempi
commit
1dc7527

+ 6 - 0
desktop/core/src/desktop/static/desktop/css/hue3.css

@@ -2798,3 +2798,9 @@ body {
 input[type='password']::-ms-reveal {
   display: none;
 }
+
+.hue-ace-error {
+  position: absolute;
+  border-bottom: 1px dotted #FF0000;
+  border-radius: 0 !important;
+}

+ 57 - 0
desktop/core/src/desktop/static/desktop/js/aceSqlWorker.js

@@ -0,0 +1,57 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+importScripts('/static/desktop/ext/js/require.js');
+
+require([
+  'autocomplete/sql',
+  'sqlFunctions'
+], function (sqlParser, sqlFunctions) {
+
+  this.throttle = -1;
+
+  this.onmessage = function (msg) {
+    clearTimeout(this.throttle);
+    this.throttle = setTimeout(function () {
+      if (msg.data) {
+        var errors = [];
+        var locations = [];
+        var lineCount = 0;
+        msg.data.text.split(';').forEach(function (statement) {
+          var parseResult = sqlParser.parseSql(statement + ' ', '', msg.data.type, sqlFunctions, false);
+          if (parseResult.errors) {
+            parseResult.errors.forEach(function (error) {
+              if (error.token.indexOf('CURSOR') === -1) {
+                error.loc.first_line += lineCount;
+                error.loc.last_line += lineCount;
+                errors.push(error);
+              }
+            })
+          }
+          if (parseResult.locations) {
+            parseResult.locations.forEach(function (location) {
+              location.location.first_line += lineCount;
+              location.location.last_line += lineCount;
+              locations.push(location);
+            })
+          }
+          lineCount += statement.split(/\r\n|\r|\n/).length - 1;
+        });
+        postMessage({ errors: errors, locations: locations });
+      }
+    }, 400);
+  }
+});

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

@@ -2678,6 +2678,8 @@
         setShowGutter: true
       };
 
+      var errorHighlightingEnabled = snippet.getApiHelper().getFromTotalStorage('hue.ace', 'errorHighlightingEnabled', false);
+
       editor.customMenuOptions = {
         setEnableAutocompleter: function (enabled) {
           editor.setOption('enableBasicAutocompletion', enabled);
@@ -2703,8 +2705,53 @@
         }
       };
 
+      if (window.Worker) {
+        editor.customMenuOptions.setExperimentalErrorHighlighting = function (enabled) {
+          errorHighlightingEnabled = enabled;
+          snippet.getApiHelper().setInTotalStorage('hue.ace', 'errorHighlightingEnabled', enabled);
+        };
+        editor.customMenuOptions.getExperimentalErrorHighlighting = function () {
+          return errorHighlightingEnabled;
+        };
+      }
+
       $.extend(editorOptions, aceOptions);
 
+      if (window.Worker) {
+        var aceSqlWorker = new Worker('/static/desktop/js/aceSqlWorker.js');
+        var AceRange = ace.require('ace/range').Range;
+        aceSqlWorker.onmessage = function(e) {
+          if (errorHighlightingEnabled) {
+            for (var id in editor.session.getMarkers()) {
+              var marker = editor.session.getMarkers()[id];
+              if (marker.clazz == "hue-ace-error"){
+                editor.session.removeMarker(marker.id);
+              }
+            }
+
+            e.data.errors.forEach(function (error) {
+              if (error.expected.length > 0) {
+                var token = editor.session.getTokenAt(error.loc.first_line - 1, error.loc.first_column);
+                token.error = error;
+                editor.session.addMarker(new AceRange(error.loc.first_line - 1, error.loc.first_column, error.loc.last_line - 1, error.loc.last_column), 'hue-ace-error', 'fail');
+              }
+            });
+          }
+          e.data.locations.forEach(function (location) {
+            var token = editor.session.getTokenAt(location.location.first_line - 1, location.location.first_column);
+            if (token && location.type === 'table') {
+              token.parseLocation = location;
+            }
+          });
+        };
+
+        editor.on("change", function (e) {
+          if (snippet.getAceMode() === 'ace/mode/hive' || snippet.getAceMode() === 'ace/mode/impala') {
+            aceSqlWorker.postMessage({ text: editor.getValue(), type: snippet.type() });
+          }
+        });
+      }
+
       editorOptions['enableBasicAutocompletion'] = snippet.getApiHelper().getFromTotalStorage('hue.ace', 'enableBasicAutocompletion', true);
       if (editorOptions['enableBasicAutocompletion']) {
         editorOptions['enableLiveAutocompletion'] = snippet.getApiHelper().getFromTotalStorage('hue.ace', 'enableLiveAutocompletion', true);
@@ -2928,11 +2975,7 @@
                 } else if (typeof token.columns !== 'undefined') {
                   colHiglightCallback(token.columns);
                 }
-              }
-              else if (token.value.indexOf("'/") == 0 && token.value.lastIndexOf("'") == token.value.length - 1 ||
-                  token.value.indexOf("\"/") == 0 && token.value.lastIndexOf("\"") == token.value.length - 1 ||
-                  currentAssistTables[token.value]) {
-                // add highlight for the clicked token
+              } else if (token.parseLocation) {
                 var range = new AceRange(docPos.row, token.start, docPos.row, token.start + token.value.length);
                 editor.session.removeMarker(this.marker);
                 this.marker = editor.session.addMarker(range, 'ace_bracket red');
@@ -2941,18 +2984,46 @@
                 if ($.totalStorage("hue.ace.showLinkTooltips") == null || $.totalStorage("hue.ace.showLinkTooltips")) {
                   this.show(null, this.x + 10, this.y + 10);
                 }
-                this.link = token;
+                this.link = token.parseLocation;
                 this.isClearable = true
-              }
-              else {
+              } else if (token.error) {
+                this.setText(this.createErrorMessage(token.error));
+                this.show(null, this.x + 10, this.y + 10);
+                this.isClearable = true
+              } else {
                 this.clear();
               }
-            }
-            else {
+            } else {
               this.clear();
             }
           };
 
+          this.createErrorMessage = function (error) {
+            var expectedTokens = {};
+            error.expected.forEach(function (expected) {
+              expected = expected.substring(1, expected.length-1);
+              if (expected.indexOf('<') === 0) {
+                expected = expected.substring(expected.indexOf('>') + 1);
+              }
+              if (/^[A-Z]+$/.test(expected) && !/PARTIAL_CURSOR|CURSOR|EOF/.test(expected)) {
+                expectedTokens[expected] = true;
+              }
+            });
+            var errorString = 'Expected: ';
+            var count = 1;
+            Object.keys(expectedTokens).forEach(function (expected) {
+              if (count > 1) {
+                errorString += ', ';
+              }
+              if (count % 5 === 0) {
+                errorString += '\n';
+              }
+              errorString += expected;
+              count++;
+            });
+            return errorString;
+          };
+
           this.clear = function () {
             if (this.isClearable) {
               this.hide(); // hides the tooltip
@@ -3003,7 +3074,15 @@
       HueLink = ace.require("huelink").HueLink;
       editor.hueLink = new HueLink(editor);
       editor.hueLink.on("open", function (token) {
-        if (token.value === " * " && token.columns.length > 0) {
+        if (token.location) {
+          if (token.type === 'table') {
+            if (token.identifierChain.length === 1) {
+              window.open("/metastore/table/" + snippet.database() + "/" + token.identifierChain[0].name, '_blank');
+            } else if (token.identifierChain.length === 2) {
+              window.open("/metastore/table/" + token.identifierChain[0].name + '/' + token.identifierChain[1].name, '_blank');
+            }
+          }
+        } else if (token.value === " * " && token.columns.length > 0) {
           editor.session.replace(token.range, token.columns.join(", "))
         } else if (token.value.indexOf("'/") == 0 && token.value.lastIndexOf("'") == token.value.length - 1) {
           window.open("/filebrowser/#" + token.value.replace(/'/gi, ""), '_blank');