You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ju...@apache.org on 2024/04/16 22:09:50 UTC

(superset) 02/05: use custom keywords

This is an automated email from the ASF dual-hosted git repository.

justinpark pushed a commit to branch justin--sqllab-plugin-test
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 6bd6ebfad18d50236da0e53a1dec607515a4b753
Author: justinpark <ju...@gmail.com>
AuthorDate: Tue Apr 16 11:44:46 2024 -0700

    use custom keywords
---
 .../superset-ui-core/src/ui-overrides/types.ts     | 18 +++++++
 .../SqlLab/components/AceEditorWrapper/index.tsx   | 57 +++++++++++++---------
 .../components/AceEditorWrapper/useKeywords.ts     | 37 ++++++++++++--
 3 files changed, 86 insertions(+), 26 deletions(-)

diff --git a/superset-frontend/packages/superset-ui-core/src/ui-overrides/types.ts b/superset-frontend/packages/superset-ui-core/src/ui-overrides/types.ts
index 60598bd4e1..98b464de16 100644
--- a/superset-frontend/packages/superset-ui-core/src/ui-overrides/types.ts
+++ b/superset-frontend/packages/superset-ui-core/src/ui-overrides/types.ts
@@ -144,6 +144,21 @@ export interface DashboardEmbedModalExtensions {
   onHide: () => void;
 }
 
+export interface CustomAutoCompleteArgs {
+  queryEditorId: string;
+  dbId?: string | number;
+  schema?: string;
+}
+
+export interface CustomAutocomplete {
+  docHTML?: string | undefined;
+  docText?: string | undefined;
+  name: string;
+  value: string;
+  score: number;
+  meta: string;
+}
+
 export type Extensions = Partial<{
   'alertsreports.header.icon': React.ComponentType;
   'embedded.documentation.configuration_details': React.ComponentType<ConfigDetailsProps>;
@@ -166,4 +181,7 @@ export type Extensions = Partial<{
   'sqleditor.extension.form': React.ComponentType<SQLFormExtensionProps>;
   'sqleditor.extension.resultTable': React.ComponentType<SQLResultTableExtentionProps>;
   'dashboard.slice.header': React.ComponentType<SliceHeaderExtension>;
+  'sqleditor.extension.customAutocomplete': (args: CustomAutoCompleteArgs) => {
+    data?: CustomAutocomplete[];
+  };
 }>;
diff --git a/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx b/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx
index a01b1cee76..f44a7e3f76 100644
--- a/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx
+++ b/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx
@@ -19,7 +19,8 @@
 import React, { useState, useEffect, useRef } from 'react';
 import type { IAceEditor } from 'react-ace/lib/types';
 import { useDispatch } from 'react-redux';
-import { css, styled, usePrevious } from '@superset-ui/core';
+import { css, styled, usePrevious, useTheme } from '@superset-ui/core';
+import { Global } from '@emotion/react';
 
 import { queryEditorSetSelectedText } from 'src/SqlLab/actions/sqlLab';
 import { FullSQLEditor as AceEditor } from 'src/components/AsyncAceEditor';
@@ -54,16 +55,6 @@ const StyledAceEditor = styled(AceEditor)`
       font-feature-settings:
         'liga' off,
         'calt' off;
-
-      &.ace_autocomplete {
-        // Use !important because Ace Editor applies extra CSS at the last second
-        // when opening the autocomplete.
-        width: ${theme.gridUnit * 130}px !important;
-      }
-
-      .ace_scroller {
-        background-color: ${theme.colors.grayscale.light4};
-      }
     }
   `}
 `;
@@ -182,20 +173,40 @@ const AceEditorWrapper = ({
     },
     !autocomplete,
   );
+  const theme = useTheme();
 
   return (
-    <StyledAceEditor
-      keywords={keywords}
-      onLoad={onEditorLoad}
-      onBlur={onBlurSql}
-      height={height}
-      onChange={onChangeText}
-      width="100%"
-      editorProps={{ $blockScrolling: true }}
-      enableLiveAutocompletion={autocomplete}
-      value={sql}
-      annotations={annotations}
-    />
+    <>
+      <Global
+        styles={css`
+          .ace_text-layer {
+            width: 100% !important;
+          }
+
+          .ace_autocomplete {
+            // Use !important because Ace Editor applies extra CSS at the last second
+            // when opening the autocomplete.
+            width: ${theme.gridUnit * 130}px !important;
+          }
+
+          .ace_scroller {
+            background-color: ${theme.colors.grayscale.light4};
+          }
+        `}
+      />
+      <StyledAceEditor
+        keywords={keywords}
+        onLoad={onEditorLoad}
+        onBlur={onBlurSql}
+        height={height}
+        onChange={onChangeText}
+        width="100%"
+        editorProps={{ $blockScrolling: true }}
+        enableLiveAutocompletion={autocomplete}
+        value={sql}
+        annotations={annotations}
+      />
+    </>
   );
 };
 
diff --git a/superset-frontend/src/SqlLab/components/AceEditorWrapper/useKeywords.ts b/superset-frontend/src/SqlLab/components/AceEditorWrapper/useKeywords.ts
index b42edc5822..680d9d73c6 100644
--- a/superset-frontend/src/SqlLab/components/AceEditorWrapper/useKeywords.ts
+++ b/superset-frontend/src/SqlLab/components/AceEditorWrapper/useKeywords.ts
@@ -18,7 +18,7 @@
  */
 import { useEffect, useMemo, useRef } from 'react';
 import { useSelector, useDispatch, shallowEqual, useStore } from 'react-redux';
-import { t } from '@superset-ui/core';
+import { getExtensionsRegistry, t } from '@superset-ui/core';
 
 import { Editor } from 'src/components/AsyncAceEditor';
 import sqlKeywords from 'src/SqlLab/utils/sqlKeywords';
@@ -55,10 +55,23 @@ const getHelperText = (value: string) =>
     docText: value,
   };
 
+const extensionsRegistry = getExtensionsRegistry();
+
+const useDefaultCustomKeywords = () => ({ data: undefined });
+
 export function useKeywords(
   { queryEditorId, dbId, schema }: Params,
   skip = false,
 ) {
+  const useCustomAutocomplete =
+    extensionsRegistry.get('sqleditor.extension.customAutocomplete') ??
+    useDefaultCustomKeywords;
+
+  const { data: customOptions } = useCustomAutocomplete({
+    queryEditorId: String(queryEditorId),
+    dbId,
+    schema,
+  });
   const dispatch = useDispatch();
   const hasFetchedKeywords = useRef(false);
   // skipFetch is used to prevent re-evaluating memoized keywords
@@ -201,14 +214,32 @@ export function useKeywords(
     [functionNames, insertMatch],
   );
 
+  const customKeywords = useMemo(
+    () =>
+      (customOptions ?? []).map(option => ({
+        ...option,
+        completer: {
+          insertMatch,
+        },
+      })),
+    [customOptions, insertMatch],
+  );
+
   const keywords = useMemo(
     () =>
       columnKeywords
         .concat(schemaKeywords)
         .concat(tableKeywords)
         .concat(functionKeywords)
-        .concat(sqlKeywords),
-    [schemaKeywords, tableKeywords, columnKeywords, functionKeywords],
+        .concat(sqlKeywords)
+        .concat(customKeywords),
+    [
+      schemaKeywords,
+      tableKeywords,
+      columnKeywords,
+      functionKeywords,
+      customKeywords,
+    ],
   );
 
   hasFetchedKeywords.current = !skip;