فهرست منبع

HUE-5764 [metadata] Style syntax check and complexity results

Enrico Berti 8 سال پیش
والد
کامیت
67c14440d9

+ 14 - 2
desktop/core/src/desktop/static/desktop/js/ace.extended.js

@@ -73,10 +73,10 @@ try {
       $(".ace-inline-button").hide();
       $(".ace-inline-button").hide();
     }
     }
 
 
-    editor.clearErrors = function() {
+    editor.clearErrorsAndWarnings = function() {
       for (var id in this.session.getMarkers()) {
       for (var id in this.session.getMarkers()) {
         var _marker = this.session.getMarkers()[id];
         var _marker = this.session.getMarkers()[id];
-        if (_marker.clazz == "ace_error-line"){
+        if (_marker.clazz == "ace_error-line" || _marker.clazz == "ace_warning-line"){
           this.session.removeMarker(_marker.id);
           this.session.removeMarker(_marker.id);
         }
         }
       };
       };
@@ -95,6 +95,18 @@ try {
       }]);
       }]);
     }
     }
 
 
+    editor.addWarning = function (message, line) {
+      var _range = new AceRange(line, 0, line, this.session.getLine(line).length);
+      this.session.addMarker(_range, "ace_warning-line");
+      this.session.setAnnotations([{
+        row: _range.start.row,
+        column: _range.start.column,
+        raw: message,
+        text: message,
+        type: "warning"
+      }]);
+    }
+
     return editor;
     return editor;
   }
   }
 }
 }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
desktop/core/src/desktop/static/desktop/js/ace/theme-hue.js


+ 41 - 12
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -573,6 +573,21 @@
     }
     }
   };
   };
 
 
