Bläddra i källkod

[editor] Externalize sqlParserRepository for the AceAutocompleter Vue component

Johan Ahlen 5 år sedan
förälder
incheckning
38beadd444

+ 17 - 3
desktop/core/src/desktop/js/apps/notebook2/components/aceEditor/AceEditor.vue

@@ -20,7 +20,7 @@
   <div class="ace-editor-component">
     <div :id="id" ref="editorElement" class="ace-editor" />
     <ace-autocomplete
-      v-if="editor"
+      v-if="editor && autocompleteParser"
       :autocomplete-parser="autocompleteParser"
       :editor="editor"
       :editor-id="id"
@@ -42,7 +42,12 @@
   import { formatSql } from 'apps/notebook2/apiUtils';
   import Executor from 'apps/notebook2/execution/executor';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
-  import { AutocompleteParser, IdentifierChainEntry, ParsedLocation } from 'parse/types';
+  import {
+    AutocompleteParser,
+    IdentifierChainEntry,
+    ParsedLocation,
+    SqlParserProvider
+  } from 'parse/types';
   import { EditorInterpreter } from 'types/config';
   import { hueWindow } from 'types/types';
   import huePubSub from 'utils/huePubSub';
@@ -74,10 +79,11 @@
     @Prop({ required: false, default: () => ({}) })
     aceOptions?: Ace.Options;
     @Prop({ required: false })
-    autocompleteParser?: AutocompleteParser;
+    sqlParserProvider?: SqlParserProvider;
 
     subTracker = new SubscriptionTracker();
     editor: Ace.Editor | null = null;
+    autocompleteParser: AutocompleteParser | null = null;
     aceLocationHandler: AceLocationHandler | null = null;
     lastFocusedEditor = false;
 
@@ -91,6 +97,14 @@
         return;
       }
 
+      if (this.sqlParserProvider) {
+        this.sqlParserProvider
+          .getAutocompleteParser(this.executor.connector().dialect)
+          .then(autocompleteParser => {
+            this.autocompleteParser = autocompleteParser;
+          });
+      }
+
       const height = localStorage.getItem('ace.editor.custom.height') || '128';
       (<HTMLElement>this.$el).style.height = height + 'px';
 

+ 5 - 2
desktop/core/src/desktop/js/apps/notebook2/components/aceEditor/AceEditorKoBridge.vue

@@ -24,6 +24,7 @@
     :executor="executor"
     :initial-cursor-position="cursorPosition"
     :initial-value="value"
+    :sql-parser-provider="sqlParserProvider"
     @ace-created="aceCreated"
     @create-new-doc="createNewDoc"
     @cursor-changed="cursorChanged"
@@ -43,6 +44,7 @@
   import AceEditor from './AceEditor.vue';
   import Executor from 'apps/notebook2/execution/executor';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
