Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/_static/docs-sql-format-icon.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/_static/docs-sql-linter.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/_static/docs-sql-validate-mode.mp4
Binary file not shown.
4 changes: 2 additions & 2 deletions docs/guides/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ step through the code, and evaluate expressions.

Here's an example of how to use `breakpoint()` in a marimo notebook cell.

![PDB in marimo](/_static/docs-pdb-demo.png)
![PDB in marimo](../_static/docs-pdb-demo.png)

Type `help` in the debugger for a list of commands:

Expand Down Expand Up @@ -92,7 +92,7 @@ When interacting with the AI chat, you can reference the notebook "Errors" with
the `@-symbol` to bring in comprehensive error information from your notebook,
making it easier to get targeted debugging help.

![Notebook Errors context in marimo](/_static/docs-notebook-errors-context.png)
![Notebook Errors context in marimo](../_static/docs-notebook-errors-context.png)

### Best practices for AI-assisted debugging

Expand Down
4 changes: 2 additions & 2 deletions docs/guides/editor_features/ai_completion.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,11 @@ To set up Windsurf:
2. Download the [Windsurf app](https://windsurf.com/download).
3. After installing Windsurf and authenticating, open up the command palette, via <kbd>cmd</kbd>+<kbd>shift</kbd>+<kbd>p</kbd>, and ask it to copy the api key to your clipboard.

![Copy Windsurf API key](/_static/windsurf-api.png)
![Copy Windsurf API key](../../_static/windsurf-api.png)

4a. Configure the UI settings in the editor to use Windsurf.

![Paste Windsurf API key](/_static/windsurf-settings.png)
![Paste Windsurf API key](../../_static/windsurf-settings.png)

4b. Alternatively you can also configure the api key from the marimo config file.

Expand Down
38 changes: 38 additions & 0 deletions docs/guides/working_with_data/sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,44 @@ df = catalog.load_table(("my-namespace", "my-table")).to_polars()
SUMMARIZE df;
```

## Utilities

marimo provides a few utilities when working with SQL

**SQL Linter**

Lint your SQL code and provide better autocompletions and error highlighting.

<div align="center">
<figure>
<img width="600" src="/_static/docs-sql-linter.webp"/>
</figure>
</div>

To disable the linter, you can set the `sql_linter` configuration to `false` in your `pyproject.toml` file or disable it in the marimo editor's settings menu.

**SQL Formatting**

Click on the paint roller icon at the bottom right of the SQL cell to format your SQL code.

<div align="center">
<figure>
<img width="300" src="/_static/docs-sql-format-icon.webp"/>
</figure>
</div>

**SQL Mode**

For In-Memory DuckDB, marimo offers a Validate mode that will validate your SQL as you write it.

<figure>
<video autoplay muted loop playsinline width="600px" align="center">
<source src="/_static/docs-sql-validate-mode.mp4" type="video/mp4">
</video>
</figure>

Under the hood, this runs a debounced query in EXPLAIN mode and returns the parsed errors.

## Interactive tutorial

For an interactive tutorial, run
Expand Down
54 changes: 29 additions & 25 deletions frontend/src/components/app-config/user-config-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,35 @@ export const UserConfigForm: React.FC = () => {
/>
</SettingGroup>

<SettingGroup title="Diagnostics">
<FormField
control={form.control}
name="diagnostics.sql_linter"
render={({ field }) => (
<div className="flex flex-col space-y-1">
<FormItem className={formItemClasses}>
<FormLabel>SQL Linter</FormLabel>
<FormControl>
<Checkbox
data-testid="sql-linter-checkbox"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
<IsOverridden
userConfig={config}
name="diagnostics.sql_linter"
/>
</FormItem>
<FormDescription>
Better linting and autocompletions for SQL cells.
</FormDescription>
</div>
)}
/>
</SettingGroup>

<SettingGroup title="Keymap">
<FormField
control={form.control}
Expand Down Expand Up @@ -1281,31 +1310,6 @@ export const UserConfigForm: React.FC = () => {
</div>
)}
/>
<FormField
control={form.control}
name="experimental.sql_linter"
render={({ field }) => (
<div className="flex flex-col gap-y-1">
<FormItem className={formItemClasses}>
<FormLabel className="font-normal">SQL Linter</FormLabel>
<FormControl>
<Checkbox
data-testid="sql-linter-checkbox"
checked={field.value === true}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
<IsOverridden
userConfig={config}
name="experimental.sql_linter"
/>
<FormDescription>
Enable experimental SQL linting and autocompletion.
</FormDescription>
</div>
)}
/>
<FormField
control={form.control}
name="experimental.external_agents"
Expand Down
30 changes: 24 additions & 6 deletions frontend/src/core/codemirror/language/__tests__/sql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import { PostgreSQL } from "@codemirror/lang-sql";
import { EditorState, type Extension } from "@codemirror/state";
import { DuckDBDialect } from "@marimo-team/codemirror-sql/dialects";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import type { CellId } from "@/core/cells/ids";
import type {
CompletionConfig,
DiagnosticsConfig,
LSPConfig,
} from "@/core/config/config-schema";
import type { DataSourceConnection } from "@/core/datasets/data-source-connections";
import {
dataSourceConnectionsAtom,
Expand All @@ -16,7 +22,9 @@ import {
import { type ConnectionName, DUCKDB_ENGINE } from "@/core/datasets/engines";
import { datasetsAtom } from "@/core/datasets/state";
import type { DatasetsState } from "@/core/datasets/types";
import type { HotkeyProvider } from "@/core/hotkeys/hotkeys";
import { store } from "@/core/state/jotai";
import type { PlaceholderType } from "../../config/types";
import { TestSQLCompletionStore } from "../languages/sql/completion-store";
import {
SQLLanguageAdapter,
Expand All @@ -28,6 +36,16 @@ const adapter = new SQLLanguageAdapter();

const TEST_ENGINE = "test_engine" as ConnectionName;

const TEST_EXTENSION_ARGS = [
{} as CellId,
{} as CompletionConfig,
{} as HotkeyProvider,
{} as PlaceholderType,
{} as LSPConfig & {
diagnostics: DiagnosticsConfig;
},
] as const;

describe("SQLLanguageAdapter", () => {
describe("defaultMetadata", () => {
it("should be set", () => {
Expand Down Expand Up @@ -1716,7 +1734,7 @@ describe("tablesCompletionSource", () => {
const ctx = createCompletionContext(state, 14);

const adapter = new SQLLanguageAdapter();
const extensions = adapter.getExtension();
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
const completion = getCompletion(extensions);

expect(completion).toBeDefined();
Expand Down Expand Up @@ -1779,7 +1797,7 @@ describe("tablesCompletionSource", () => {
const ctx = createCompletionContext(state, 15, "u", 14);

const adapter = new SQLLanguageAdapter();
const extensions = adapter.getExtension();
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
const completion = getCompletion(extensions);

expect(completion).toBeDefined();
Expand Down Expand Up @@ -1810,7 +1828,7 @@ describe("tablesCompletionSource", () => {
const ctx = createCompletionContext(state, 15, "d", 14);

const adapter = new SQLLanguageAdapter();
const extensions = adapter.getExtension();
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
const completion = getCompletion(extensions);

expect(completion).toBeDefined();
Expand Down Expand Up @@ -1843,7 +1861,7 @@ describe("tablesCompletionSource", () => {
const ctx = createCompletionContext(state, 3, "SEL", 0);

const adapter = new SQLLanguageAdapter();
const extensions = adapter.getExtension();
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
const completion = getCompletion(extensions);

expect(completion).toBeDefined();
Expand Down Expand Up @@ -1874,7 +1892,7 @@ describe("tablesCompletionSource", () => {
const ctx = createCompletionContext(state, 14, ".n", 12);

const adapter = new SQLLanguageAdapter();
const extensions = adapter.getExtension();
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
const completion = getCompletion(extensions);

expect(completion).toBeDefined();
Expand Down Expand Up @@ -1906,7 +1924,7 @@ describe("tablesCompletionSource", () => {
describe("variableCompletionSource", () => {
it("should be included in extension overrides", () => {
const adapter = new SQLLanguageAdapter();
const extensions = adapter.getExtension();
const extensions = adapter.getExtension(...TEST_EXTENSION_ARGS);
const completion = getCompletion(extensions);

expect(completion).toBeDefined();
Expand Down
30 changes: 19 additions & 11 deletions frontend/src/core/codemirror/language/languages/sql/sql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ import {
} from "@marimo-team/codemirror-sql";
import { DuckDBDialect } from "@marimo-team/codemirror-sql/dialects";
import dedent from "string-dedent";
import type { CellId } from "@/core/cells/ids";
import { cellIdState } from "@/core/codemirror/cells/state";
import { getFeatureFlag } from "@/core/config/feature-flag";
import type { PlaceholderType } from "@/core/codemirror/config/types";
import type {
CompletionConfig,
DiagnosticsConfig,
LSPConfig,
} from "@/core/config/config-schema";
import {
dataSourceConnectionsAtom,
setLatestEngineSelected,
Expand All @@ -30,6 +36,7 @@ import {
INTERNAL_SQL_ENGINES,
} from "@/core/datasets/engines";
import { ValidateSQL } from "@/core/datasets/request-registry";
import type { HotkeyProvider } from "@/core/hotkeys/hotkeys";
import type { ValidateSQLResult } from "@/core/kernel/messages";
import { store } from "@/core/state/jotai";
import { resolvedThemeAtom } from "@/theme/useTheme";
Expand Down Expand Up @@ -77,17 +84,10 @@ export class SQLLanguageAdapter
implements LanguageAdapter<SQLLanguageAdapterMetadata>
{
readonly type = "sql";
sqlLinterEnabled: boolean;
sqlModeEnabled: boolean;

constructor() {
try {
this.sqlLinterEnabled = getFeatureFlag("sql_linter");
this.sqlModeEnabled = getFeatureFlag("sql_mode");
} catch {
this.sqlLinterEnabled = false;
this.sqlModeEnabled = false;
}
this.sqlModeEnabled = true;
}

get defaultMetadata(): SQLLanguageAdapterMetadata {
Expand Down Expand Up @@ -215,7 +215,13 @@ export class SQLLanguageAdapter
return commentLines;
}

getExtension(): Extension[] {
getExtension(
_cellId: CellId,
_completionConfig: CompletionConfig,
_hotkeys: HotkeyProvider,
_placeholderType: PlaceholderType,
lspConfig: LSPConfig & { diagnostics: DiagnosticsConfig },
): Extension[] {
const extensions = [
// This can be updated with a dispatch effect
sqlConfigCompartment.of(sql({ dialect: DEFAULT_DIALECT })),
Expand Down Expand Up @@ -245,7 +251,9 @@ export class SQLLanguageAdapter
}),
];

if (this.sqlLinterEnabled) {
const sqlLinterEnabled = lspConfig?.diagnostics?.sql_linter ?? false;

if (sqlLinterEnabled) {
const theme = store.get(resolvedThemeAtom);
const parser = new CustomSqlParser({
getParserOptions: (state: EditorState) => {
Expand Down
7 changes: 1 addition & 6 deletions frontend/src/core/codemirror/language/panel/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Tooltip, TooltipProvider } from "@/components/ui/tooltip";
import { normalizeName } from "@/core/cells/names";
import { getFeatureFlag } from "@/core/config/feature-flag";
import { type ConnectionName, DUCKDB_ENGINE } from "@/core/datasets/engines";
import { useAutoGrowInputProps } from "@/hooks/useAutoGrowInputProps";
import { formatSQL } from "../../format";
Expand Down Expand Up @@ -71,8 +70,6 @@ export const LanguagePanelComponent: React.FC<{
updateSQLDialectFromConnection(view, engine);
};

const sqlModeEnabled = getFeatureFlag("sql_mode");

actions = (
<div className="flex flex-1 gap-2 items-center">
<label className="flex gap-2 items-center">
Expand All @@ -98,9 +95,7 @@ export const LanguagePanelComponent: React.FC<{
onChange={switchEngine}
/>
<div className="flex items-center gap-2 ml-auto">
{sqlModeEnabled && metadata.engine === DUCKDB_ENGINE && (
<SQLModeSelect />
)}
{metadata.engine === DUCKDB_ENGINE && <SQLModeSelect />}
<Tooltip content="Format SQL">
<Button
variant="text"
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/core/config/__tests__/config-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ test("default UserConfig - empty", () => {
"activate_on_typing": true,
"copilot": false,
},
"diagnostics": {},
"display": {
"cell_output": "above",
"code_editor_font_size": 14,
Expand Down Expand Up @@ -123,6 +124,7 @@ test("default UserConfig - one level", () => {
"activate_on_typing": true,
"copilot": false,
},
"diagnostics": {},
"display": {
"cell_output": "above",
"code_editor_font_size": 14,
Expand Down Expand Up @@ -172,6 +174,7 @@ test("default UserConfig - one level", () => {
runtime: {},
display: {},
experimental: {},
diagnostics: {},
}),
).toEqual(UserConfigSchema.parse({}));
});
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/core/config/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ export const UserConfigSchema = z
// Pass through so that we don't remove any extra keys that the user has added.
.prefault(() => ({})),
server: z.looseObject({}).prefault(() => ({})),
diagnostics: z
.looseObject({
enabled: z.boolean().optional(),
sql_linter: z.boolean().optional(),
})
.prefault(() => ({})),
sharing: z
.looseObject({
html: z.boolean().optional(),
Expand All @@ -204,6 +210,7 @@ export const UserConfigSchema = z
keymap: {},
runtime: {},
display: {},
diagnostics: {},
experimental: {},
server: {},
ai: {},
Expand Down Expand Up @@ -307,6 +314,7 @@ export function defaultUserConfig(): UserConfig {
keymap: {},
runtime: {},
display: {},
diagnostics: {},
experimental: {},
server: {},
ai: {},
Expand Down
Loading
Loading