Skip to content

feat: Improve SQL editor completions for DuckDB functions and columns - #665

Merged
ilyabo merged 5 commits into
mainfrom
sql-editor-improvements
May 31, 2026
Merged

ilyabo merged 5 commits into
mainfrom
sql-editor-improvements

Conversation

@ilyabo

@ilyabo ilyabo commented May 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Close button added to the SQL editor header
    • Tab key now accepts code completions
    • Function documentation in completions loads asynchronously
  • Improvements

    • Schema-aware column completions with smarter identifier quoting and context suppression
    • Dialect-aware keyword ordering and refined keyword completion behavior
    • Improved tab deletion/selection logic and updated tab strip/header styling
    • SQL editor modal hides its built-in close button

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 27, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5217a287-8f63-4fa9-9db1-06e86cc96901

📥 Commits

Reviewing files that changed from the base of the PR and between 4ded3ca and aa31599.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • packages/duckdb-core/package.json

📝 Walkthrough

Walkthrough

This PR removes memoization from DuckDB function APIs, refactors CodeMirror completions to use schema-driven column suggestions and delayed function docs, introduces a custom DuckDB dialect with keyword sorting, adjusts keymap and tab styling, refactors query-tab deletion state, and adds a close button to the SQL editor and modal.

Changes

SQL Editor Completions and UX

Layer / File(s) Summary
DuckDB Function API Simplification
packages/duckdb-core/src/get-function-documentation.ts, packages/duckdb-core/src/get-function-suggestions.ts
Remove memoizeOnce wrappers and inline implementations; export getFunctionDocumentation and getFunctionSuggestions as direct async functions.
Hierarchical Schema Namespace Converter
packages/sql-editor/src/codemirror/utils/schema-converter.ts
Rewrite convertToSQLNamespace to build hierarchical SQLNamespace objects with per-table self nodes and column children, escaping keys and nesting by schema/database.
Schema-Driven CodeMirror Completions
packages/sql-editor/src/codemirror/extensions/completion.ts
Replace keyword-driven completions: require language and currentSchemas, add schema-driven column completions with quoting-aware insert handlers, remove getKeywordSuggestions, implement delayedFunctionInfo for async function docs, and compose multiple language.data layers with updated autocompletion.
DuckDB Dialect with Custom Keywords
packages/sql-editor/src/codemirror/extensions/duck-db/duckdb-sql.ts
Add SqlRoomsDuckDBDialect with customized lowercase keywords and STARTER_KEYWORD_SORT_TEXT; createDuckDbSql uses keywordCompletion to boost/sort starter keywords.
Extension Wiring for Dialect and Schemas
packages/sql-editor/src/codemirror/extensions/duck-db/duck-db.ts
Import SqlRoomsDuckDBDialect and pass language: SqlRoomsDuckDBDialect.language and currentSchemas into the completion configuration, removing the prior keyword-suggestion hook.
Query Tab Deletion State Management
packages/sql-editor/src/SqlEditorSlice.tsx
Refactor deleteQueryTab to use a local config reference from the Immer draft for queries/openTabs filtering, queryResultsById cleanup, and selection fallback logic.
Keyboard Shortcuts and Tab Styling
packages/sql-editor/src/codemirror/extensions/sql-keymap.ts, packages/ui/src/components/tab-strip.tsx
Add Tab binding to acceptCompletion, always return a keymap from createSqlKeymap, conditionally register Mod-Enter when onRunQuery exists, and center non-editing tab labels with updated flex layout.
SQL Editor UI: Close Button and Modal
packages/sql-editor/src/components/SqlEditorHeader.tsx, packages/sql-editor/src/SqlEditor.tsx, packages/sql-editor/src/SqlEditorModal.tsx
Add optional onClose prop to SqlEditorHeader and render a close icon button when provided; hide modal close with showCloseButton={false} and forward onClose through SqlEditor.
DuckDB Core Package Dependency
packages/duckdb-core/package.json
Remove @sqlrooms/utils from workspace dependencies in packages/duckdb-core.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • sqlrooms/sqlrooms#418: Prior work updating DuckDB function suggestion/documentation APIs and CodeMirror completion/duck-db wiring.

Suggested reviewers

  • dmitriy-kostianetskiy

Poem

🐰 I hop through code with cheer,
Schema trees and keywords clear,
Memoized routes step aside,
Completions bloom, docs fetch in tide,
A tiny X to close the guide.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: refactoring memoization removal, completion improvements for DuckDB functions/columns, and header close button addition.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sql-editor-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/sql-editor/src/SqlEditorSlice.tsx (1)

263-283: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard against -1 open-tab index when reselecting after delete.

If the deleted selected tab is not currently in openTabs, deletingOpenIndex is -1 and newOpenTabs[newIndex] can be undefined, leaving selectedQueryId on a removed tab.