+  ko.bindingHandlers.toggleIconExplanation = {
+    init: function (element, valueAccessor) {
+      var $explanation = $(element).next('.icon-explanation');
+      $explanation.addClass('hide');
+      $(element).addClass('pointer').on('click', function(){
+        if ($explanation.hasClass('hide')) {
+          $explanation.removeClass('hide');
+        }
+        else {
+          $explanation.addClass('hide');
+        }
+      });
+    }
+  };
+
   /**
   /**
    * This binding can be used to show a custom context menu on right-click,
    * This binding can be used to show a custom context menu on right-click,
    * It assumes that the context menu is nested within the bound element and
    * It assumes that the context menu is nested within the bound element and
@@ -3149,31 +3164,45 @@
         editor.setOptions({ fontSize: "14px" });
         editor.setOptions({ fontSize: "14px" });
       }
       }
 
 
-      snippet.errors.subscribe(function(newErrors) {
-        editor.clearErrors();
+      function processErrorsAndWarnings(type, list) {
+        editor.clearErrorsAndWarnings();
         var offset = 0;
         var offset = 0;
         if (snippet.isSqlDialect()) {
         if (snippet.isSqlDialect()) {
-          if (editor.getSelectedText()){
+          if (editor.getSelectedText()) {
             var selectionRange = editor.getSelectionRange();
             var selectionRange = editor.getSelectionRange();
             offset = Math.min(selectionRange.start.row, selectionRange.end.row);
             offset = Math.min(selectionRange.start.row, selectionRange.end.row);
           }
           }
-          if (snippet.result && snippet.result.statements_count() > 1){
+          if (snippet.result && snippet.result.statements_count() > 1) {
             offset = snippet.result.statement_range().start.row;
             offset = snippet.result.statement_range().start.row;
           }
           }
         }
         }
-        if (newErrors.length > 0) {
-          newErrors.forEach(function (err, cnt) {
-            if (err.line !== null) {
-              editor.addError(err.message, err.line + offset);
+        if (list.length > 0) {
+          list.forEach(function (item, cnt) {
+            if (item.line !== null) {
+              if (type === 'error') {
+                editor.addError(item.message, item.line + offset);
+              }
+              else {
+                editor.addWarning(item.message, item.line + offset);
+              }
               if (cnt == 0) {
               if (cnt == 0) {
-                editor.scrollToLine(err.line + offset, true, true, function () {});
-                if (err.col !== null){
-                  editor.renderer.scrollCursorIntoView({row: err.line + offset, column: err.col + 10}, 0.5)
+                editor.scrollToLine(item.line + offset, true, true, function () {
+                });
+                if (item.col !== null) {
+                  editor.renderer.scrollCursorIntoView({row: item.line + offset, column: item.col + 10}, 0.5)
                 }
                 }
               }
               }
             }
             }
           });
           });
         }
         }
+      }
+
+      snippet.warnings.subscribe(function (newWarnings) {
+        processErrorsAndWarnings('warning', newWarnings);
+      });
+
+      snippet.errors.subscribe(function (newErrors) {
+        processErrorsAndWarnings('error', newErrors);
       });
       });
 
 
       editor.setTheme($.totalStorage("hue.ace.theme") || "ace/theme/hue");
       editor.setTheme($.totalStorage("hue.ace.theme") || "ace/theme/hue");
@@ -3600,7 +3629,7 @@
       });
       });
 
 
       editor.on("click", function (e) {
       editor.on("click", function (e) {
-        editor.clearErrors();
+        editor.clearErrorsAndWarnings();
       });
       });
 
 
       editor.on("change", function (e) {
       editor.on("change", function (e) {

+ 49 - 0
desktop/libs/notebook/src/notebook/static/notebook/css/notebook.css

@@ -673,6 +673,55 @@ table.airy tr td {
   background: linear-gradient(to right, #fcf8e3 0%, rgba(255, 255, 255, 0) 100%);
   background: linear-gradient(to right, #fcf8e3 0%, rgba(255, 255, 255, 0) 100%);
   filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fcf8e3', endColorstr='#00ffffff', GradientType=1);
   filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fcf8e3', endColorstr='#00ffffff', GradientType=1);
 }
 }
+.alert-info-gradient {
+  border: none;
+  margin-top: -3px;
+  background: -moz-linear-gradient(left, #d9edf7 0%, rgba(255, 255, 255, 0) 100%);
+  background: -webkit-linear-gradient(left, #d9edf7 0%, rgba(255, 255, 255, 0) 100%);
+  background: -o-linear-gradient(left, #d9edf7 0%, rgba(255, 255, 255, 0) 100%);
+  background: -ms-linear-gradient(left, #d9edf7 0%, rgba(255, 255, 255, 0) 100%);
+  background: linear-gradient(to right, #d9edf7 0%, rgba(255, 255, 255, 0) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#d9edf7', endColorstr='#00ffffff', GradientType=1);
+}
+.alert-success-gradient {
+  border: none;
+  margin-top: -3px;
+  background: -moz-linear-gradient(left, #dff0d8 0%, rgba(255, 255, 255, 0) 100%);
+  background: -webkit-linear-gradient(left, #dff0d8 0%, rgba(255, 255, 255, 0) 100%);
+  background: -o-linear-gradient(left, #dff0d8 0%, rgba(255, 255, 255, 0) 100%);
+  background: -ms-linear-gradient(left, #dff0d8 0%, rgba(255, 255, 255, 0) 100%);
+  background: linear-gradient(to right, #dff0d8 0%, rgba(255, 255, 255, 0) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#dff0d8', endColorstr='#00ffffff', GradientType=1);
+}
+.alert-neutral-gradient {
+  border: none;
+  border-left: 1px solid #DBE8F1;
+  margin-top: -3px;
+  background: -moz-linear-gradient(left, #F1F1F1 0%, rgba(255, 255, 255, 0) 100%);
+  background: -webkit-linear-gradient(left, #F1F1F1 0%, rgba(255, 255, 255, 0) 100%);
+  background: -o-linear-gradient(left, #F1F1F1 0%, rgba(255, 255, 255, 0) 100%);
+  background: -ms-linear-gradient(left, #F1F1F1 0%, rgba(255, 255, 255, 0) 100%);
+  background: linear-gradient(to right, #F1F1F1 0%, rgba(255, 255, 255, 0) 100%);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F1F1F1', endColorstr='#00ffffff', GradientType=1);
+}
+.alert-neutral {
+  border: none;
+  margin-top: -3px;
+  background: transparent;
+  margin-bottom: 0;
+  padding: 0;
+  padding-right: 4px;
+  display: inline-block;
+}
+.alert-empty {
+  border: none;
+  margin-bottom: 0;
+  background: transparent;
+  padding-left: 4px;
+}
+.icon-explanation {
+  margin-left: 4px;
+}
 .hoverMsg {
 .hoverMsg {
   background: #000;
   background: #000;
   height: 100%;
   height: 100%;

+ 52 - 12
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -272,6 +272,7 @@ var EditorViewModel = (function() {
     // Ace stuff
     // Ace stuff
     self.ace = ko.observable(null);
     self.ace = ko.observable(null);
     self.errors = ko.observableArray([]);
     self.errors = ko.observableArray([]);
+    self.warnings = ko.observableArray([]);
 
 
     self.availableSnippets = vm.availableSnippets();
     self.availableSnippets = vm.availableSnippets();
     self.inFocus = ko.observable(false);
     self.inFocus = ko.observable(false);
@@ -795,13 +796,16 @@ var EditorViewModel = (function() {
     self.suggestion = ko.observable(typeof snippet.complexity != "undefined" && snippet.complexity != null ? snippet.complexity : '');
     self.suggestion = ko.observable(typeof snippet.complexity != "undefined" && snippet.complexity != null ? snippet.complexity : '');
     self.hasSuggestion = ko.observable(false);
     self.hasSuggestion = ko.observable(false);
 
 
-    self.complexityCheckActive = ko.observable(false);
+    self.complexityCheckActive = ko.observable(true);
+    self.compatibilityCheckActive = ko.observable(true);
+
+    self.isOptimizing = ko.observable(false);
 
 
     if (HAS_OPTIMIZER) {
     if (HAS_OPTIMIZER) {
       var lastRequest;
       var lastRequest;
       var lastCheckedStatement;
       var lastCheckedStatement;
 
 
-      self.delayedStatement = ko.pureComputed(self.statement).extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 5000 } });
+      self.delayedStatement = ko.pureComputed(self.statement).extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 3000 } });
 
 
       self.checkComplexity = function () {
       self.checkComplexity = function () {
         if (lastCheckedStatement === self.statement_raw()) {
         if (lastCheckedStatement === self.statement_raw()) {
@@ -811,9 +815,9 @@ var EditorViewModel = (function() {
         if (lastRequest && lastRequest.readyState < 4) {
         if (lastRequest && lastRequest.readyState < 4) {
           lastRequest.abort();
           lastRequest.abort();
         }
         }
-        self.complexity(null);
 
 
         logGA('get_query_risk');
         logGA('get_query_risk');
+        self.isOptimizing(true);
         lastRequest = $.ajax({
         lastRequest = $.ajax({
           type: 'POST',
           type: 'POST',
           url: '/notebook/api/optimizer/statement/risk',
           url: '/notebook/api/optimizer/statement/risk',
@@ -830,17 +834,30 @@ var EditorViewModel = (function() {
               $(document).trigger('error', data.message);
               $(document).trigger('error', data.message);
             }
             }
             lastCheckedStatement = self.statement_raw();
             lastCheckedStatement = self.statement_raw();
+            self.isOptimizing(false);
           }
           }
         });
         });
       };
       };
 
 
       self.delayedStatement.subscribe(function () {
       self.delayedStatement.subscribe(function () {
-        if (self.type() === 'hive' && self.complexityCheckActive()) {
-          self.checkComplexity();
+        if (self.type() === 'hive') {
+          if (self.complexityCheckActive()) {
+            self.checkComplexity();
+          }
+          if (self.compatibilityCheckActive()) {
+            self.queryCompatibility();
+          }
         }
         }
       });
       });
-      if (self.statement_raw() && self.complexityCheckActive()) {
-        window.setTimeout(self.checkComplexity, 2000);
+      if (self.statement_raw()) {
+        window.setTimeout(function(){
+          if (self.complexityCheckActive()) {
+            self.checkComplexity();
+          }
+          if (self.compatibilityCheckActive()) {
+            self.queryCompatibility();
+          }
+        }, 2000);
       }
       }
     }
     }
 
 
@@ -1120,28 +1137,51 @@ var EditorViewModel = (function() {
       });
       });
     };
     };
 
 
+    self.compatibilityTarget = ko.observable('');
+
     self.queryCompatibility = function (targetPlatform) {
     self.queryCompatibility = function (targetPlatform) {
       logGA('compatibility');
       logGA('compatibility');
-      self.suggestion(false);
+      self.isOptimizing(true);
 
 
-      if (! targetPlatform) {
+      if (!targetPlatform) {
         targetPlatform = self.type();
         targetPlatform = self.type();
       }
       }
 
 
+      self.compatibilityTarget(targetPlatform);
+
       $.post("/notebook/api/optimizer/statement/compatibility", {
       $.post("/notebook/api/optimizer/statement/compatibility", {
         notebook: ko.mapping.toJSON(notebook.getContext()),
         notebook: ko.mapping.toJSON(notebook.getContext()),
         snippet: ko.mapping.toJSON(self.getContext()),
         snippet: ko.mapping.toJSON(self.getContext()),
         sourcePlatform: self.type(),
         sourcePlatform: self.type(),
         targetPlatform: targetPlatform
         targetPlatform: targetPlatform
-      }, function(data) {
+      }, function (data) {
         if (data.status == 0) {
         if (data.status == 0) {
-         self.suggestion(ko.mapping.fromJS(data.query_compatibility));
-         self.hasSuggestion(true);
+          self.suggestion(ko.mapping.fromJS(data.query_compatibility));
+          if (self.suggestion().queryError.errorString()) {
+            var match = ERROR_REGEX.exec(self.suggestion().queryError.errorString());
+            self.warnings.push({
+              message: self.suggestion().queryError.errorString(),
+              line: match === null ? null : parseInt(match[1]) - 1,
+              col: match === null ? null : (typeof match[3] !== 'undefined' ? parseInt(match[3]) : null)
+            });
+          }
+          if (self.suggestion().parseError()) {
+            var match = ERROR_REGEX.exec(self.suggestion().parseError());
+            self.errors.push({
+              message: self.suggestion().parseError(),
+              line: match === null ? null : parseInt(match[1]) - 1,
+              col: match === null ? null : (typeof match[3] !== 'undefined' ? parseInt(match[3]) : null)
+            });
+          }
+
+          self.hasSuggestion(true);
         } else {
         } else {
           $(document).trigger("error", data.message);
           $(document).trigger("error", data.message);
         }
         }
       }).fail(function (xhr, textStatus, errorThrown) {
       }).fail(function (xhr, textStatus, errorThrown) {
         $(document).trigger("error", xhr.responseText);
         $(document).trigger("error", xhr.responseText);
+      }).always(function () {
+        self.isOptimizing(false);
       });
       });
     };
     };
 
 

+ 40 - 51
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -1401,14 +1401,21 @@ from notebook.conf import ENABLE_QUERY_BUILDER, ENABLE_QUERY_SCHEDULING, ENABLE_
 </script>
 </script>
 
 
 <script type="text/html" id="longer-operation">
 <script type="text/html" id="longer-operation">
-  <div rel="tooltip" data-placement="bottom" data-bind="tooltip, fadeVisible: showLongOperationWarning" title="${ _('The operation in the server is taking longer than expected') }" class="inline-block">
-    <i  class="fa fa-exclamation-triangle warning"></i>
+  <div rel="tooltip" data-placement="bottom" data-bind="tooltip, fadeVisible: showLongOperationWarning" title="${ _('The operation in the server is taking longer than expected') }" class="inline-block margin-right-10">
+    <i class="fa fa-exclamation-triangle warning"></i>
+  </div>
+</script>
+
+<script type="text/html" id="query-redacted">
+  <div rel="tooltip" data-placement="bottom" data-bind="tooltip, fadeVisible: is_redacted" title="${ _('The current query has been redacted to hide sensitive information.') }" class="inline-block margin-right-10">
+    <i class="fa fa-low-vision warning"></i>
   </div>
   </div>
 </script>
 </script>
 
 
 <script type="text/html" id="notebook-snippet-header">
 <script type="text/html" id="notebook-snippet-header">
   <div class="inactive-action hover-actions inline"><span class="inactive-action" data-bind="css: { 'empty-title': name() === '' }, editable: name, editableOptions: { emptytext: '${_ko('My Snippet')}', mode: 'inline', enabled: true, placement: 'right' }" style="border:none;color: #DDD"></span></div>
   <div class="inactive-action hover-actions inline"><span class="inactive-action" data-bind="css: { 'empty-title': name() === '' }, editable: name, editableOptions: { emptytext: '${_ko('My Snippet')}', mode: 'inline', enabled: true, placement: 'right' }" style="border:none;color: #DDD"></span></div>
   <div class="hover-actions inline pull-right" style="font-size: 15px;">
   <div class="hover-actions inline pull-right" style="font-size: 15px;">
+    <!-- ko template: { name: 'query-redacted' } --><!-- /ko -->
     <!-- ko template: { name: 'longer-operation' } --><!-- /ko -->
     <!-- ko template: { name: 'longer-operation' } --><!-- /ko -->
     <a class="inactive-action" href="javascript:void(0)" data-bind="visible: status() != 'ready' && status() != 'loading' && errors().length == 0, click: function() { hideFixedHeaders(); $data.showLogs(!$data.showLogs());}, css: {'blue': $data.showLogs}" title="${ _('Show Logs') }"><i class="fa fa-file-text-o"></i></a>
     <a class="inactive-action" href="javascript:void(0)" data-bind="visible: status() != 'ready' && status() != 'loading' && errors().length == 0, click: function() { hideFixedHeaders(); $data.showLogs(!$data.showLogs());}, css: {'blue': $data.showLogs}" title="${ _('Show Logs') }"><i class="fa fa-file-text-o"></i></a>
     <span class="execution-timer" data-bind="visible: type() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
     <span class="execution-timer" data-bind="visible: type() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
@@ -1423,6 +1430,7 @@ from notebook.conf import ENABLE_QUERY_BUILDER, ENABLE_QUERY_SCHEDULING, ENABLE_
 
 
 <script type="text/html" id="editor-snippet-header">
 <script type="text/html" id="editor-snippet-header">
   <div class="hover-actions inline pull-right" style="font-size: 15px; position: relative;" data-bind="style: { 'marginRight': $root.editorMode() && !$root.isFullscreenMode() && $root.isPlayerMode() ? '40px' : '0' }">
   <div class="hover-actions inline pull-right" style="font-size: 15px; position: relative;" data-bind="style: { 'marginRight': $root.editorMode() && !$root.isFullscreenMode() && $root.isPlayerMode() ? '40px' : '0' }">
+    <!-- ko template: { name: 'query-redacted' } --><!-- /ko -->
     <!-- ko template: { name: 'longer-operation' } --><!-- /ko -->
     <!-- ko template: { name: 'longer-operation' } --><!-- /ko -->
     <span class="execution-timer" data-bind="visible: type() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
     <span class="execution-timer" data-bind="visible: type() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
     <!-- ko if: availableDatabases().length > 0 && isSqlDialect() -->
     <!-- ko if: availableDatabases().length > 0 && isSqlDialect() -->
@@ -1520,66 +1528,47 @@ from notebook.conf import ENABLE_QUERY_BUILDER, ENABLE_QUERY_SCHEDULING, ENABLE_
 </script>
 </script>
 
 
 <script type="text/html" id="code-editor-snippet-body">
 <script type="text/html" id="code-editor-snippet-body">
-  <div class="alert alert-gradient" data-bind="visible: is_redacted">
-    <div style="float:left;">
-      <svg class="hi" style="height: 20px; width: 20px;">
-        <use xlink:href="#hi-warning"></use>
-      </svg>
-    </div>
-    <div style="margin-left: 30px; line-height:20px;vertical-align: middle;">${ _('The current query has been redacted to hide sensitive information.') }</div>
-  </div>
-
-  <div class="alert alert-error alert-error-gradient" data-bind="visible: hasComplexity">
-    <div style="float:left;">
-      <svg class="hi" style="height: 20px; width: 20px;">
-        <use xlink:href="#hi-warning"></use>
-      </svg>
-    </div>
-    <!-- ko if: hasComplexity -->
-    <div style="margin-left: 30px; line-height: 20px; vertical-align: middle;">
-      <!-- ko if: complexity().risk().length == 0 -->
-        <span style="margin-right:10px; font-weight: bold;">
-          ${ _('No risks were detected in this query.') }
-        </span>
+  <!-- ko if: HAS_OPTIMIZER -->
+  <div class="alert alert-empty">
+    &nbsp;
+    <!-- ko if: hasComplexity() -->
+      <!-- ko if: complexity() && complexity().risk() -->
+      <div class="alert alert-neutral" data-bind="css: {'alert-success': complexity().risk().length == 0, 'alert-warning': complexity().risk().length > 0 && complexity().risk() !== 'high', 'alert-error': complexity().risk() === 'high' }">
+        <!-- ko if: complexity().risk().length == 0 -->
+        <i class="fa fa-check-circle" data-bind="toggleIconExplanation"></i> <span class="icon-explanation">${ _('No risks were detected in this query.') }</span>
+        <!-- /ko -->
+        <!-- ko if: complexity().risk().length > 0 && complexity().risk() !== 'high' -->
+        <i class="fa fa-arrow-circle-down" data-bind="toggleIconExplanation"></i> <span class="icon-explanation">${ _('This query has a low risk profile.') }</span>
+        <!-- /ko -->
+        <!-- ko if: complexity().risk() === 'high' -->
+        <i class="fa fa-exclamation-circle" data-bind="toggleIconExplanation"></i> <span class="icon-explanation"><strong data-bind="text: complexity().riskAnalysis"></strong> <span data-bind="text: complexity().riskRecommendation"></span></span>
+        <!-- /ko -->
+      </div>
       <!-- /ko -->
       <!-- /ko -->
-      <span style="margin-right:10px; font-weight: bold;" data-bind="text: complexity().risk"></span>
-      <span data-bind="text: complexity().riskAnalysis"></span>
-      <span data-bind="text: complexity().riskRecommendation"></span>
-    </div>
     <!-- /ko -->
     <!-- /ko -->
-  </div>
 
 
-  <div class="alert" data-bind="visible: hasSuggestion">
-    <div style="float:left;">
-      <svg class="hi" style="height: 20px; width: 20px;">
-        <use xlink:href="#hi-warning"></use>
-      </svg>
-    </div>
-    <div style="margin-left: 30px; line-height:20px; vertical-align: middle;">
-      <!-- ko if: hasSuggestion -->
+    <!-- ko if: hasSuggestion() -->
       <!-- ko with: suggestion() -->
       <!-- ko with: suggestion() -->
-        <!-- ko if: queryError.encounteredString().length == 0 && ! parseError -->
-          ${ _('The query is compatible!') } <a href="javascript:void(0)" data-bind="click: function() { $parent.type('impala') }">${ _('Execute with Impala?') }</a>
+        <div class="alert alert-neutral" data-bind="css: {'alert-success': queryError.encounteredString().length == 0 && !parseError(), 'alert-warning': queryError.encounteredString().length > 0 && !parseError(), 'alert-error': parseError() }">
+        <!-- ko if: !parseError() && $parent.compatibilityTarget() != $parent.type() && queryError.encounteredString().length == 0 -->
+          <i class="fa fa-check" data-bind="toggleIconExplanation"></i> <span class="icon-explanation">${ _('The query is compatible with Impala.') } <a href="javascript:void(0)" data-bind="click: function() { $parent.type('impala') }">${ _('Execute it now in Impala!') }</a></span>
+        <!-- /ko -->
+        <!-- ko if: !parseError() && $parent.compatibilityTarget() == $parent.type() -->
+          <i class="fa fa-check" data-bind="toggleIconExplanation"></i> <span class="icon-explanation">${ _('The query has no errors.') }</span>
         <!-- /ko -->
         <!-- /ko -->
         <!-- ko if: parseError -->
         <!-- ko if: parseError -->
-          <span data-bind="text: parseError"></span>
+          <i class="fa fa-exclamation" data-bind="toggleIconExplanation"></i> <span class="icon-explanation"><span data-bind="text: parseError"></span></span>
         <!-- /ko -->
         <!-- /ko -->
         <!-- ko if: queryError.encounteredString -->
         <!-- ko if: queryError.encounteredString -->
-          ${ _('Query is not compatible with Impala.') }
-          <br>
-          <span data-bind="text: queryError.encounteredString"></span>
-          <span data-bind="text: queryError.errorString"></span>
+          <i class="fa fa-exclamation" data-bind="toggleIconExplanation"></i> <span class="icon-explanation">${ _('This query is not compatible with Impala.') }</span>
         <!-- /ko -->
         <!-- /ko -->
-        <!-- ko if: queryError.expectedString -->
-          <br>
-          <!-- ko if: queryError.expectedString -->
-            <span data-bind="text: queryError.expectedString"></span>
-          <!-- /ko -->
-        <!-- /ko -->
-      <!-- /ko -->
+        </div>
       <!-- /ko -->
       <!-- /ko -->
-    </div>
+    <!-- /ko -->
   </div>
   </div>
+  <!-- /ko -->
+
+
   <div class="row-fluid" style="margin-bottom: 5px">
   <div class="row-fluid" style="margin-bottom: 5px">
 
 
     <div class="editor span12" data-bind="css: {'single-snippet-editor ace-container-resizable' : $root.editorMode() }, clickForAceFocus: ace">
     <div class="editor span12" data-bind="css: {'single-snippet-editor ace-container-resizable' : $root.editorMode() }, clickForAceFocus: ace">

+ 14 - 1
tools/ace-editor/lib/ace/theme/hue.css

@@ -62,6 +62,13 @@
   margin-left: -3px;
   margin-left: -3px;
 }
 }
 
 
+.ace-hue .ace_marker-layer .ace_warning-line {
+  position: absolute;
+  background-color: #fcf8e3;
+  width: 100% !important;
+  margin-left: -3px;
+}
+
 .ace-hue .ace_invisible {
 .ace-hue .ace_invisible {
   color: #BBB
   color: #BBB
 }
 }
@@ -89,7 +96,13 @@
 
 
 .ace_gutter-cell.ace_error {
 .ace_gutter-cell.ace_error {
   background-color: #f2dede;
   background-color: #f2dede;
-  border-left:1px solid #eed3d7;
+  border-left:1px solid #d6616b;
+  background-image: none !important;
+}
+
+.ace_gutter-cell.ace_warning {
+  background-color: #fcf8e3;
+  border-left:1px solid #f0c36d;
   background-image: none !important;
   background-image: none !important;
 }
 }
 
 

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است