+  import sqlParserRepository from 'parse/sql/sqlParserRepository';
 
   @Component({
     components: { AceEditor }
@@ -60,10 +62,11 @@
     aceOptions?: Ace.Options;
 
     cursorPosition?: Ace.Position;
-    value?: string;
     editorId?: string;
-    subTracker = new SubscriptionTracker();
     initialized = false;
+    sqlParserProvider = sqlParserRepository;
+    subTracker = new SubscriptionTracker();
+    value?: string;
 
     updated(): void {
       if (!this.initialized) {

+ 5 - 5
desktop/core/src/desktop/js/apps/notebook2/components/aceEditor/autocomplete/AceAutocomplete.vue

@@ -122,17 +122,17 @@
   })
   export default class AceAutocomplete extends Vue {
     @Prop({ required: true })
-    executor!: Executor;
+    autocompleteParser!: AutocompleteParser;
+    @Prop({ required: true })
+    editor!: Ace.Editor;
     @Prop({ required: true })
     editorId!: string;
     @Prop({ required: true })
-    editor!: Ace.Editor;
+    executor!: Executor;
+
     @Prop({ required: false, default: false })
     temporaryOnly?: boolean;
 
-    @Prop({ required: false })
-    autocompleteParser?: AutocompleteParser;
-
     startLayout: DOMRect | null = null;
     startPixelRatio = window.devicePixelRatio;
     left = 0;

+ 7 - 9
desktop/core/src/desktop/js/apps/notebook2/components/aceEditor/autocomplete/SqlAutocompleter.ts

@@ -28,14 +28,13 @@ import { AutocompleteParser, AutocompleteParseResult } from 'parse/types';
 import { EditorInterpreter } from 'types/config';
 import AutocompleteResults from './AutocompleteResults';
 import huePubSub from 'utils/huePubSub';
-import sqlParserRepository from 'parse/sql/sqlParserRepository';
 
 export default class SqlAutocompleter implements Disposable {
   editor: Ace.Editor;
   executor: Executor;
   fixedPrefix: () => string;
   fixedPostfix: () => string;
-  autocompleteParser?: AutocompleteParser;
+  autocompleteParser: AutocompleteParser;
   autocompleteResults: AutocompleteResults;
   editorId: string;
 
@@ -51,7 +50,7 @@ export default class SqlAutocompleter implements Disposable {
     editor: Ace.Editor;
     fixedPrefix?: () => string;
     fixedPostfix?: () => string;
-    autocompleteParser?: AutocompleteParser;
+    autocompleteParser: AutocompleteParser;
   }) {
     this.editorId = options.editorId;
     this.editor = options.editor;
@@ -114,10 +113,7 @@ export default class SqlAutocompleter implements Disposable {
         }) + this.fixedPostfix();
 
       try {
-        const parser =
-          this.autocompleteParser ||
-          (await sqlParserRepository.getAutocompleter(this.getDialect()));
-        return parser.parseSql(beforeCursor, afterCursor);
+        return this.autocompleteParser.parseSql(beforeCursor, afterCursor);
       } catch (err) {
         console.warn(err);
       }
@@ -125,9 +121,11 @@ export default class SqlAutocompleter implements Disposable {
   }
 
   async parseAll(): Promise<AutocompleteParseResult | undefined> {
-    const parser = await sqlParserRepository.getAutocompleter(this.getDialect());
     try {
-      return parser.parseSql(this.editor.getTextBeforeCursor(), this.editor.getTextAfterCursor());
+      return this.autocompleteParser.parseSql(
+        this.editor.getTextBeforeCursor(),
+        this.editor.getTextAfterCursor()
+      );
     } catch (err) {
       console.warn(err);
     }

+ 1 - 1
desktop/core/src/desktop/js/catalog/optimizer/localStrategy.js

@@ -27,7 +27,7 @@ export default class LocalStrategy extends BaseStrategy {
 
     // TODO: Get parser from repository, need to extract SqlFunctions dep first
     // to reduce size of main hue bundle
-    // const parser = await sqlParserRepository.getAutocompleter(snippet.dialect);
+    // const parser = await sqlParserRepository.getAutocompleteParser(snippet.dialect);
     const sqlParseResult = sqlAutocompleteParser.parseSql(snippet.statement + ' ', '');
 
     const hasLimit = sqlParseResult.locations.some(

+ 18 - 15
desktop/core/src/desktop/js/parse/sql/sqlParserRepository.js → desktop/core/src/desktop/js/parse/sql/sqlParserRepository.ts

@@ -15,6 +15,8 @@
 // limitations under the License.
 
 /* eslint-disable */
+import { AutocompleteParser, SqlParserProvider, SyntaxParser } from 'parse/types';
+
 /**
  * AUTOCOMPLETE_MODULES and SYNTAX_MODULES are generated, do not edit manually, see tools/jison/generateParsers.js
  */
@@ -44,16 +46,19 @@ const SYNTAX_MODULES = {
 };
 /* eslint-enable */
 
-class SqlParserRepository {
-  constructor() {
-    this.modulePromises = {};
-  }
+class SqlParserRepository implements SqlParserProvider {
+  modulePromises: { [dialect: string]: Promise<AutocompleteParser | SyntaxParser> } = {};
 
-  async getParser(dialect, parserType) {
+  async getParser(dialect: string, parserType: string): Promise<AutocompleteParser | SyntaxParser> {
     if (!this.modulePromises[dialect + parserType]) {
-      const modules = parserType === 'Autocomplete' ? AUTOCOMPLETE_MODULES : SYNTAX_MODULES;
+      const modules = <{ [dialect: string]: () => unknown }>(
+        (parserType === 'Autocomplete' ? AUTOCOMPLETE_MODULES : SYNTAX_MODULES)
+      );
+      const targetModule = <() => Promise<{ default: SyntaxParser | AutocompleteParser }>>(
+        (modules[dialect] || modules.generic)
+      );
+
       this.modulePromises[dialect + parserType] = new Promise((resolve, reject) => {
-        const targetModule = modules[dialect] || modules.generic;
         targetModule()
           .then(module => resolve(module.default))
           .catch(reject);
@@ -62,16 +67,14 @@ class SqlParserRepository {
     return this.modulePromises[dialect + parserType];
   }
 
-  /**
-   * @param dialect
-   * @return {Promise<*>}
-   */
-  async getAutocompleter(dialect) {
-    return this.getParser(dialect, 'Autocomplete');
+  async getAutocompleteParser(dialect: string): Promise<AutocompleteParser> {
+    const autocompleteParser = await this.getParser(dialect, 'Autocomplete');
+    return <AutocompleteParser>autocompleteParser;
   }
 
-  async getSyntaxParser(dialect) {
-    return this.getParser(dialect, 'Syntax');
+  async getSyntaxParser(dialect: string): Promise<SyntaxParser> {
+    const syntaxParser = await this.getParser(dialect, 'Syntax');
+    return <SyntaxParser>syntaxParser;
   }
 }
 

+ 9 - 0
desktop/core/src/desktop/js/parse/types.ts

@@ -190,4 +190,13 @@ export interface AutocompleteParser {
   parseSql(beforeCursor: string, afterCursor: string): AutocompleteParseResult;
 }
 
+export interface SyntaxParser {
+  parseSyntax(beforeCursor: string, afterCursor: string): unknown;
+}
+
+export interface SqlParserProvider {
+  getAutocompleteParser(dialect: string): Promise<AutocompleteParser>;
+  getSyntaxParser(dialect: string): Promise<SyntaxParser>;
+}
+
 declare const sqlStatementsParser: SqlStatementsParser;

+ 2 - 2
desktop/core/src/desktop/js/sql/sqlAutocompleter.js

@@ -75,7 +75,7 @@ class SqlAutocompleter {
               }
             }) + this.fixedPostfix();
           sqlParserRepository
-            .getAutocompleter(this.snippet.dialect())
+            .getAutocompleteParser(this.snippet.dialect())
             .then(autocompleteParser => {
               resolve(autocompleteParser.parseSql(beforeCursor, afterCursor));
             })
@@ -95,7 +95,7 @@ class SqlAutocompleter {
   async parseAll() {
     return new Promise((resolve, reject) => {
       sqlParserRepository
-        .getAutocompleter(this.snippet.dialect())
+        .getAutocompleteParser(this.snippet.dialect())
         .then(autocompleteParser => {
           resolve(
             autocompleteParser.parseSql(

+ 1 - 1
desktop/core/src/desktop/js/sql/sqlLocationWebWorker.js

@@ -56,7 +56,7 @@ const onMessage = msg => {
   clearTimeout(throttle);
   throttle = setTimeout(() => {
     if (msg.data.statementDetails) {
-      sqlParserRepository.getAutocompleter(msg.data.connector.dialect).then(parser => {
+      sqlParserRepository.getAutocompleteParser(msg.data.connector.dialect).then(parser => {
         let locations = [];
         const activeStatementLocations = [];
         msg.data.statementDetails.precedingStatements.forEach(statement => {

+ 1 - 1
docs/docs-site/content/developer/parsers/_index.md

@@ -44,7 +44,7 @@ The parsers provide one function, parseSql, that accepts the text before the cur
 
 As an example:
 
-    sqlParserRepository.getAutocompleter('impala').then(parser => {
+    sqlParserRepository.getAutocompleteParser('impala').then(parser => {
       console.log(parser.parseSql('SELECT * FROM customers'));
     });
 

+ 1 - 1
docs/gethue/content/en/posts/2019-07-19-build-your-own-autocompleter.md

@@ -57,7 +57,7 @@ The parsers provide one function, `parseSql`, that accepts the text before the c
 
 As an example:
 
-<pre><code class="javascript">sqlParserRepository.getAutocompleter('impala').then(parser =&gt; {
+<pre><code class="javascript">sqlParserRepository.getAutocompleteParser('impala').then(parser =&gt; {
     console.log(parser.parseSql('SELECT * FROM customers'));
   });
 </code></pre>

+ 2 - 2
tools/jison/generateParsers.js

@@ -47,7 +47,7 @@ const PARSER_FOLDER = '../../desktop/core/src/desktop/js/parse/sql/';
 const OUTPUT_FOLDER = '../../desktop/core/src/desktop/js/parse/';
 const JISON_FOLDER = '../../desktop/core/src/desktop/js/parse/jison/';
 const SQL_PARSER_REPOSITORY_PATH =
-  '../../desktop/core/src/desktop/js/parse/sql/sqlParserRepository.js';
+  '../../desktop/core/src/desktop/js/parse/sql/sqlParserRepository.ts';
 const SYNTAX_PARSER_IMPORT_TEMPLATE =
   '  KEY: () => import(/* webpackChunkName: "KEY-parser" */ \'parse/sql/KEY/KEYSyntaxParser\')';
 const AUTOCOMPLETE_PARSER_IMPORT_TEMPLATE =
@@ -471,7 +471,7 @@ identifySqlParsers().then(() => {
       } else {
         const autocompParsers = [];
         const syntaxParsers = [];
-        console.log('Updating sqlParserRepository.js...');
+        console.log('Updating sqlParserRepository.ts...');
         Object.keys(parserDefinitions).forEach(key => {
           if (parserDefinitions[key].sqlParser === 'AUTOCOMPLETE') {
             autocompParsers.push(