Suggested fix
-                  const newIndex =
-                    deletingOpenIndex === 0
-                      ? 0
-                      : Math.min(deletingOpenIndex - 1, newOpenTabs.length - 1);
+                  const baseIndex =
+                    deletingOpenIndex <= 0 ? 0 : deletingOpenIndex - 1;
+                  const newIndex = Math.min(baseIndex, newOpenTabs.length - 1);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sql-editor/src/SqlEditorSlice.tsx` around lines 263 - 283, When
reselecting after deleting a tab, guard against deletingOpenIndex being -1 by
clamping it into the valid range for config.openTabs before using it: compute a
safeIndex = deletingOpenIndex < 0 ? Math.min(0, newOpenTabs.length - 1) (or
simply use Math.max(0, Math.min(deletingOpenIndex, newOpenTabs.length - 1))) and
use that to pick newSelectedId from newOpenTabs; if newOpenTabs[newIndex] is
undefined (or newOpenTabs is empty) fall back to picking a valid id from
remainingQueries (e.g., remainingQueries[0]?.id) or set selectedQueryId to null,
and update assignment sites that use newSelectedId/selectedQueryId accordingly
(references: deletingOpenIndex, config.openTabs/newOpenTabs,
remainingQueries/config.queries, newSelectedId).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/sql-editor/src/codemirror/extensions/completion.ts`:
- Around line 84-97: The suppression logic only inspects the current line via
lineAt/completionContext.pos, so multi-line clauses like "FROM\n  ord|" slip
through; change how textBeforeCursor is computed to look back a small window in
the document instead of only the current line: use completionContext.state.doc
and completionContext.pos to compute a start index (e.g. Math.max(0, pos - N)
with N ~100–300) and slice the document from that start to pos to produce
textBeforeCursor, then run the existing regex checks against that window (keep
the same regexes and return null behavior).
- Around line 29-57: The current logic only uses applyQuotedIdentifier when
needsIdentifierQuotes(...) is true, which misses reserved words like "select";
make createColumnCompletion always set apply to the custom apply function
(remove the conditional), and inside applyQuotedIdentifier compute const escaped
= escapeId(column.name) and use that for insertCompletionText; determine whether
to back up over an existing leading quote by checking whether
escaped.startsWith('"') (if true, and the char before `from` is '"' then set
replaceFrom = from - 1, otherwise leave replaceFrom = from), then call
insertCompletionText(view.state, escaped, replaceFrom, to). Reference:
createColumnCompletion, applyQuotedIdentifier, needsIdentifierQuotes, escapeId,
insertCompletionText.

In `@packages/sql-editor/src/codemirror/utils/schema-converter.ts`:
- Around line 42-43: The current logic in schema-converter.ts returns
existing.children as a NamespaceRecord whenever an object has a children
property (the block around the existing variable), which wrongly treats table
nodes (where children is a Completion[]) as namespace nodes; update the guard in
the code paths that return or merge existing.children (the if that checks
existing && !Array.isArray(existing) && 'children' in existing and the similar
block around lines 46–50) to verify children is an object map (e.g., not an
array and typeof children === 'object' && children !== null) before casting to
NamespaceRecord, and if children is an array preserve/merge it as completions
instead of treating it as a NamespaceRecord.

---

Outside diff comments:
In `@packages/sql-editor/src/SqlEditorSlice.tsx`:
- Around line 263-283: When reselecting after deleting a tab, guard against
deletingOpenIndex being -1 by clamping it into the valid range for
config.openTabs before using it: compute a safeIndex = deletingOpenIndex < 0 ?
Math.min(0, newOpenTabs.length - 1) (or simply use Math.max(0,
Math.min(deletingOpenIndex, newOpenTabs.length - 1))) and use that to pick
newSelectedId from newOpenTabs; if newOpenTabs[newIndex] is undefined (or
newOpenTabs is empty) fall back to picking a valid id from remainingQueries
(e.g., remainingQueries[0]?.id) or set selectedQueryId to null, and update
assignment sites that use newSelectedId/selectedQueryId accordingly (references:
deletingOpenIndex, config.openTabs/newOpenTabs, remainingQueries/config.queries,
newSelectedId).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d0f97bc1-eb30-4e5e-87d9-d29701cd1fa2

📥 Commits

Reviewing files that changed from the base of the PR and between 01fab51 and 98f1211.

📒 Files selected for processing (12)
  • packages/duckdb-core/src/get-function-documentation.ts
  • packages/duckdb-core/src/get-function-suggestions.ts
  • packages/sql-editor/src/SqlEditor.tsx
  • packages/sql-editor/src/SqlEditorModal.tsx
  • packages/sql-editor/src/SqlEditorSlice.tsx
  • packages/sql-editor/src/codemirror/extensions/completion.ts
  • packages/sql-editor/src/codemirror/extensions/duck-db/duck-db.ts
  • packages/sql-editor/src/codemirror/extensions/duck-db/duckdb-sql.ts
  • packages/sql-editor/src/codemirror/extensions/sql-keymap.ts
  • packages/sql-editor/src/codemirror/utils/schema-converter.ts
  • packages/sql-editor/src/components/SqlEditorHeader.tsx
  • packages/ui/src/components/tab-strip.tsx

Comment thread packages/sql-editor/src/codemirror/extensions/completion.ts Outdated
Comment thread packages/sql-editor/src/codemirror/extensions/completion.ts Outdated
Comment thread packages/sql-editor/src/codemirror/utils/schema-converter.ts Outdated
@ilyabo
ilyabo merged commit 87549ca into main May 31, 2026
4 checks passed
@ilyabo
ilyabo deleted the sql-editor-improvements branch May 31, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants