@marimo-team/islands 0.23.16-dev9 → 0.23.17-dev0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chat-ui-JeekqoMH.js → chat-ui-DsZJj75A.js} +3104 -3107
- package/dist/{code-visibility-DveLKNtj.js → common-CotPLtrU.js} +1068 -1036
- package/dist/{formats-CO8cn5_K.js → formats-Dwq1THhr.js} +1 -1
- package/dist/{html-to-image-Bif4utQZ.js → html-to-image-CZ1kLKkq.js} +7336 -6342
- package/dist/main.js +211 -147
- package/dist/{node-sql-parser-DLl_zNw0.js → node-sql-parser-v1Vi-Fft.js} +16867 -16665
- package/dist/{process-output-TAzy-GUk.js → process-output-bVfbcx5_.js} +30 -26
- package/dist/{reveal-component-DxV4Ppa2.js → reveal-component-IoHipy82.js} +634 -626
- package/dist/style.css +1 -1
- package/dist/toDate-CqhBWjvO.js +713 -0
- package/dist/{vega-component-DphdxaJ_.js → vega-component-C7nO5zcf.js} +2 -2
- package/package.json +3 -4
- package/src/__mocks__/requests.ts +56 -4
- package/src/components/data-table/__tests__/__snapshots__/chart-spec-model.test.ts.snap +174 -21
- package/src/components/data-table/__tests__/chart-spec-model.test.ts +102 -1
- package/src/components/data-table/__tests__/columns.test.tsx +9 -1
- package/src/components/data-table/column-summary/chart-spec-model.tsx +163 -103
- package/src/components/data-table/column-summary/legacy-chart-spec.ts +15 -7
- package/src/components/data-table/column-summary/utils.ts +26 -7
- package/src/components/data-table/columns.tsx +11 -1
- package/src/components/data-table/types.ts +1 -1
- package/src/components/editor/__tests__/viewer-banner.test.tsx +14 -0
- package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +99 -18
- package/src/components/editor/actions/__tests__/pdf-export.test.ts +32 -13
- package/src/components/editor/actions/pair-with-agent-commands.ts +21 -10
- package/src/components/editor/actions/pair-with-agent-modal.tsx +2 -2
- package/src/components/editor/actions/pdf-export.ts +11 -14
- package/src/components/editor/actions/useNotebookActions.tsx +28 -19
- package/src/components/editor/cell/code/__tests__/cell-editor-activity-lifecycle.test.ts +250 -0
- package/src/components/editor/cell/code/cell-editor.tsx +13 -7
- package/src/components/editor/chrome/__tests__/state.test.ts +7 -1
- package/src/components/editor/chrome/panels/__tests__/logs-panel.test.tsx +25 -0
- package/src/components/editor/chrome/panels/__tests__/panel-accordion-state.test.ts +42 -0
- package/src/components/editor/chrome/panels/file-explorer-panel.tsx +6 -17
- package/src/components/editor/chrome/panels/logs-panel.tsx +12 -2
- package/src/components/editor/chrome/panels/panel-accordion-state.ts +51 -0
- package/src/components/editor/chrome/panels/session-panel.tsx +5 -16
- package/src/components/editor/chrome/panels/write-secret-modal.tsx +40 -12
- package/src/components/editor/chrome/state.ts +10 -0
- package/src/components/editor/connections/__tests__/json-credentials.test.ts +114 -0
- package/src/components/editor/connections/__tests__/paths.test.ts +39 -0
- package/src/components/editor/connections/__tests__/quick-add-data-sources.test.tsx +113 -0
- package/src/components/editor/connections/add-connection-dialog.tsx +2 -0
- package/src/components/editor/connections/components.tsx +6 -2
- package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +27 -5
- package/src/components/editor/connections/database/__tests__/as-code.test.ts +33 -0
- package/src/components/editor/connections/database/as-code.ts +37 -13
- package/src/components/editor/connections/database/schemas.ts +6 -5
- package/src/components/editor/connections/form-renderers.tsx +99 -1
- package/src/components/editor/connections/json-credentials.ts +57 -0
- package/src/components/editor/connections/paths.ts +21 -0
- package/src/components/editor/connections/quick-add-data-sources.tsx +106 -0
- package/src/components/editor/connections/secret-combobox.tsx +41 -20
- package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +88 -2
- package/src/components/editor/connections/storage/__tests__/as-code.test.ts +128 -8
- package/src/components/editor/connections/storage/as-code.ts +79 -36
- package/src/components/editor/connections/storage/schemas.ts +85 -44
- package/src/components/editor/file-tree/__tests__/renderers.test.ts +31 -0
- package/src/components/editor/file-tree/file-viewer.tsx +11 -7
- package/src/components/editor/file-tree/renderers.tsx +21 -0
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +2 -2
- package/src/components/editor/viewer-banner.tsx +6 -1
- package/src/components/forms/form.tsx +59 -24
- package/src/components/forms/options.ts +2 -1
- package/src/components/slides/__tests__/cell-output-block.test.tsx +40 -0
- package/src/components/slides/__tests__/slide-cell-view.test.tsx +33 -0
- package/src/components/slides/reveal-component.tsx +22 -17
- package/src/components/slides/slide-cell-view.tsx +1 -1
- package/src/components/slides/slides.css +4 -2
- package/src/components/static-html/share-modal.tsx +1 -1
- package/src/core/ai/context/providers/file.ts +8 -29
- package/src/core/cells/__tests__/cells.test.ts +12 -2
- package/src/core/cells/__tests__/logs.test.ts +5 -5
- package/src/core/cells/cells.ts +17 -4
- package/src/core/cells/document-changes.ts +1 -0
- package/src/core/cells/logs.ts +14 -4
- package/src/core/codemirror/cm.ts +3 -0
- package/src/core/codemirror/code-lens/__tests__/actions.test.ts +68 -0
- package/src/core/codemirror/code-lens/__tests__/analyzer.test.ts +224 -0
- package/src/core/codemirror/code-lens/__tests__/entities.test.ts +175 -0
- package/src/core/codemirror/code-lens/__tests__/extension.test.ts +325 -0
- package/src/core/codemirror/code-lens/__tests__/perf.test.ts +121 -0
- package/src/core/codemirror/code-lens/__tests__/popover.test.ts +93 -0
- package/src/core/codemirror/code-lens/actions.ts +33 -0
- package/src/core/codemirror/code-lens/analyzer.ts +284 -0
- package/src/core/codemirror/code-lens/entities.ts +123 -0
- package/src/core/codemirror/code-lens/extension.ts +298 -0
- package/src/core/codemirror/code-lens/icons.ts +36 -0
- package/src/core/codemirror/code-lens/popover.tsx +240 -0
- package/src/core/codemirror/copilot/__tests__/language-server.test.ts +396 -100
- package/src/core/codemirror/copilot/__tests__/transport.test.ts +128 -540
- package/src/core/codemirror/copilot/language-server.ts +182 -70
- package/src/core/codemirror/copilot/transport.ts +15 -207
- package/src/core/codemirror/copilot/types.ts +9 -2
- package/src/core/codemirror/find-replace/__tests__/navigate.test.ts +42 -30
- package/src/core/codemirror/find-replace/navigate.ts +2 -0
- package/src/core/codemirror/go-to-definition/__tests__/commands.test.ts +24 -3
- package/src/core/codemirror/go-to-definition/commands.ts +93 -90
- package/src/core/codemirror/language/__tests__/sql.test.ts +627 -61
- package/src/core/codemirror/language/languages/python.ts +23 -11
- package/src/core/codemirror/language/languages/sql/completion-store.ts +5 -1
- package/src/core/codemirror/language/languages/sql/renderers.tsx +3 -3
- package/src/core/codemirror/language/languages/sql/sql.ts +251 -16
- package/src/core/codemirror/lsp/__tests__/federated-lsp.test.ts +161 -0
- package/src/core/codemirror/lsp/__tests__/log-messages.test.ts +53 -0
- package/src/core/codemirror/lsp/__tests__/normalize-markdown-math.test.ts +25 -0
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +598 -156
- package/src/core/codemirror/lsp/__tests__/transports.test.ts +112 -0
- package/src/core/codemirror/lsp/federated-lsp.ts +76 -23
- package/src/core/codemirror/lsp/log-messages.ts +37 -0
- package/src/core/codemirror/lsp/normalize-markdown-math.ts +8 -4
- package/src/core/codemirror/lsp/notebook-lsp.ts +220 -56
- package/src/core/codemirror/lsp/transports.ts +8 -1
- package/src/core/codemirror/lsp/types.ts +68 -8
- package/src/core/codemirror/lsp/utils.ts +14 -0
- package/src/core/codemirror/python-node-names.ts +90 -0
- package/src/core/codemirror/reactive-references/analyzer.ts +52 -57
- package/src/core/codemirror/utils.ts +28 -0
- package/src/core/config/feature-flag.tsx +3 -1
- package/src/core/datasets/data-source-discovery.ts +5 -0
- package/src/core/datasets/request-registry.ts +11 -0
- package/src/core/edit-app.tsx +1 -2
- package/src/core/export/__tests__/hooks.test.ts +2 -4
- package/src/core/export/hooks.ts +4 -3
- package/src/core/islands/__tests__/bridge.test.ts +107 -0
- package/src/core/islands/bootstrap.ts +1 -0
- package/src/core/islands/bridge.ts +18 -0
- package/src/core/kernel/messages.ts +2 -0
- package/src/core/lsp/__tests__/transport.test.ts +509 -337
- package/src/core/lsp/transport.ts +304 -132
- package/src/core/mode.ts +10 -0
- package/src/core/network/__tests__/api.test.ts +42 -0
- package/src/core/network/__tests__/connection.test.ts +45 -0
- package/src/core/network/__tests__/requests-lazy.test.ts +47 -1
- package/src/core/network/__tests__/requests-network.test.ts +48 -0
- package/src/core/network/api.ts +27 -0
- package/src/core/network/connection.ts +15 -0
- package/src/core/network/requests-lazy.ts +3 -0
- package/src/core/network/requests-network.ts +31 -8
- package/src/core/network/requests-static.ts +3 -0
- package/src/core/network/requests-toasting.tsx +3 -0
- package/src/core/network/types.ts +27 -6
- package/src/core/rtc/state.ts +9 -0
- package/src/core/static/download-html.ts +5 -14
- package/src/core/wasm/__tests__/bridge.test.ts +99 -5
- package/src/core/wasm/bridge.ts +42 -9
- package/src/core/wasm/worker/types.ts +10 -3
- package/src/core/wasm/worker/worker.ts +7 -8
- package/src/core/websocket/useMarimoKernelConnection.tsx +4 -0
- package/src/css/app/codemirror.css +16 -2
- package/src/hooks/__tests__/useDataSourceDiscovery.test.ts +80 -0
- package/src/hooks/useDataSourceDiscovery.ts +18 -0
- package/src/plugins/impl/DataTablePlugin.tsx +4 -1
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +158 -0
- package/src/plugins/impl/anywidget/__tests__/model.test.ts +55 -0
- package/src/plugins/impl/anywidget/model.ts +20 -5
- package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +33 -21
- package/src/plugins/impl/data-frames/forms/renderers.tsx +11 -8
- package/src/stories/log-viewer.stories.tsx +12 -6
- package/src/utils/__tests__/blob.test.ts +27 -1
- package/src/utils/__tests__/download.test.tsx +44 -4
- package/src/utils/blob.ts +24 -1
- package/src/utils/download.ts +14 -15
- package/dist/toDate-CmR_xR3P.js +0 -692
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "@codemirror/
|
|
7
|
-
import { PostgreSQL } from "@codemirror/lang-sql";
|
|
3
|
+
import { CompletionContext } from "@codemirror/autocomplete";
|
|
4
|
+
import type { CompletionSource } from "@codemirror/autocomplete";
|
|
5
|
+
import { PostgreSQL, sql } from "@codemirror/lang-sql";
|
|
6
|
+
import { forEachDiagnostic, forceLinting } from "@codemirror/lint";
|
|
8
7
|
import { EditorState, type Extension } from "@codemirror/state";
|
|
8
|
+
import { EditorView } from "@codemirror/view";
|
|
9
9
|
import { DuckDBDialect } from "@marimo-team/codemirror-sql/dialects";
|
|
10
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
11
11
|
import type { CellId } from "@/core/cells/ids";
|
|
12
12
|
import type {
|
|
13
13
|
CompletionConfig,
|
|
@@ -20,17 +20,20 @@ import {
|
|
|
20
20
|
setLatestEngineSelected,
|
|
21
21
|
} from "@/core/datasets/data-source-connections";
|
|
22
22
|
import { type ConnectionName, DUCKDB_ENGINE } from "@/core/datasets/engines";
|
|
23
|
+
import { ValidateSQL } from "@/core/datasets/request-registry";
|
|
23
24
|
import { datasetsAtom } from "@/core/datasets/state";
|
|
24
25
|
import type { DatasetsState } from "@/core/datasets/types";
|
|
25
26
|
import type { HotkeyProvider } from "@/core/hotkeys/hotkeys";
|
|
26
27
|
import { store } from "@/core/state/jotai";
|
|
27
28
|
import type { PlaceholderType } from "../../config/types";
|
|
28
29
|
import { TestSQLCompletionStore } from "../languages/sql/completion-store";
|
|
30
|
+
import * as sqlMode from "../languages/sql/sql-mode";
|
|
29
31
|
import {
|
|
32
|
+
exportedForTesting,
|
|
30
33
|
SQLLanguageAdapter,
|
|
31
34
|
type SQLLanguageAdapterMetadata,
|
|
32
35
|
} from "../languages/sql/sql";
|
|
33
|
-
import { languageMetadataField } from "../metadata";
|
|
36
|
+
import { languageMetadataField, updateLanguageMetadata } from "../metadata";
|
|
34
37
|
|
|
35
38
|
const adapter = new SQLLanguageAdapter();
|
|
36
39
|
|
|
@@ -634,6 +637,572 @@ _df = mo.sql(
|
|
|
634
637
|
});
|
|
635
638
|
});
|
|
636
639
|
|
|
640
|
+
describe("SQL analysis features", () => {
|
|
641
|
+
const metadata: SQLLanguageAdapterMetadata = {
|
|
642
|
+
dataframeName: "_df",
|
|
643
|
+
quotePrefix: "f",
|
|
644
|
+
commentLines: [],
|
|
645
|
+
showOutput: true,
|
|
646
|
+
engine: TEST_ENGINE,
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
const createConnection = (
|
|
650
|
+
columns: string[],
|
|
651
|
+
includeDefaultSchema = true,
|
|
652
|
+
): DataSourceConnection => ({
|
|
653
|
+
name: TEST_ENGINE,
|
|
654
|
+
dialect: "postgres",
|
|
655
|
+
display_name: "PostgreSQL",
|
|
656
|
+
source: "postgres",
|
|
657
|
+
default_database: "test_db",
|
|
658
|
+
default_schema: includeDefaultSchema ? "public" : undefined,
|
|
659
|
+
databases: [
|
|
660
|
+
{
|
|
661
|
+
name: "test_db",
|
|
662
|
+
dialect: "postgres",
|
|
663
|
+
schemas: [
|
|
664
|
+
{
|
|
665
|
+
name: "public",
|
|
666
|
+
tables: [
|
|
667
|
+
{
|
|
668
|
+
name: "users",
|
|
669
|
+
source: "postgres",
|
|
670
|
+
source_type: "local",
|
|
671
|
+
type: "table",
|
|
672
|
+
num_columns: columns.length,
|
|
673
|
+
num_rows: 0,
|
|
674
|
+
variable_name: null,
|
|
675
|
+
columns: columns.map((name) => ({
|
|
676
|
+
name,
|
|
677
|
+
external_type: "string",
|
|
678
|
+
type: "string",
|
|
679
|
+
sample_values: [],
|
|
680
|
+
})),
|
|
681
|
+
},
|
|
682
|
+
],
|
|
683
|
+
},
|
|
684
|
+
],
|
|
685
|
+
},
|
|
686
|
+
],
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
const createCompletionContext = (doc: string, pos: number) => {
|
|
690
|
+
const state = EditorState.create({
|
|
691
|
+
doc,
|
|
692
|
+
extensions: [languageMetadataField.init(() => metadata)],
|
|
693
|
+
});
|
|
694
|
+
const view = new EditorView({ state });
|
|
695
|
+
return {
|
|
696
|
+
context: new CompletionContext(view.state, pos, true, view),
|
|
697
|
+
view,
|
|
698
|
+
};
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
const getLabels = async (
|
|
702
|
+
source: CompletionSource,
|
|
703
|
+
context: CompletionContext,
|
|
704
|
+
) => {
|
|
705
|
+
const result = await source(context);
|
|
706
|
+
return result?.options.map((option) => option.label) ?? [];
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
beforeEach(() => {
|
|
710
|
+
store.set(datasetsAtom, { tables: [] } as unknown as DatasetsState);
|
|
711
|
+
store.set(dataSourceConnectionsAtom, {
|
|
712
|
+
connectionsMap: new Map([
|
|
713
|
+
[TEST_ENGINE, createConnection(["id", "name"])],
|
|
714
|
+
]),
|
|
715
|
+
latestEngineSelected: TEST_ENGINE,
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
it("resolves alias completions from the latest connection schema", async () => {
|
|
720
|
+
const analysis = exportedForTesting.createSQLAnalysis();
|
|
721
|
+
const doc = "SELECT u. FROM users u";
|
|
722
|
+
const { context, view } = createCompletionContext(doc, "SELECT u.".length);
|
|
723
|
+
|
|
724
|
+
expect(await getLabels(analysis.aliasCompletionSource, context)).toEqual(
|
|
725
|
+
expect.arrayContaining(["id", "name"]),
|
|
726
|
+
);
|
|
727
|
+
|
|
728
|
+
store.set(dataSourceConnectionsAtom, {
|
|
729
|
+
connectionsMap: new Map([
|
|
730
|
+
[TEST_ENGINE, createConnection(["id", "name", "email"])],
|
|
731
|
+
]),
|
|
732
|
+
latestEngineSelected: TEST_ENGINE,
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
expect(await getLabels(analysis.aliasCompletionSource, context)).toContain(
|
|
736
|
+
"email",
|
|
737
|
+
);
|
|
738
|
+
view.destroy();
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
it("resolves aliases when the only schema is implicit", async () => {
|
|
742
|
+
store.set(dataSourceConnectionsAtom, {
|
|
743
|
+
connectionsMap: new Map([
|
|
744
|
+
[TEST_ENGINE, createConnection(["id", "name"], false)],
|
|
745
|
+
]),
|
|
746
|
+
latestEngineSelected: TEST_ENGINE,
|
|
747
|
+
});
|
|
748
|
+
const analysis = exportedForTesting.createSQLAnalysis();
|
|
749
|
+
const doc = "SELECT u. FROM users u";
|
|
750
|
+
const { context, view } = createCompletionContext(doc, "SELECT u.".length);
|
|
751
|
+
|
|
752
|
+
expect(await getLabels(analysis.aliasCompletionSource, context)).toEqual(
|
|
753
|
+
expect.arrayContaining(["id", "name"]),
|
|
754
|
+
);
|
|
755
|
+
view.destroy();
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
it("preserves top-level namespaces when promoting default-schema tables", () => {
|
|
759
|
+
const connection = createConnection(["id"]);
|
|
760
|
+
const database = connection.databases[0]!;
|
|
761
|
+
const publicSchema = database.schemas[0]!;
|
|
762
|
+
const usersTable = publicSchema.tables[0]!;
|
|
763
|
+
store.set(dataSourceConnectionsAtom, {
|
|
764
|
+
connectionsMap: new Map([
|
|
765
|
+
[
|
|
766
|
+
TEST_ENGINE,
|
|
767
|
+
{
|
|
768
|
+
...connection,
|
|
769
|
+
databases: [
|
|
770
|
+
{
|
|
771
|
+
...database,
|
|
772
|
+
schemas: [
|
|
773
|
+
{
|
|
774
|
+
...publicSchema,
|
|
775
|
+
tables: [
|
|
776
|
+
...publicSchema.tables,
|
|
777
|
+
{ ...usersTable, name: "analytics" },
|
|
778
|
+
{ ...usersTable, name: "test_db" },
|
|
779
|
+
],
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
name: "analytics",
|
|
783
|
+
tables: [{ ...usersTable, name: "events" }],
|
|
784
|
+
},
|
|
785
|
+
],
|
|
786
|
+
},
|
|
787
|
+
],
|
|
788
|
+
},
|
|
789
|
+
],
|
|
790
|
+
]),
|
|
791
|
+
latestEngineSelected: TEST_ENGINE,
|
|
792
|
+
});
|
|
793
|
+
const state = EditorState.create({
|
|
794
|
+
extensions: [languageMetadataField.init(() => metadata)],
|
|
795
|
+
});
|
|
796
|
+
const view = new EditorView({ state });
|
|
797
|
+
|
|
798
|
+
expect(exportedForTesting.getSchema(view)).toMatchObject({
|
|
799
|
+
analytics: {
|
|
800
|
+
self: { label: "analytics", type: "schema" },
|
|
801
|
+
children: {
|
|
802
|
+
events: { self: { label: "events", type: "table" } },
|
|
803
|
+
},
|
|
804
|
+
},
|
|
805
|
+
public: {
|
|
806
|
+
children: {
|
|
807
|
+
analytics: { self: { label: "analytics", type: "table" } },
|
|
808
|
+
test_db: { self: { label: "test_db", type: "table" } },
|
|
809
|
+
},
|
|
810
|
+
},
|
|
811
|
+
test_db: {
|
|
812
|
+
self: { label: "test_db", type: "database" },
|
|
813
|
+
},
|
|
814
|
+
});
|
|
815
|
+
view.destroy();
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
it("resolves a table literally named self", async () => {
|
|
819
|
+
store.set(datasetsAtom, {
|
|
820
|
+
tables: [
|
|
821
|
+
{
|
|
822
|
+
name: "self",
|
|
823
|
+
columns: [{ name: "value", type: "string" }],
|
|
824
|
+
},
|
|
825
|
+
],
|
|
826
|
+
} as DatasetsState);
|
|
827
|
+
const analysis = exportedForTesting.createSQLAnalysis();
|
|
828
|
+
const doc = "SELECT s. FROM self s";
|
|
829
|
+
const { context, view } = createCompletionContext(doc, "SELECT s.".length);
|
|
830
|
+
|
|
831
|
+
expect(await getLabels(analysis.aliasCompletionSource, context)).toContain(
|
|
832
|
+
"value",
|
|
833
|
+
);
|
|
834
|
+
view.destroy();
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it("completes declared CTE columns", async () => {
|
|
838
|
+
const analysis = exportedForTesting.createSQLAnalysis();
|
|
839
|
+
const doc =
|
|
840
|
+
"WITH active_users(user_id, user_email) AS (SELECT id, email FROM users) " +
|
|
841
|
+
"SELECT active_users. FROM active_users";
|
|
842
|
+
const pos = doc.indexOf("active_users.") + "active_users.".length;
|
|
843
|
+
const { context, view } = createCompletionContext(doc, pos);
|
|
844
|
+
|
|
845
|
+
expect(await getLabels(analysis.cteCompletionSource, context)).toEqual(
|
|
846
|
+
expect.arrayContaining(["user_id", "user_email"]),
|
|
847
|
+
);
|
|
848
|
+
view.destroy();
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
it("completes columns from tables in the current statement", async () => {
|
|
852
|
+
const analysis = exportedForTesting.createSQLAnalysis();
|
|
853
|
+
const doc = "SELECT na FROM users";
|
|
854
|
+
const { context, view } = createCompletionContext(doc, "SELECT na".length);
|
|
855
|
+
|
|
856
|
+
expect(await getLabels(analysis.columnCompletionSource, context)).toContain(
|
|
857
|
+
"name",
|
|
858
|
+
);
|
|
859
|
+
view.destroy();
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
it("reports semantic diagnostics as warnings", async () => {
|
|
863
|
+
const adapter = new SQLLanguageAdapter();
|
|
864
|
+
const extensions = adapter.getExtension(
|
|
865
|
+
{} as CellId,
|
|
866
|
+
{} as CompletionConfig,
|
|
867
|
+
{} as HotkeyProvider,
|
|
868
|
+
{} as PlaceholderType,
|
|
869
|
+
{
|
|
870
|
+
diagnostics: { sql_linter: true },
|
|
871
|
+
} as LSPConfig & { diagnostics: DiagnosticsConfig },
|
|
872
|
+
);
|
|
873
|
+
const state = EditorState.create({
|
|
874
|
+
doc: "SELECT missing FROM users",
|
|
875
|
+
extensions: [languageMetadataField.init(() => metadata), ...extensions],
|
|
876
|
+
});
|
|
877
|
+
const view = new EditorView({ state });
|
|
878
|
+
|
|
879
|
+
forceLinting(view);
|
|
880
|
+
await vi.waitFor(() => {
|
|
881
|
+
const diagnostics: Array<{ message: string; severity: string }> = [];
|
|
882
|
+
forEachDiagnostic(view.state, (diagnostic) => {
|
|
883
|
+
diagnostics.push(diagnostic);
|
|
884
|
+
});
|
|
885
|
+
expect(diagnostics).toContainEqual(
|
|
886
|
+
expect.objectContaining({
|
|
887
|
+
message: "Column 'missing' not found in table 'users'",
|
|
888
|
+
severity: "warning",
|
|
889
|
+
}),
|
|
890
|
+
);
|
|
891
|
+
});
|
|
892
|
+
view.destroy();
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
it("does not report unknown tables", async () => {
|
|
896
|
+
const adapter = new SQLLanguageAdapter();
|
|
897
|
+
const extensions = adapter.getExtension(
|
|
898
|
+
{} as CellId,
|
|
899
|
+
{} as CompletionConfig,
|
|
900
|
+
{} as HotkeyProvider,
|
|
901
|
+
{} as PlaceholderType,
|
|
902
|
+
{
|
|
903
|
+
diagnostics: { sql_linter: true },
|
|
904
|
+
} as LSPConfig & { diagnostics: DiagnosticsConfig },
|
|
905
|
+
);
|
|
906
|
+
const state = EditorState.create({
|
|
907
|
+
doc: "SELECT missing FROM users; SELECT * FROM unknown_table",
|
|
908
|
+
extensions: [languageMetadataField.init(() => metadata), ...extensions],
|
|
909
|
+
});
|
|
910
|
+
const view = new EditorView({ state });
|
|
911
|
+
|
|
912
|
+
forceLinting(view);
|
|
913
|
+
await vi.waitFor(() => {
|
|
914
|
+
const diagnostics: string[] = [];
|
|
915
|
+
forEachDiagnostic(view.state, (diagnostic) => {
|
|
916
|
+
diagnostics.push(diagnostic.message);
|
|
917
|
+
});
|
|
918
|
+
expect(diagnostics).toEqual([
|
|
919
|
+
"Column 'missing' not found in table 'users'",
|
|
920
|
+
]);
|
|
921
|
+
});
|
|
922
|
+
view.destroy();
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
it("invalidates analysis caches when the engine changes", async () => {
|
|
926
|
+
const mssqlEngine = "mssql_engine" as ConnectionName;
|
|
927
|
+
const postgresEngine = "postgres_engine" as ConnectionName;
|
|
928
|
+
store.set(dataSourceConnectionsAtom, {
|
|
929
|
+
connectionsMap: new Map([
|
|
930
|
+
[
|
|
931
|
+
mssqlEngine,
|
|
932
|
+
{
|
|
933
|
+
...createConnection([]),
|
|
934
|
+
name: mssqlEngine,
|
|
935
|
+
dialect: "mssql",
|
|
936
|
+
},
|
|
937
|
+
],
|
|
938
|
+
[
|
|
939
|
+
postgresEngine,
|
|
940
|
+
{
|
|
941
|
+
...createConnection([]),
|
|
942
|
+
name: postgresEngine,
|
|
943
|
+
dialect: "postgres",
|
|
944
|
+
},
|
|
945
|
+
],
|
|
946
|
+
]),
|
|
947
|
+
latestEngineSelected: mssqlEngine,
|
|
948
|
+
});
|
|
949
|
+
const analysis = exportedForTesting.createSQLAnalysis();
|
|
950
|
+
const state = EditorState.create({
|
|
951
|
+
doc: "SELECT TOP 1 * FROM users",
|
|
952
|
+
extensions: [
|
|
953
|
+
languageMetadataField.init(() => ({
|
|
954
|
+
...metadata,
|
|
955
|
+
engine: mssqlEngine,
|
|
956
|
+
})),
|
|
957
|
+
],
|
|
958
|
+
});
|
|
959
|
+
const view = new EditorView({ state });
|
|
960
|
+
|
|
961
|
+
const before = await analysis.structureAnalyzer.analyzeDocument(view.state);
|
|
962
|
+
expect(before[0]?.isValid).toBe(true);
|
|
963
|
+
|
|
964
|
+
view.dispatch({
|
|
965
|
+
effects: updateLanguageMetadata.of({ engine: postgresEngine }),
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
const after = await analysis.structureAnalyzer.analyzeDocument(view.state);
|
|
969
|
+
expect(after[0]?.isValid).toBe(false);
|
|
970
|
+
view.destroy();
|
|
971
|
+
});
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
describe("CustomSqlParser", () => {
|
|
975
|
+
beforeEach(() => {
|
|
976
|
+
store.set(dataSourceConnectionsAtom, {
|
|
977
|
+
connectionsMap: new Map(),
|
|
978
|
+
latestEngineSelected: DUCKDB_ENGINE,
|
|
979
|
+
});
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
afterEach(() => {
|
|
983
|
+
vi.useRealTimers();
|
|
984
|
+
vi.restoreAllMocks();
|
|
985
|
+
store.set(dataSourceConnectionsAtom, {
|
|
986
|
+
connectionsMap: new Map(),
|
|
987
|
+
latestEngineSelected: DUCKDB_ENGINE,
|
|
988
|
+
});
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
const createNamedConnectionState = ({
|
|
992
|
+
doc,
|
|
993
|
+
engine,
|
|
994
|
+
dialect,
|
|
995
|
+
}: {
|
|
996
|
+
doc: string;
|
|
997
|
+
engine: ConnectionName;
|
|
998
|
+
dialect: string;
|
|
999
|
+
}) => {
|
|
1000
|
+
store.set(dataSourceConnectionsAtom, {
|
|
1001
|
+
connectionsMap: new Map([
|
|
1002
|
+
[
|
|
1003
|
+
engine,
|
|
1004
|
+
{
|
|
1005
|
+
name: engine,
|
|
1006
|
+
dialect,
|
|
1007
|
+
display_name: engine,
|
|
1008
|
+
source: dialect,
|
|
1009
|
+
databases: [],
|
|
1010
|
+
},
|
|
1011
|
+
],
|
|
1012
|
+
]),
|
|
1013
|
+
latestEngineSelected: engine,
|
|
1014
|
+
});
|
|
1015
|
+
return EditorState.create({
|
|
1016
|
+
doc,
|
|
1017
|
+
extensions: [
|
|
1018
|
+
languageMetadataField.init(() => ({
|
|
1019
|
+
dataframeName: "_df",
|
|
1020
|
+
quotePrefix: "f",
|
|
1021
|
+
commentLines: [],
|
|
1022
|
+
showOutput: true,
|
|
1023
|
+
engine,
|
|
1024
|
+
})),
|
|
1025
|
+
],
|
|
1026
|
+
});
|
|
1027
|
+
};
|
|
1028
|
+
|
|
1029
|
+
it("uses backend DuckDB validation", async () => {
|
|
1030
|
+
vi.useFakeTimers();
|
|
1031
|
+
vi.spyOn(sqlMode, "getSQLMode").mockReturnValue("validate");
|
|
1032
|
+
const error = {
|
|
1033
|
+
message: "Backend syntax error",
|
|
1034
|
+
line: 1,
|
|
1035
|
+
column: 1,
|
|
1036
|
+
severity: "error" as const,
|
|
1037
|
+
};
|
|
1038
|
+
const request = vi.spyOn(ValidateSQL, "request").mockResolvedValue({
|
|
1039
|
+
error: null,
|
|
1040
|
+
parse_result: { success: false, errors: [error] },
|
|
1041
|
+
request_id: "request-id",
|
|
1042
|
+
validate_result: null,
|
|
1043
|
+
});
|
|
1044
|
+
const state = EditorState.create({
|
|
1045
|
+
doc: "SELECT",
|
|
1046
|
+
extensions: [
|
|
1047
|
+
languageMetadataField.init(() => ({
|
|
1048
|
+
dataframeName: "_df",
|
|
1049
|
+
quotePrefix: "f",
|
|
1050
|
+
commentLines: [],
|
|
1051
|
+
showOutput: true,
|
|
1052
|
+
engine: DUCKDB_ENGINE,
|
|
1053
|
+
})),
|
|
1054
|
+
],
|
|
1055
|
+
});
|
|
1056
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1057
|
+
parser.setFocusState(true);
|
|
1058
|
+
|
|
1059
|
+
const result = parser.validateSql("SELECT", { state });
|
|
1060
|
+
await vi.runAllTimersAsync();
|
|
1061
|
+
|
|
1062
|
+
await expect(result).resolves.toEqual([error]);
|
|
1063
|
+
// No connection is registered for the internal engine, so the dialect
|
|
1064
|
+
// falls back to DEFAULT_PARSER_DIALECT ("DuckDB").
|
|
1065
|
+
expect(request).toHaveBeenCalledWith({
|
|
1066
|
+
dialect: "DuckDB",
|
|
1067
|
+
engine: DUCKDB_ENGINE,
|
|
1068
|
+
onlyParse: false,
|
|
1069
|
+
query: "SELECT",
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
it("resolves superseded validation requests instead of leaving them pending", async () => {
|
|
1074
|
+
vi.useFakeTimers();
|
|
1075
|
+
vi.spyOn(sqlMode, "getSQLMode").mockReturnValue("validate");
|
|
1076
|
+
const error = {
|
|
1077
|
+
message: "Backend syntax error",
|
|
1078
|
+
line: 1,
|
|
1079
|
+
column: 1,
|
|
1080
|
+
severity: "error" as const,
|
|
1081
|
+
};
|
|
1082
|
+
vi.spyOn(ValidateSQL, "request").mockResolvedValue({
|
|
1083
|
+
error: null,
|
|
1084
|
+
parse_result: { success: false, errors: [error] },
|
|
1085
|
+
request_id: "request-id",
|
|
1086
|
+
validate_result: null,
|
|
1087
|
+
});
|
|
1088
|
+
const state = EditorState.create({
|
|
1089
|
+
doc: "SELECT",
|
|
1090
|
+
extensions: [
|
|
1091
|
+
languageMetadataField.init(() => ({
|
|
1092
|
+
dataframeName: "_df",
|
|
1093
|
+
quotePrefix: "f",
|
|
1094
|
+
commentLines: [],
|
|
1095
|
+
showOutput: true,
|
|
1096
|
+
engine: DUCKDB_ENGINE,
|
|
1097
|
+
})),
|
|
1098
|
+
],
|
|
1099
|
+
});
|
|
1100
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1101
|
+
parser.setFocusState(true);
|
|
1102
|
+
|
|
1103
|
+
// Simulate a rapid edit: a second validation call arrives before the
|
|
1104
|
+
// first call's internal debounce timer has fired. The first call should
|
|
1105
|
+
// resolve (with no errors) rather than hang forever.
|
|
1106
|
+
const first = parser.validateSql("SELECT 1", { state });
|
|
1107
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
1108
|
+
const second = parser.validateSql("SELECT 2", { state });
|
|
1109
|
+
await vi.runAllTimersAsync();
|
|
1110
|
+
|
|
1111
|
+
await expect(first).resolves.toEqual([]);
|
|
1112
|
+
await expect(second).resolves.toEqual([error]);
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
it("uses backend validation for a named DuckDB connection", async () => {
|
|
1116
|
+
vi.useFakeTimers();
|
|
1117
|
+
vi.spyOn(sqlMode, "getSQLMode").mockReturnValue("validate");
|
|
1118
|
+
const query =
|
|
1119
|
+
'INSERT OR IGNORE INTO "rebalance-dates" (Timestamp) VALUES (CURRENT_TIMESTAMP);';
|
|
1120
|
+
const error = {
|
|
1121
|
+
message: "Named DuckDB backend result",
|
|
1122
|
+
line: 1,
|
|
1123
|
+
column: 1,
|
|
1124
|
+
severity: "error" as const,
|
|
1125
|
+
};
|
|
1126
|
+
const request = vi.spyOn(ValidateSQL, "request").mockResolvedValue({
|
|
1127
|
+
error: null,
|
|
1128
|
+
parse_result: { success: false, errors: [error] },
|
|
1129
|
+
request_id: "request-id",
|
|
1130
|
+
validate_result: null,
|
|
1131
|
+
});
|
|
1132
|
+
const state = createNamedConnectionState({
|
|
1133
|
+
doc: query,
|
|
1134
|
+
engine: "con" as ConnectionName,
|
|
1135
|
+
dialect: "duckdb",
|
|
1136
|
+
});
|
|
1137
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1138
|
+
parser.setFocusState(true);
|
|
1139
|
+
|
|
1140
|
+
const result = parser.validateSql(query, { state });
|
|
1141
|
+
await vi.runAllTimersAsync();
|
|
1142
|
+
|
|
1143
|
+
await expect(result).resolves.toEqual([error]);
|
|
1144
|
+
expect(request).toHaveBeenCalledWith({
|
|
1145
|
+
dialect: "DuckDB",
|
|
1146
|
+
engine: "con",
|
|
1147
|
+
onlyParse: true,
|
|
1148
|
+
query,
|
|
1149
|
+
});
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
it("skips client-side parsing for named DuckDB SQL", async () => {
|
|
1153
|
+
const query = "CHECKPOINT;";
|
|
1154
|
+
const state = createNamedConnectionState({
|
|
1155
|
+
doc: query,
|
|
1156
|
+
engine: "con" as ConnectionName,
|
|
1157
|
+
dialect: "duckdb",
|
|
1158
|
+
});
|
|
1159
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1160
|
+
|
|
1161
|
+
await expect(parser.parse(query, { state })).resolves.toEqual({
|
|
1162
|
+
success: true,
|
|
1163
|
+
errors: [],
|
|
1164
|
+
});
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
it("preserves client-side parsing for non-DuckDB connections", async () => {
|
|
1168
|
+
const query = "SELECT TOP 1 * FROM users";
|
|
1169
|
+
const state = createNamedConnectionState({
|
|
1170
|
+
doc: query,
|
|
1171
|
+
engine: "postgres_con" as ConnectionName,
|
|
1172
|
+
dialect: "postgres",
|
|
1173
|
+
});
|
|
1174
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1175
|
+
|
|
1176
|
+
await expect(parser.parse(query, { state })).resolves.toMatchObject({
|
|
1177
|
+
success: false,
|
|
1178
|
+
});
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
it("preserves client-side parsing for an unregistered connection", async () => {
|
|
1182
|
+
// No entry is registered in the data source connections store for this
|
|
1183
|
+
// engine, so its dialect cannot be resolved (unlike a known non-DuckDB
|
|
1184
|
+
// dialect, which resolves to a non-null, non-DuckDB value).
|
|
1185
|
+
const query = "SELECT TOP 1 * FROM users";
|
|
1186
|
+
const state = EditorState.create({
|
|
1187
|
+
doc: query,
|
|
1188
|
+
extensions: [
|
|
1189
|
+
languageMetadataField.init(() => ({
|
|
1190
|
+
dataframeName: "_df",
|
|
1191
|
+
quotePrefix: "f",
|
|
1192
|
+
commentLines: [],
|
|
1193
|
+
showOutput: true,
|
|
1194
|
+
engine: "unregistered_con" as ConnectionName,
|
|
1195
|
+
})),
|
|
1196
|
+
],
|
|
1197
|
+
});
|
|
1198
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1199
|
+
|
|
1200
|
+
await expect(parser.parse(query, { state })).resolves.toMatchObject({
|
|
1201
|
+
success: false,
|
|
1202
|
+
});
|
|
1203
|
+
});
|
|
1204
|
+
});
|
|
1205
|
+
|
|
637
1206
|
describe("tablesCompletionSource", () => {
|
|
638
1207
|
const mockStore = store;
|
|
639
1208
|
const completionStore = new TestSQLCompletionStore();
|
|
@@ -1523,6 +2092,7 @@ describe("tablesCompletionSource", () => {
|
|
|
1523
2092
|
|
|
1524
2093
|
const completionSource = completionStore.getCompletionSource(TEST_ENGINE);
|
|
1525
2094
|
expect(completionSource?.defaultTable).toBe("users");
|
|
2095
|
+
expect(completionSource?.defaultSchema).toBe("public");
|
|
1526
2096
|
expect(completionSource?.dialect).toBe(PostgreSQL);
|
|
1527
2097
|
});
|
|
1528
2098
|
|
|
@@ -1654,17 +2224,7 @@ describe("tablesCompletionSource", () => {
|
|
|
1654
2224
|
`);
|
|
1655
2225
|
});
|
|
1656
2226
|
|
|
1657
|
-
|
|
1658
|
-
const testDatasets = [
|
|
1659
|
-
{
|
|
1660
|
-
name: "dataset1",
|
|
1661
|
-
columns: [
|
|
1662
|
-
{ name: "col1", type: "number" },
|
|
1663
|
-
{ name: "col2", type: "string" },
|
|
1664
|
-
],
|
|
1665
|
-
},
|
|
1666
|
-
];
|
|
1667
|
-
|
|
2227
|
+
describe("completion sources", () => {
|
|
1668
2228
|
describe("SQL Completions", () => {
|
|
1669
2229
|
const completionStore = new TestSQLCompletionStore();
|
|
1670
2230
|
|
|
@@ -1695,54 +2255,57 @@ describe("tablesCompletionSource", () => {
|
|
|
1695
2255
|
|
|
1696
2256
|
return EditorState.create({
|
|
1697
2257
|
doc,
|
|
1698
|
-
extensions: [
|
|
2258
|
+
extensions: [
|
|
2259
|
+
languageMetadataField.init(() => defaultMetadata),
|
|
2260
|
+
sql({ dialect: PostgreSQL }),
|
|
2261
|
+
],
|
|
1699
2262
|
});
|
|
1700
2263
|
};
|
|
1701
2264
|
|
|
1702
2265
|
const createCompletionContext = (
|
|
1703
2266
|
state: EditorState,
|
|
1704
2267
|
pos: number,
|
|
1705
|
-
matchText?: string,
|
|
1706
|
-
matchFrom?: number,
|
|
1707
2268
|
): CompletionContext => {
|
|
1708
|
-
return
|
|
1709
|
-
pos,
|
|
1710
|
-
explicit: false,
|
|
1711
|
-
matchBefore: matchText
|
|
1712
|
-
? () => ({
|
|
1713
|
-
from: matchFrom || pos - matchText.length,
|
|
1714
|
-
to: pos,
|
|
1715
|
-
text: matchText,
|
|
1716
|
-
})
|
|
1717
|
-
: () => null,
|
|
1718
|
-
state,
|
|
1719
|
-
aborted: false,
|
|
1720
|
-
tokenBefore: () => null,
|
|
1721
|
-
} as unknown as CompletionContext;
|
|
2269
|
+
return new CompletionContext(state, pos, false);
|
|
1722
2270
|
};
|
|
1723
2271
|
|
|
1724
|
-
const
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
2272
|
+
const getCompletionSources = (
|
|
2273
|
+
extensions: readonly Extension[],
|
|
2274
|
+
): readonly CompletionSource[] | undefined => {
|
|
2275
|
+
for (const extension of extensions) {
|
|
2276
|
+
if (Array.isArray(extension)) {
|
|
2277
|
+
const sources = getCompletionSources(extension);
|
|
2278
|
+
if (sources) {
|
|
2279
|
+
return sources;
|
|
2280
|
+
}
|
|
2281
|
+
continue;
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
const config = extension as {
|
|
2285
|
+
value?: { override?: readonly CompletionSource[] };
|
|
2286
|
+
};
|
|
2287
|
+
if (config.value?.override) {
|
|
2288
|
+
return config.value.override;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
return undefined;
|
|
1729
2292
|
};
|
|
1730
2293
|
|
|
1731
2294
|
describe("tablesCompletionSource", () => {
|
|
1732
|
-
it("should return null when no connection exists", () => {
|
|
2295
|
+
it("should return null when no connection exists", async () => {
|
|
1733
2296
|
const state = createEditorState("SELECT * FROM ");
|
|
1734
2297
|
const ctx = createCompletionContext(state, 14);
|
|
1735
2298
|
|
|
1736
2299
|
const adapter = new SQLLanguageAdapter();
|
|
1737
2300
|
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
|
|
1738
|
-
const completion =
|
|
2301
|
+
const completion = getCompletionSources(extensions)?.[0];
|
|
1739
2302
|
|
|
1740
2303
|
expect(completion).toBeDefined();
|
|
1741
|
-
const result = completion!(ctx);
|
|
2304
|
+
const result = await completion!(ctx);
|
|
1742
2305
|
expect(result).toBeNull();
|
|
1743
2306
|
});
|
|
1744
2307
|
|
|
1745
|
-
it("should provide table completions when connection exists", () => {
|
|
2308
|
+
it("should provide table completions when connection exists", async () => {
|
|
1746
2309
|
const mockConnection: DataSourceConnection = {
|
|
1747
2310
|
name: TEST_ENGINE,
|
|
1748
2311
|
dialect: "postgres",
|
|
@@ -1794,19 +2357,19 @@ describe("tablesCompletionSource", () => {
|
|
|
1794
2357
|
const state = createEditorState("SELECT * FROM u", {
|
|
1795
2358
|
engine: TEST_ENGINE,
|
|
1796
2359
|
});
|
|
1797
|
-
const ctx = createCompletionContext(state, 15
|
|
2360
|
+
const ctx = createCompletionContext(state, 15);
|
|
1798
2361
|
|
|
1799
2362
|
const adapter = new SQLLanguageAdapter();
|
|
1800
2363
|
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
|
|
1801
|
-
const completion =
|
|
2364
|
+
const completion = getCompletionSources(extensions)?.[0];
|
|
1802
2365
|
|
|
1803
2366
|
expect(completion).toBeDefined();
|
|
1804
|
-
const result = completion!(ctx);
|
|
2367
|
+
const result = await completion!(ctx);
|
|
1805
2368
|
expect(result).toBeDefined();
|
|
1806
2369
|
expect(result?.options.length).toBeGreaterThan(0);
|
|
1807
2370
|
});
|
|
1808
2371
|
|
|
1809
|
-
it("should include local datasets in completions", () => {
|
|
2372
|
+
it("should include local datasets in completions", async () => {
|
|
1810
2373
|
const mockConnection: DataSourceConnection = {
|
|
1811
2374
|
name: TEST_ENGINE,
|
|
1812
2375
|
dialect: "duckdb",
|
|
@@ -1825,14 +2388,14 @@ describe("tablesCompletionSource", () => {
|
|
|
1825
2388
|
const state = createEditorState("SELECT * FROM d", {
|
|
1826
2389
|
engine: TEST_ENGINE,
|
|
1827
2390
|
});
|
|
1828
|
-
const ctx = createCompletionContext(state, 15
|
|
2391
|
+
const ctx = createCompletionContext(state, 15);
|
|
1829
2392
|
|
|
1830
2393
|
const adapter = new SQLLanguageAdapter();
|
|
1831
2394
|
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
|
|
1832
|
-
const completion =
|
|
2395
|
+
const completion = getCompletionSources(extensions)?.[0];
|
|
1833
2396
|
|
|
1834
2397
|
expect(completion).toBeDefined();
|
|
1835
|
-
const result
|
|
2398
|
+
const result = await completion!(ctx);
|
|
1836
2399
|
expect(result).toBeDefined();
|
|
1837
2400
|
expect(result?.options.some((opt) => opt.label === "dataset1")).toBe(
|
|
1838
2401
|
true,
|
|
@@ -1841,7 +2404,7 @@ describe("tablesCompletionSource", () => {
|
|
|
1841
2404
|
});
|
|
1842
2405
|
|
|
1843
2406
|
describe("customKeywordCompletionSource", () => {
|
|
1844
|
-
it("should provide SQL keyword completions", () => {
|
|
2407
|
+
it("should provide SQL keyword completions", async () => {
|
|
1845
2408
|
const mockConnection: DataSourceConnection = {
|
|
1846
2409
|
name: TEST_ENGINE,
|
|
1847
2410
|
dialect: "postgres",
|
|
@@ -1858,21 +2421,21 @@ describe("tablesCompletionSource", () => {
|
|
|
1858
2421
|
const state = createEditorState("SEL", {
|
|
1859
2422
|
engine: TEST_ENGINE,
|
|
1860
2423
|
});
|
|
1861
|
-
const ctx = createCompletionContext(state, 3
|
|
2424
|
+
const ctx = createCompletionContext(state, 3);
|
|
1862
2425
|
|
|
1863
2426
|
const adapter = new SQLLanguageAdapter();
|
|
1864
2427
|
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
|
|
1865
|
-
const completion =
|
|
2428
|
+
const completion = getCompletionSources(extensions)?.[2];
|
|
1866
2429
|
|
|
1867
2430
|
expect(completion).toBeDefined();
|
|
1868
|
-
const result
|
|
2431
|
+
const result = await completion!(ctx);
|
|
1869
2432
|
expect(result).toBeDefined();
|
|
1870
2433
|
expect(result?.options.some((opt) => opt.label === "SELECT")).toBe(
|
|
1871
2434
|
true,
|
|
1872
2435
|
);
|
|
1873
2436
|
});
|
|
1874
2437
|
|
|
1875
|
-
it("should not provide keyword completions after dot", () => {
|
|
2438
|
+
it("should not provide keyword completions after dot", async () => {
|
|
1876
2439
|
const mockConnection: DataSourceConnection = {
|
|
1877
2440
|
name: TEST_ENGINE,
|
|
1878
2441
|
dialect: "postgres",
|
|
@@ -1889,14 +2452,14 @@ describe("tablesCompletionSource", () => {
|
|
|
1889
2452
|
const state = createEditorState("SELECT users.n", {
|
|
1890
2453
|
engine: TEST_ENGINE,
|
|
1891
2454
|
});
|
|
1892
|
-
const ctx = createCompletionContext(state, 14
|
|
2455
|
+
const ctx = createCompletionContext(state, 14);
|
|
1893
2456
|
|
|
1894
2457
|
const adapter = new SQLLanguageAdapter();
|
|
1895
2458
|
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
|
|
1896
|
-
const completion =
|
|
2459
|
+
const completion = getCompletionSources(extensions)?.[2];
|
|
1897
2460
|
|
|
1898
2461
|
expect(completion).toBeDefined();
|
|
1899
|
-
const result = completion!(ctx);
|
|
2462
|
+
const result = await completion!(ctx);
|
|
1900
2463
|
expect(result).toBeNull();
|
|
1901
2464
|
});
|
|
1902
2465
|
|
|
@@ -1925,13 +2488,16 @@ describe("tablesCompletionSource", () => {
|
|
|
1925
2488
|
it("should be included in extension overrides", () => {
|
|
1926
2489
|
const adapter = new SQLLanguageAdapter();
|
|
1927
2490
|
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
|
|
1928
|
-
const completion =
|
|
2491
|
+
const completion = getCompletionSources(extensions);
|
|
1929
2492
|
|
|
1930
2493
|
expect(completion).toBeDefined();
|
|
1931
|
-
expect(completion).toHaveLength(
|
|
2494
|
+
expect(completion).toHaveLength(6);
|
|
1932
2495
|
});
|
|
1933
2496
|
});
|
|
1934
2497
|
});
|
|
2498
|
+
});
|
|
2499
|
+
|
|
2500
|
+
it("should return local tables", () => {
|
|
1935
2501
|
mockStore.set(datasetsAtom, { tables: testDatasets } as DatasetsState);
|
|
1936
2502
|
|
|
1937
2503
|
const mockConnection: DataSourceConnection = {
|