Преглед изворни кода

HUE-4031 [editor] Show a dropdown with suggestions when right-clicking syntax errors

Johan Ahlen пре 8 година
родитељ
комит
76fae74

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue.css


+ 29 - 15
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -4200,26 +4200,40 @@
         var contextmenuListener = function (e) {
           var selectionRange = editor.selection.getRange();
           huePubSub.publish('sql.context.popover.hide');
+          huePubSub.publish('sql.syntax.dropdown.hide');
           if (selectionRange.isEmpty()) {
             var pointerPosition = editor.renderer.screenToTextCoordinates(e.clientX + 5, e.clientY);
             var token = editor.session.getTokenAt(pointerPosition.row, pointerPosition.column);
-
-            if (token !== null && typeof token.parseLocation !== 'undefined') {
-              var range = markLocation(token.parseLocation);
+            if (token && (token.parseLocation || token.syntaxError)) {
+              var range = token.parseLocation ? markLocation(token.parseLocation) : new AceRange(token.syntaxError.loc.first_line - 1, token.syntaxError.loc.first_column, token.syntaxError.loc.last_line - 1, token.syntaxError.loc.first_column + token.syntaxError.text.length);
               var startCoordinates = editor.renderer.textToScreenCoordinates(range.start.row, range.start.column);
               var endCoordinates = editor.renderer.textToScreenCoordinates(range.end.row, range.end.column);
-              huePubSub.publish('sql.context.popover.show', {
-                data: token.parseLocation,
-                sourceType: snippet.type(),
-                defaultDatabase: snippet.database(),
-                pinEnabled: true,
-                source: {
-                  left: startCoordinates.pageX - 3,
-                  top: startCoordinates.pageY,
-                  right: endCoordinates.pageX - 3,
-                  bottom: endCoordinates.pageY + editor.renderer.lineHeight
-                }
-              });
+              var source = {
+                 // TODO: add element likely in the event
+                left: startCoordinates.pageX - 3,
+                top: startCoordinates.pageY,
+                right: endCoordinates.pageX - 3,
+                bottom: endCoordinates.pageY + editor.renderer.lineHeight
+              };
+
+              if (token.parseLocation) {
+                huePubSub.publish('sql.context.popover.show', {
+                  data: token.parseLocation,
+                  sourceType: snippet.type(),
+                  defaultDatabase: snippet.database(),
+                  pinEnabled: true,
+                  source: source
+                });
+              } else if (token.syntaxError) {
+                huePubSub.publish('sql.syntax.dropdown.show', {
+                  data: token.syntaxError,
+                  editor: editor,
+                  range: range,
+                  sourceType: snippet.type(),
+                  defaultDatabase: snippet.database(),
+                  source: source
+                });
+              }
               e.preventDefault();
               return false;
             }

+ 5 - 0
desktop/core/src/desktop/static/desktop/less/hue.less

@@ -478,6 +478,11 @@ a:visited {
   .hue-box-shadow-bottom;
 }
 
+.hue-syntax-drop-down {
+  position: absolute;
+  z-index: 1038;
+}
+
 .hue-popover {
   .display-flex;
   position: absolute;

+ 4 - 3
desktop/core/src/desktop/templates/ko_components.mako

@@ -24,7 +24,7 @@ from desktop.views import _ko
 
 <%def name="all()">
   <script type="text/html" id="hue-drop-down-template">
-    <!-- ko if: !dropDownVisible() || !searchable -->
+    <!-- ko if: !menuOnly && (!dropDownVisible() || !searchable) -->
     <a class="inactive-action hue-drop-down-active" href="javascript:void(0)" data-bind="toggle: dropDownVisible, css: { 'blue': dropDownVisible }">
       <!-- ko if: icon --><i class="fa" data-bind="css: icon"></i><!-- /ko -->
       <!-- ko if: value -->
@@ -33,7 +33,7 @@ from desktop.views import _ko
       <i class="fa fa-caret-down"></i>
     </a>
     <!-- /ko -->
-    <!-- ko if: dropDownVisible() && searchable -->
+    <!-- ko if: !menuOnly && (dropDownVisible() && searchable) -->
     <input class="hue-drop-down-input" type="text" data-bind="textInput: filter, attr: { 'placeHolder': value }, visible: dropDownVisible, style: { color: filterEdited() ? '#000' : '#AAA', 'min-height': '22px', 'margin-left': '10px' }"/>
     <i class="fa fa-caret-down"></i>
     <!-- /ko -->
@@ -57,7 +57,8 @@ from desktop.views import _ko
     (function () {
       var HueDropDown = function (params, element) {
         var self = this;
-        self.dropDownVisible = ko.observable(false);
+        self.dropDownVisible = ko.observable(!!params.showOnInit);
+        self.menuOnly = !!params.menuOnly;
         self.icon = params.icon;
         self.value = params.value;
         self.entries = params.entries;

+ 107 - 0
desktop/core/src/desktop/templates/sql_syntax_dropdown.mako

@@ -0,0 +1,107 @@
+## 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.
+
+<%!
+from desktop.lib.i18n import smart_unicode
+from django.utils.translation import ugettext as _
+%>
+
+<%def name="sqlSyntaxDropdown()">
+  <script type="text/html" id="sql-syntax-dropdown-template">
+    <div class="hue-syntax-drop-down" data-bind="style: { 'left': left() + 'px', 'top': top() + 'px' }, component: { name: 'hue-drop-down', params: { value: selected, entries: expected, foreachVisible: false, searchable: false, showOnInit: true, menuOnly: true } }">
+    </div>
+  </script>
+
+  <script type="text/javascript">
+    (function () {
+      var hideSyntaxDropdown = function () {
+        var $sqlSyntaxDropdown = $('#sqlSyntaxDropdown');
+        if ($sqlSyntaxDropdown.length > 0) {
+          ko.cleanNode($sqlSyntaxDropdown[0]);
+          $sqlSyntaxDropdown.remove();
+          $(document).off('click', hideOnClickOutside);
+        }
+      };
+
+      var hideOnClickOutside = function (event) {
+        if (jQuery.contains(document, event.target) && !$.contains($('#sqlSyntaxDropdown')[0], event.target) && ($('.modal')[0].length === 0 || !$.contains($('.modal')[0], event.target))) {
+          hideSyntaxDropdown();
+        }
+      };
+
+      function SqlSyntaxDropdownViewModel(params) {
+        var self = this;
+        self.disposalFunctions = [];
+        self.selected = ko.observable();
+        self.expected = ko.observableArray($.map(params.data.expected, function (expected) {
+          return expected.text;
+        }));
+
+        var selectedSub = self.selected.subscribe(function (newValue) {
+          params.editor.session.replace(params.range, newValue);
+          hideSyntaxDropdown();
+        });
+        self.disposalFunctions.push(function () {
+          selectedSub.dispose();
+        });
+
+        self.left = ko.observable(params.source.left);
+        self.top = ko.observable(params.source.bottom);
+
+        var closeOnEsc = function (e) {
+          if (e.keyCode === 27) {
+            hideSyntaxDropdown();
+          }
+        };
+
+        $(document).on('keyup', closeOnEsc);
+
+        self.disposalFunctions.push(function () {
+          $(document).off('keyup', closeOnEsc);
+        });
+
+        window.setTimeout(function() {
+          $(document).on('click', hideOnClickOutside);
+        }, 0);
+
+        self.disposalFunctions.push(function () {
+          $(document).off('click', hideOnClickOutside);
+        })
+      }
+
+      SqlSyntaxDropdownViewModel.prototype.dispose = function() {
+        var self = this;
+        self.disposalFunctions.forEach(function (fn) {
+          fn();
+        })
+      };
+
+      ko.components.register('sql-syntax-dropdown', {
+        viewModel: SqlSyntaxDropdownViewModel,
+        template: { element: 'sql-syntax-dropdown-template' }
+      });
+
+      huePubSub.subscribe('sql.syntax.dropdown.hide', hideSyntaxDropdown);
+
+      huePubSub.subscribe('sql.syntax.dropdown.show', function (details) {
+        hideSyntaxDropdown();
+        var $sqlSyntaxDropdown = $('<div id="sqlSyntaxDropdown" data-bind="component: { name: \'sql-syntax-dropdown\', params: $data }" />');
+        $('body').append($sqlSyntaxDropdown);
+        ko.applyBindings(details, $sqlSyntaxDropdown[0]);
+      });
+    })();
+  </script>
+</%def>

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

@@ -162,6 +162,7 @@ from notebook.conf import ENABLE_QUERY_BUILDER, ENABLE_QUERY_SCHEDULING, ENABLE_
 <script src="${ static('desktop/ext/js/download.min.js') }"></script>
 
 <%namespace name="dashboard" file="/common_dashboard.mako" />
+<%namespace name="sqlSyntaxDropdown" file="/sql_syntax_dropdown.mako" />
 
 <script src="${ static('desktop/js/ko.common-dashboard.js') }" type="text/javascript" charset="utf-8"></script>
 
@@ -190,6 +191,9 @@ from notebook.conf import ENABLE_QUERY_BUILDER, ENABLE_QUERY_SCHEDULING, ENABLE_
 % endif
 </style>
 
+
+${ sqlSyntaxDropdown.sqlSyntaxDropdown() }
+
 <div class="print-logo">
   <img class="pull-right" src="${ static('desktop/art/icon_hue_48.png') }"  alt="${ _('Hue logo') }"/>
 </div>

Неке датотеке нису приказане због велике количине промена