-
-
Notifications
You must be signed in to change notification settings - Fork 1
SQL Completion Engine
Zentro implements context-aware SQL autocomplete entirely in the frontend (frontend/src/lib/monaco/). It does not call the backend for suggestion data β all schema information is read from the in-memory schemaStore and resultStore caches.
frontend/src/lib/monaco/
βββ sqlCompletionProvider.ts # Monaco CompletionItemProvider registration
βββ sqlCompletionEngine.ts # Public re-export barrel
βββ sqlCompletionCore.ts # High-level API (analyzeSqlText, buildSqlCompletionItems, β¦)
βββ sqlCompletionAnalysis.ts # Statement boundary detection + full SqlAnalysis builder
βββ sqlCompletionTokenizer.ts # Token stream from raw SQL text
βββ sqlCompletionBuilder.ts # Assemble CompletionItem[] from analysis + schema
βββ sqlCompletionBuilderUtils.ts # Range helpers, offset-at-position
βββ sqlCompletionCatalog.ts # findCatalogMatches β look up tables/views in schema tree
βββ sqlCompletionSources.ts # Extract FROM/JOIN sources + CTEs from statement
βββ sqlCompletionIdentifiers.ts # Alias normalization, quoteIdentifierForDriver, getSchemasForActiveDatabase
βββ sqlCompletionJoinSuggestions.ts # JOIN β¦ ON snippet generation from FK relationships
βββ sqlCompletionProjection.ts # SELECT column projection (table.* expansion)
βββ sqlCompletionTypes.ts # All shared TypeScript types
βββ sqlCompletionConstants.ts # SQL keyword lists, clause keywords
βββ sqlDriverFacade.ts # Per-driver dialect hooks (quoting style, keywords)
βββ sqlSuggestionSchemaContext.ts # Schema-aware suggestion ranking
βββ sqlSuggestionTableDocs.ts # resolveCompletionItem: lazy-load column docs
βββ sqlFolding.ts # Code folding provider (BEGIN/END blocks)
βββ sqlHighlight.ts # Custom token colorization rules
βββ sqlTableNavigation.ts # Go-to-table action
βββ resultFilterCompletion.ts # Completion for the result-panel filter bar
registerContextAwareSQLCompletion is called once after Monaco is initialized (inside features/editor/MonacoEditor.tsx). It registers a CompletionItemProvider for the sql language with a set of trigger characters:
triggerCharacters: ['.', ' ', ',', '(', 'j', 'J', 'o', 'O', 'i', 'I', 'n', 'N']The j/o/i/n characters trigger early so JOIN snippet suggestions appear as the user types the keyword.
Any time the connection changes (new schema), the old provider is disposed and a new one is registered with the updated schema.
When the user triggers completion, provideCompletionItems runs this pipeline:
model.getValue() β full editor text
cursorOffset β position in chars from start
analyzeSqlText(text, offset)
β
ββ findStatementStartOffset() β locate the ; before cursor
ββ findStatementEndOffset() β locate the ; after cursor
ββ tokenizeSql(statementText) β SqlToken[] + finalDepth (paren depth)
ββ detectClause(tokens, depth) β "select" | "from" | "join" | "where" | "on" | β¦
ββ isInsideLineCommentOrString() β bail early if true
ββ afterDotMatch β is cursor after a dot? (table.column completion)
ββ extractCtes(statementSpan) β WITH cte_name AS (β¦) β CTE map
ββ extractSources(span, tokens) β FROM/JOIN table references + aliases
returns SqlAnalysis {
clause, isInCommentOrString, afterDot, dotIdentifier,
sources, ctes, cursorDepth, statementText, ...
}
tokenizeSql in sqlCompletionTokenizer.ts makes a single linear pass over the SQL text character by character. It tracks:
- String literals (
'...') and line comments (--) and block comments (/* */) β tokens inside these are suppressed - Parenthesis depth β used to scope subquery clause detection
- Keyword tokens (normalized to uppercase) with their position offsets
- Punctuation tokens:
(,),,,;,.
detectClause walks the token array from the cursor backward (skipping nested subqueries at deeper depth) to find the nearest clause keyword at the cursor's paren depth.
getSchemasForActiveDatabase reads directly from useSchemaStore.getState().trees keyed by "ProfileName:DBName" β no async call. The schema tree is populated when the user connects (via the schema:loaded Wails event).
const schemas = getSchemasForActiveDatabase(
useSchemaStore.getState().trees,
profile.name, // profileKey
profile.db_name // dbName
);
// schemas: SchemaNode[] = [{ Name: "public", Tables: ["users", "orders"], Views: [...], ... }]Column definitions are fetched lazily β checkAndFetchColumns in schemaStore deduplicates in-flight requests and caches results for 5 minutes (TTL: COLUMN_CACHE_TTL_MS).
buildSqlCompletionItems in sqlCompletionBuilder.ts decides what to suggest based on SqlAnalysis.clause and afterDot:
| Condition | Suggestions |
|---|---|
afterDot === true, dotIdentifier matches an alias or table name |
Columns of that table |
afterDot === true, dotIdentifier matches a schema name |
Tables in that schema |
clause === "select" |
Columns from all FROM/JOIN sources + functions + keywords |
clause === "from" or clause === "join"
|
Tables, views, CTEs from all schemas |
clause === "where" or clause === "on"
|
Columns from all sources + operators |
Typing jo, joi, join in FROM/JOIN context |
JOIN snippet suggestions (see below) |
| Any clause | SQL keywords, functions, templates from templateStore
|
sqlCompletionJoinSuggestions.ts activates when the cursor is in a from or join clause and the current word starts with j or matches a JOIN prefix.
For each table already in the FROM/JOIN list (analysis.sources), it:
- Calls
fetchRelationships(schemaName, tableName)β returns foreign key pairs fromschemaStore(cached, async). - Groups FK column pairs by
(constraintName, joinTable)to handle composite keys. - Generates a Monaco
CompletionItemof kindSnippetwith aninsertTextlike:
INNER JOIN public.orders o ON users.id = o.user_idsqlCompletionProjection.ts handles SELECT table.* expansion. When the cursor is in the SELECT clause after a tablealias.* pattern, it fetches all columns for that table and expands them into individual column references.
When the user hovers or selects a table suggestion in the dropdown, Monaco calls resolveCompletionItem. sqlSuggestionTableDocs.ts intercepts this, fetches the column list from schemaStore, and injects a formatted Markdown documentation string listing column names and types β without blocking the initial suggestion list.
sqlDriverFacade.ts and sqlDriverFacade registration allow per-driver quoting and keyword customization. The active driver name comes from useConnectionStore.getState().activeProfile?.driver. Quoting styles:
| Driver | Identifier quoting |
|---|---|
postgres |
"identifier" |
mysql |
`identifier` |
sqlserver |
[identifier] |
sqlite |
"identifier" |
- Managing Connection Profiles
- Environment Management
- Importing & Exporting Connections
- Supported Databases
- Writing & Running Queries
- SQL Autocomplete
- Query Formatting
- Multi-tab & Tab Groups
- Query Bookmarks
- Query Comparison