Explorar o código

HUE-7362 [frontend] Improve the autocomplete logic of the inline autocompleter

Johan Ahlen %!s(int64=8) %!d(string=hai) anos
pai
achega
e3d922d

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
desktop/core/src/desktop/static/desktop/js/autocomplete/globalSearchParser.js


+ 50 - 44
desktop/core/src/desktop/static/desktop/js/autocomplete/jison/globalSearchParser.jison

@@ -18,7 +18,7 @@
 %x singleQuote doubleQuote
 %%
 
-\s                                              { /* skip whitespace */ }
+\s+                                             { return 'WS' }
 
 '\u2020'                                        { parser.yy.cursorFound = yylloc; return 'CURSOR'; }
 
@@ -57,21 +57,21 @@
 %%
 
 GlobalSearchAutocomplete
- : SearchParts 'EOF'
+ : OptionalWhitespace SearchParts OptionalWhitespace 'EOF'
    {
-     return $1;
+     return $2;
    }
- | SearchParts_EDIT 'EOF'
+ | OptionalWhitespace SearchParts_EDIT 'EOF'
    {
-     if (!$1.facets) {
-       $1.facets = {};
+     if (!$2.facets) {
+       $2.facets = {};
      }
-     if (!$1.text) {
-       $1.text = [];
+     if (!$2.text) {
+       $2.text = [];
      }
-     return $1;
+     return $2;
    }
- | 'EOF'
+ | OptionalWhitespace 'EOF'
    {
      return { facets: {}, text: [] };
    }
@@ -79,54 +79,44 @@ GlobalSearchAutocomplete
 
 SearchParts
  : SearchPart
+ | SearchParts WS SearchPart
    {
-     $$ = {
-       facets: $1.facets ? $1.facets : {},
-       text: $1.text ? $1.text : []
-     };
-   }
- | SearchParts SearchPart
-   {
-     $$ = {
-       facets: $1.facets ? $1.facets : {},
-       text: $1.text ? $1.text : []
-     };
-     if ($2.facets) {
-       parser.mergeFacets($$.facets, $2.facets);
-     }
-     if ($2.text && $2.text.length) {
-       $$.text = $$.text.concat($2.text);
-     }
+     parser.mergeFacets($1, $3);
+     parser.mergeText($1, $3);
    }
  ;
 
 SearchParts_EDIT
  : SearchPart_EDIT
- | SearchParts SearchPart_EDIT
+ | SearchParts WS SearchPart_EDIT
    {
-     $$ = $2;
-     $$.facets = $1.facets;
+     parser.mergeFacets($1, $3);
+     parser.mergeText($1, $3);
+     $$ = $3;
      $$.text = $1.text;
+     $$.facets = $1.facets;
    }
- | SearchPart_EDIT SearchParts
+ | SearchPart_EDIT WS SearchParts
    {
      $$ = $1;
-     $$.facets = $2.facets;
-     $$.text = $2.text;
+     parser.mergeFacets($$, $3);
+     parser.mergeText($$, $3);
    }
- | SearchParts SearchPart_EDIT SearchParts
+ | SearchParts WS SearchPart_EDIT WS SearchParts
    {
-     $$ = $2;
-     $$.facets = $1.facets;
+     parser.mergeFacets($1, $3);
+     parser.mergeFacets($1, $5);
+     parser.mergeText($1, $3);
+     parser.mergeText($1, $5);
+     $$ = $3;
      $$.text = $1.text;
-     parser.mergeFacets($$.facets, $3.facets);
-     $$.text = $$.text.concat($3.text);
+     $$.facets = $1.facets;
    }
  ;
 
 SearchPart
- : Facet
- | FreeText  --> { text: [ $1 ] }
+ : Facet     --> { text: [], facets: $1.facets }
+ | FreeText  --> { text: [$1], facets: {} }
  ;
 
 SearchPart_EDIT
@@ -135,18 +125,26 @@ SearchPart_EDIT
  ;
 
 Facet
- : 'FACET' FreeText
+ : 'FACET' OptionalWhitespace FreeText
    {
      var facet = {};
      var facetName = $1.substring(0, $1.length - 1).toLowerCase();
      facet[facetName] = {};
-     facet[facetName][$2.toLowerCase()] = true;
+     facet[facetName][$3.toLowerCase()] = true;
      $$ = { facets: facet };
    }
  ;
 
 Facet_EDIT
- : 'FACET' 'CURSOR'     --> { suggestFacetValues: $1.substring(0, $1.length - 1).toLowerCase() }
+ : 'FACET' OptionalWhitespace 'CURSOR'           --> { suggestFacetValues: $1.substring(0, $1.length - 1).toLowerCase() }
+ | 'FACET' OptionalWhitespace FreeText 'CURSOR'
+   {
+     var facet = {};
+     var facetName = $1.substring(0, $1.length - 1).toLowerCase();
+     facet[facetName] = {};
+     facet[facetName][$3.toLowerCase()] = true;
+     $$ = { suggestFacetValues: facetName, facets: facet }
+   }
  ;
 
 FreeText
@@ -155,7 +153,10 @@ FreeText
  ;
 
 FreeText_EDIT
- : 'CURSOR'             --> { suggestFacets: true, suggestResults: true }
+ : 'CURSOR'                --> { suggestFacets: true, suggestResults: true }
+ | 'CURSOR' 'TEXT'         --> { suggestFacets: true, suggestResults: true, text: [$2] }
+ | 'TEXT' 'CURSOR' 'TEXT'  --> { suggestFacets: true, suggestResults: true, text: [$1+$3] }
+ | 'TEXT' 'CURSOR'         --> { suggestFacets: true, suggestResults: true, text: [$1] }
  | QuotedValue_EDIT
  ;
 
@@ -168,6 +169,11 @@ QuotedValue_EDIT
  : 'QUOTE' 'PARTIAL_VALUE'  --> $2
  ;
 
+OptionalWhitespace
+ :
+ | WS
+ ;
+
 %%
 
 SqlParseSupport.initGlobalSearchParser(parser);

+ 23 - 10
desktop/core/src/desktop/static/desktop/js/autocomplete/sqlParseSupport.js

@@ -1904,19 +1904,32 @@ var SqlParseSupport = (function () {
     };
 
     parser.mergeFacets = function (a, b) {
-      if (a && b) {
-        Object.keys(b).forEach(function (key) {
-          if (a[key]) {
-            Object.keys(b[key]).forEach(function (val) {
-              a[key][val.toLowerCase()] = true;
-            });
-          } else {
-            a[key] = b[key];
-          }
-        });
+      if (!a.facets) {
+        a.facets = {};
       }
+      if (!b.facets) {
+        return;
+      }
+      Object.keys(b.facets).forEach(function (key) {
+        if (a.facets[key]) {
+          Object.keys(b.facets[key]).forEach(function (val) {
+            a.facets[key][val.toLowerCase()] = true;
+          });
+        } else {
+          a.facets[key] = b.facets[key];
+        }
+      });
     };
 
+    parser.mergeText = function (a, b) {
+      if (!a.text) {
+        a.text = [];
+      }
+      if (!b.text) {
+        return;
+      }
+      a.text = a.text.concat(b.text);
+    };
     parser.handleQuotedValueWithCursor = function (lexer, yytext, yylloc, quoteChar) {
       if (yytext.indexOf('\u2020') !== -1 || yytext.indexOf('\u2021') !== -1) {
         var cursorIndex = yytext.indexOf('\u2020');

+ 11 - 0
desktop/core/src/desktop/static/desktop/spec/autocomplete/globalSearchParserSpec.js

@@ -51,6 +51,17 @@
       });
     });
 
+    it('should suggest facet values for "type:table tags:a"', function() {
+      testParser('type:table tags:a', '', {
+        suggestFacetValues: 'tags',
+        facets: {
+          'type': { 'table': true },
+          'tags': { 'a': true }
+        },
+        text: []
+      });
+    });
+
     it('should give correct facet values for "type:table type:column"', function() {
       testParser('type:table type:column ', '', {
         suggestFacets: true,

+ 69 - 86
desktop/core/src/desktop/templates/ko_components.mako

@@ -668,10 +668,10 @@ from desktop.views import _ko
         self.autocompleteFromEntries = params.autocompleteFromEntries || function () {};
         self.facets = params.facets || [];
         self.knownFacetValues = params.knownFacetValues || {};
+        self.uniqueFacets = !!params.uniqueFacets;
 
         self.searchInput = ko.observable('');
         self.inlineAutocomplete = ko.observable('');
-        self.lastNonPartial = null;
         self.lastResult = {};
 
         self.querySpec({
@@ -689,7 +689,7 @@ from desktop.views import _ko
 
         if (params.triggerObservable) {
           var triggerSub = params.triggerObservable.subscribe(function () {
-            self.triggerAutocomplete(self.searchInput(), true);
+            self.autocomplete(self.searchInput(), self.inlineAutocomplete);
           });
           self.disposals.push(function () {
             triggerSub.remove();
@@ -697,17 +697,40 @@ from desktop.views import _ko
         }
 
         var inputSub = self.searchInput.subscribe(function (newValue) {
-          if (self.inlineAutocomplete().indexOf(newValue) !== 0 || newValue === '') {
-            self.inlineAutocomplete(newValue);
-          }
-          if (newValue !== '') {
-            self.triggerAutocomplete(newValue);
-          } else {
+          if (newValue === '' && self.querySpec() && self.querySpec().query !== '') {
             self.querySpec({
               query: '',
               facets: {},
               text: []
-            })
+            });
+          } else {
+            // TODO: Get cursor position and split to before and after
+            self.lastResult = globalSearchParser.parseGlobalSearch(newValue, '');
+            if (hueDebug && hueDebug.showGlobalSearchParseResults) {
+              console.log(self.lastResult);
+            }
+            var querySpec = { query: newValue };
+            if (self.lastResult.facets) {
+              querySpec.facets = self.lastResult.facets
+            }
+            if (self.lastResult.text) {
+              querySpec.text = self.lastResult.text;
+            }
+            self.querySpec(querySpec);
+          }
+
+          if (newValue === '') {
+            self.inlineAutocomplete('');
+          } else if (self.inlineAutocomplete() === newValue || self.inlineAutocomplete().indexOf(newValue) !== 0) {
+            self.autocomplete(newValue, self.inlineAutocomplete);
+          } else if (self.inlineAutocomplete().indexOf(newValue) === 0) {
+            var newAutocomp = self.inlineAutocomplete();
+            while (newAutocomp.lastIndexOf(' ') >= newValue.length) {
+              newAutocomp = newAutocomp.substring(0, newAutocomp.lastIndexOf(' '));
+            }
+            if (newAutocomp !== self.inlineAutocomplete()) {
+              self.inlineAutocomplete(newAutocomp);
+            }
           }
         });
 
@@ -720,7 +743,7 @@ from desktop.views import _ko
             return;
           }
           if (event.keyCode === 32 && event.ctrlKey) { // Ctrl-Space
-            self.triggerAutocomplete(self.searchInput(), true);
+            self.autocomplete(self.searchInput(), self.inlineAutocomplete);
             return;
           }
           if (event.keyCode === 39 && self.inlineAutocomplete() !== '' && self.inlineAutocomplete() !== self.searchInput()) { // Right arrow
@@ -743,7 +766,7 @@ from desktop.views import _ko
             self.inlineAutocomplete('');
             $(document).off('keydown', onKeyDown);
           } else if (self.searchInput() !== '') {
-            self.triggerAutocomplete(self.searchInput());
+            self.autocomplete(self.searchInput(), self.inlineAutocomplete);
             $(document).on('keydown', onKeyDown);
           } else {
             $(document).on('keydown', onKeyDown);
@@ -762,35 +785,48 @@ from desktop.views import _ko
         }
       };
 
-      InlineAutocomplete.prototype.updateInlineAutocomplete = function (partial) {
+      InlineAutocomplete.prototype.autocomplete = function (text, callback) {
         var self = this;
+        if (!self.lastResult) {
+          callback('');
+          return;
+        }
+
+        var partial, nonPartial;
+        var partialMatch = text.match(/([^:\s]+)$/i);
+        if (partialMatch) {
+          partial = partialMatch[0];
+          nonPartial = text.substring(0, text.length - partial.length);
+        } else {
+          partial = '';
+          nonPartial = text;
+        }
+
         var newAutocomplete = '';
         var partialLower = partial.toLowerCase();
         if (self.lastResult.suggestFacets) {
           var existingFacetIndex = {};
-          if (self.lastResult.facets) {
+          if (self.uniqueFacets && self.lastResult.facets) {
             Object.keys(self.lastResult.facets).forEach(function (facet) {
               existingFacetIndex[facet.toLowerCase()] = true;
             })
           }
 
-          if (partial !== '') {
-            var lowerCase = partial.length !== '' && partialLower[partialLower.length - 1] === partial[partial.length - 1];
-            var suggestion = self.lastNonPartial + partial;
-            self.facets.every(function (facet) {
-              if (existingFacetIndex[facet]) {
-                return true;
-              }
-              if (facet.indexOf(partialLower) === 0) {
-                var remainder = facet.substring(partial.length);
-                suggestion += lowerCase ? remainder : remainder.toUpperCase();
-                suggestion += ':';
-                newAutocomplete = suggestion;
-                return false;
-              }
+          var suggestion = nonPartial + partial;
+          var isLowerCase = suggestion.length > 0 && suggestion.toLowerCase() === suggestion;
+          self.facets.every(function (facet) {
+            if (self.uniqueFacets && existingFacetIndex[facet]) {
               return true;
-            });
-          }
+            }
+            if (partial.length === 0 || facet.indexOf(partialLower) === 0) {
+              var remainder = facet.substring(partial.length);
+              suggestion += isLowerCase ? remainder : remainder.toUpperCase();
+              suggestion += ':';
+              newAutocomplete = suggestion;
+              return false;
+            }
+            return true;
+          });
         }
 
         if (self.lastResult.suggestFacetValues && !newAutocomplete) {
@@ -798,7 +834,7 @@ from desktop.views import _ko
           if (facetValues[self.lastResult.suggestFacetValues.toLowerCase()]) {
             Object.keys(facetValues[self.lastResult.suggestFacetValues.toLowerCase()]).every(function (value) {
               if (value.toLowerCase().indexOf(partialLower) === 0) {
-                newAutocomplete = self.lastNonPartial + partial + value.substring(partial.length, value.length);
+                newAutocomplete = nonPartial + partial + value.substring(partial.length, value.length);
                 return false;
               }
               return true;
@@ -807,69 +843,16 @@ from desktop.views import _ko
         }
 
         if (partial !== '' && self.lastResult.suggestResults && !newAutocomplete) {
-          newAutocomplete = self.autocompleteFromEntries(self.lastNonPartial, partial);
+          newAutocomplete = self.autocompleteFromEntries(nonPartial, partial);
         }
 
         if (!newAutocomplete) {
-          if (self.inlineAutocomplete() !== '') {
-            self.inlineAutocomplete('');
-          }
+          callback('');
         } else if (newAutocomplete !== self.inlineAutocomplete()) {
-          self.inlineAutocomplete(newAutocomplete);
+          callback(newAutocomplete);
         }
       };
 
-      InlineAutocomplete.prototype.triggerAutocomplete = function (newValue, direct) {
-        var self = this;
-        var partial, nonPartial;
-        var partialMatch = newValue.match(/([^:\s]+)$/i);
-        if (partialMatch) {
-          partial = partialMatch[0];
-          nonPartial = newValue.substring(0, newValue.length - partial.length);
-        } else {
-          partial = '';
-          nonPartial = newValue;
-        }
-
-        if (self.lastNonPartial && self.lastNonPartial === nonPartial) {
-          self.updateInlineAutocomplete(partial);
-          self.lastResult = globalSearchParser.parseGlobalSearch(newValue, '');
-          var querySpec = { query: newValue };
-          if (self.lastResult.facets) {
-            querySpec.facets = self.lastResult.facets
-          }
-          if (self.lastResult.text) {
-            querySpec.text = self.lastResult.text;
-          }
-          self.querySpec(querySpec);
-          return;
-        }
-
-        window.clearTimeout(self.autocompleteThrottle);
-        self.autocompleteThrottle = window.setTimeout(function () {
-          self.lastNonPartial = nonPartial;
-
-          // TODO: Get cursor position and split to before and after
-          self.lastResult = globalSearchParser.parseGlobalSearch(newValue, '');
-          if (hueDebug && hueDebug.showGlobalSearchParseResults) {
-            console.log(self.lastResult);
-          }
-          if (self.lastResult) {
-            var querySpec = { query: newValue };
-            if (self.lastResult.facets) {
-              querySpec.facets = self.lastResult.facets
-            }
-            if (self.lastResult.text) {
-              querySpec.text = self.lastResult.text;
-            }
-            self.querySpec(querySpec);
-            self.updateInlineAutocomplete(partial);
-          } else {
-            self.lastNonPartial = null;
-          }
-        }, direct ? 0 : 200);
-      };
-
       ko.components.register('inline-autocomplete', {
         viewModel: InlineAutocomplete,
         template: {element: 'inline-autocomplete-template'}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio