anitnilay20 / anitnilay20/thoth

Seshat: context-ranked SQL autocomplete in the editor

Open
#121 0 comments 0 reactions 0 assignees View on GitHub
feature future plugin:database priority:medium size:medium ux
Dominant language
Rust
Stars
70
Forks
5
Avg merge
10h 33m
Merged PRs (30d)
2

Description

## Problem

The SQL editor's autocomplete is **prefix-only over one flat word set**. `editor_view` flattens table names into the syntax's `special` list ([editor.rs:151-157](plugins/seshat/src/ui/editor.rs#L151-L157)), and the SDK hands that straight to `egui_code_editor::Completer::new_with_syntax` ([code_editor/mod.rs:276](thoth-plugin-sdk/src/components/code_editor/mod.rs#L276)), which builds a single prefix trie over `keywords + types + special`.

Two consequences:
- **No context.** After `SELECT`, after `FROM`, after `o.`, or at statement start you get the same undifferentiated list, ranked only by prefix.
- **Columns aren't suggested at all** — only table names are fed in. The data exists (`TableNode.columns: Option>`, [state.rs:173](plugins/seshat/src/state.rs#L173)) but lazily (populated on describe).

## Goal

Context-aware, schema-aware completion that suggests the *right* subset based on the token before the caret — tables after `FROM`/`JOIN`, a table's columns after `alias.`, in-scope columns after `SELECT`/`WHERE`, keywords at statement start.

## Design sketch

### Context classifier (no SQL parser needed)
A lightweight backward scan from the caret, scoped to the current statement (reuse `crate::sql::statements`, [editor.rs:35](plugins/seshat/src/ui/editor.rs#L35)):

```
enum CompletionContext {
TableRef, // after FROM, JOIN, UPDATE, INTO → tables (+ schemas)
ColumnOf(String), // after `alias.` / `table.` → that table's columns
ColumnInScope, // after SELECT/WHERE/ON/,/AND/ORDER → columns of FROM tables + funcs
Keyword, // statement start / fallback → verbs + keywords
Suppress, // inside string / comment → nothing
}
```

### Alias resolution
~30-line token pass over the current statement's FROM/JOIN chain → `alias → table` map, so `o.` resolves to `orders`' columns. No full parser.

### Architecture (the key decision)
Do the ranking **host-side**, not plugin-side. Round-tripping text+caret to the WASM plugin on every keystroke is exactly what made the editor sluggish before. Instead:
- Plugin stops flattening into `special` and hands the host **structured, scoped schema** (a new `CompletionSchema { tables: [{ name, columns }], functions }` on `CodeEditor`).
- Host runs `classify` + ranking locally against egui's live caret — zero round-trip.

### The real cost
`egui_code_editor::Completer` is a fixed trie and can't express "show this subset now, that subset next keystroke." So context ranking means **bypassing its Completer with a small custom popup layer** (candidate list + prefix filter + kind icon + Tab/Enter accept — the Enter→Tab rewrite at [code_editor/mod.rs:299-311](thoth-plugin-sdk/src/components/code_editor/mod.rs#L299-L311) shows the shape). That popup (~200-300 lines in the SDK) is the bulk of the work; the classifier + alias map are cheap.

### Lazy columns
Columns load on describe. On the first `alias.` for a not-yet-described table, emit a describe request (reuse `Request::DescribeTable`) to prefetch; fall back to table names until they arrive — never block.

## Phasing

| Phase | Change | Effort | Payoff |
|---|---|---|---|
| **0** | Also feed loaded columns into `special` (one `.chain()` in editor.rs) | ~10 lines | Column completion works immediately (still context-free) |
| **1** | `CompletionSchema` on `CodeEditor` + host-side `classify` + custom popup | bulk | Context-scoped suggestions |
| **2** | Alias map + lazy column prefetch | moderate | `o.` → orders' columns |
| **3** | Kind icons, snippets (`SELECT * FROM`), keyword casing | small | Polish |

Phase 0 is worth doing regardless — quick win, and validates columns are available client-side before committing to the popup rebuild.

## Notes
- Keep the completer **lazy + scoped to the selected DB** — the earlier "loads everything → sluggish" regression is the guiding constraint.
- Standalone Seshat UX improvement; not part of the cross-plugin data ecosystem (#118) or the additional-data-source epic (#109).

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.