diff --git a/.cursorrules b/.cursorrules
new file mode 100644
index 0000000..a167dc3
--- /dev/null
+++ b/.cursorrules
@@ -0,0 +1,20 @@
+# Principles
+
+- Follow the project's existing conventions and patterns.
+- Prefer strong typing throughout the codebase.
+- Avoid using `any` or `unknown` types unless absolutely necessary.
+- Do not use type assertions; instead, define or refine types as needed.
+- Create and use explicit types or interfaces for complex structures.
+- Prefer using node-sql-parser's API and types for SQL parsing and analysis.
+
+# Code Style
+
+- Prefer concise code changes and minimal extra explanatory text.
+- Keep code changes minimal and focused on the requested functionality.
+
+# Development Workflow
+
+- Always use `pnpm test` or its variations to run tests.
+- Always use `pnpm lint` to run linting.
+- Always use `pnpm typecheck` to run typechecking.
+- Ensure all code changes pass tests, linting, and typechecking before merging.
diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml
index ac6f902..4c718f0 100644
--- a/.github/workflows/demo.yml
+++ b/.github/workflows/demo.yml
@@ -16,17 +16,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v4
with:
- version: 9
+ version: 10
- name: ⎔ Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: 22
+ node-version: 24
cache: pnpm
- name: 📥 Install dependencies
@@ -39,7 +39,7 @@ jobs:
uses: actions/configure-pages@v5
- name: 📤 Upload artifact
- uses: actions/upload-pages-artifact@v3
+ uses: actions/upload-pages-artifact@v4
with:
path: './demo/dist'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 52fd9ef..7e610e5 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,19 +12,19 @@ jobs:
release:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v4
with:
- version: 9
+ version: 10
- name: ⎔ Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: 22
+ node-version: 24
registry-url: 'https://registry.npmjs.org'
cache: pnpm
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 47ad0ea..6a1115e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -21,22 +21,25 @@ jobs:
pull-requests: write
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v4
with:
- version: 9
+ version: 10
- name: ⎔ Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
- node-version: 22
+ node-version: 24
cache: pnpm
- name: 📥 Install dependencies
run: pnpm install --ignore-scripts --frozen-lockfile
+ - name: 🎭 Install Playwright browsers
+ run: pnpx playwright install chromium
+
- name: 🔍 Type Check
run: pnpm run typecheck
@@ -46,21 +49,21 @@ jobs:
security:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: 🔍 Run CodeQL
- uses: github/codeql-action/init@v3
+ uses: github/codeql-action/init@v4
with:
languages: javascript
- name: 🔍 Perform Analysis
- uses: github/codeql-action/analyze@v3
+ uses: github/codeql-action/analyze@v4
spelling:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: 🔍 Spell Check Repo
- uses: crate-ci/typos@v1.35.2
+ uses: crate-ci/typos@v1.39.0
diff --git a/README.md b/README.md
index 3a38902..3c85616 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# codemirror-sql
-A CodeMirror extension for SQL linting and visual gutter indicators.
+A CodeMirror extension for SQL linting and visual gutter indicators. Built by and used in [marimo](https://github.com/marimo-team/marimo).
## Features
@@ -8,6 +8,9 @@ A CodeMirror extension for SQL linting and visual gutter indicators.
- 🎨 **Visual gutter** - Color-coded statement indicators and error highlighting
- 💡 **Hover tooltips** - Schema info, keywords, and column details on hover
- 🔮 **CTE autocomplete** - Auto-complete support for CTEs
+- 🎯 **Query-aware resolution** - Context-sensitive schema and column suggestions
+- 🔍 **Additional dialects** - DuckDB, BigQuery
+- 🛠️ **Custom renderers** - Customizable tooltip rendering for tables, columns, and keywords
## Installation
@@ -22,13 +25,13 @@ pnpm add @marimo-team/codemirror-sql
### Basic Setup
```ts
-import { sql, StandardSQL } from '@codemirror/lang-sql';
-import { basicSetup, EditorView } from 'codemirror';
-import { sqlExtension, cteCompletionSource } from '@marimo-team/codemirror-sql';
+import { sql, StandardSQL } from "@codemirror/lang-sql";
+import { basicSetup, EditorView } from "codemirror";
+import { sqlExtension, cteCompletionSource } from "@marimo-team/codemirror-sql";
const schema = {
users: ["id", "name", "email", "active"],
- posts: ["id", "title", "content", "user_id"]
+ posts: ["id", "title", "content", "user_id"],
};
const editor = new EditorView({
@@ -38,19 +41,19 @@ const editor = new EditorView({
sql({
dialect: StandardSQL,
schema: schema,
- upperCaseKeywords: true
+ upperCaseKeywords: true,
}),
StandardSQL.language.data.of({
autocomplete: cteCompletionSource,
}),
sqlExtension({
linterConfig: {
- delay: 250 // Validation delay in ms
+ delay: 250, // Validation delay in ms
},
gutterConfig: {
backgroundColor: "#3b82f6", // Current statement color
errorBackgroundColor: "#ef4444", // Error highlight color
- hideWhenNotFocused: true
+ hideWhenNotFocused: true,
},
enableHover: true,
hoverConfig: {
@@ -58,14 +61,26 @@ const editor = new EditorView({
hoverTime: 300,
enableKeywords: true,
enableTables: true,
- enableColumns: true
- }
- })
+ enableColumns: true,
+ },
+ }),
],
- parent: document.querySelector('#editor')
+ parent: document.querySelector("#editor"),
});
```
+## Additional Dialects
+
+This extension adds support for additional dialects:
+
+- **DuckDB**
+- **BigQuery**
+
+## Keyword Completion
+
+The extension includes keyword documentation for common **SQL keywords** including used in hover and completion,
+which can be found in the `src/data` directory.
+
## Demo
See the [demo](https://marimo-team.github.io/codemirror-sql/) for a full example.
diff --git a/demo/custom-renderers.ts b/demo/custom-renderers.ts
index 1949c20..207d9a6 100644
--- a/demo/custom-renderers.ts
+++ b/demo/custom-renderers.ts
@@ -30,7 +30,7 @@ interface TableMetadata {
indexes: IndexMetadata[];
}
-export type Schema = "users" | "posts" | "orders" | "customers" | "categories";
+export type Schema = "users" | "posts" | "orders" | "customers" | "categories" | "Users_Posts";
export const tableTooltipRenderer = (data: NamespaceTooltipData) => {
// Show table name, columns, description, primary key, foreign key, index, unique, check, default, comment
@@ -292,6 +292,24 @@ function getTableMetadata(tableName: string): TableMetadata {
{ name: "idx_categories_parent", columns: ["parent_id"], unique: false },
],
},
+ Users_Posts: {
+ description: "User-Post relationships",
+ rowCount: "1,234",
+ columns: {
+ user_id: { type: "INT", notNull: true, foreignKey: true, comment: "User who posted" },
+ post_id: {
+ type: "INT",
+ notNull: true,
+ foreignKey: true,
+ comment: "Post being commented on",
+ },
+ },
+ foreignKeys: [
+ { column: "user_id", referencedTable: "users", referencedColumn: "id" },
+ { column: "post_id", referencedTable: "posts", referencedColumn: "id" },
+ ],
+ indexes: [{ name: "idx_users_posts_user", columns: ["user_id"], unique: false }],
+ },
};
return (
diff --git a/demo/data.ts b/demo/data.ts
new file mode 100644
index 0000000..d0d95c7
--- /dev/null
+++ b/demo/data.ts
@@ -0,0 +1,81 @@
+import type { Schema } from "./custom-renderers";
+
+// Default SQL content for the demo
+export const defaultSqlDoc = `-- Welcome to the SQL Editor Demo!
+-- Try editing the queries below to see real-time validation
+
+WITH cte_name AS (
+ SELECT * FROM users
+)
+
+-- Valid queries (no errors):
+SELECT id, name, email
+FROM users
+WHERE active = true
+ORDER BY created_at DESC;
+
+SELECT
+ u.name,
+ p.title,
+ p.created_at
+FROM users u
+JOIN posts p ON u.id = p.user_id
+WHERE u.status = 'active'
+ AND p.published = true
+LIMIT 10;
+
+-- Try editing these to create syntax errors:
+-- Uncomment the lines below to see error highlighting
+
+-- SELECT * FROM; -- Missing table name
+-- SELECT * FORM users; -- Typo in FROM keyword
+-- INSERT INTO VALUES (1, 2); -- Missing table name
+-- UPDATE SET name = 'test'; -- Missing table name
+
+-- Complex example with subquery:
+SELECT
+ customer_id,
+ order_date,
+ total_amount,
+ (SELECT AVG(total_amount) FROM orders) as avg_order_value
+FROM orders
+WHERE order_date >= '2024-01-01'
+ AND total_amount > (
+ SELECT AVG(total_amount) * 0.8
+ FROM orders
+ WHERE YEAR(order_date) = 2024
+ )
+ORDER BY total_amount DESC;
+`;
+
+export const schema: Record = {
+ // Users table
+ users: ["id", "name", "email", "active", "status", "created_at", "updated_at", "profile_id"],
+ // Posts table
+ posts: [
+ "id",
+ "title",
+ "content",
+ "user_id",
+ "published",
+ "created_at",
+ "updated_at",
+ "category_id",
+ ],
+ // Orders table
+ orders: [
+ "id",
+ "customer_id",
+ "order_date",
+ "total_amount",
+ "status",
+ "shipping_address",
+ "created_at",
+ ],
+ // Customers table (additional example)
+ customers: ["id", "first_name", "last_name", "email", "phone", "address", "city", "country"],
+ // Categories table
+ categories: ["id", "name", "description", "parent_id"],
+ // Users_Posts table
+ Users_Posts: ["user_id", "post_id"],
+};
diff --git a/demo/index.html b/demo/index.html
index 0dc206b..b038d16 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -34,7 +34,7 @@ SQL Editor with Diagnostics
-