import type { SqlDocument } from "../document/document.js"; import { type PartSpan } from "../ir/part-span.js"; import type { SchemaProvider } from "../qualify/schema-provider.js"; import { type TemplateCall } from "../qualify/template-provider.js"; import { type ResolvedSource } from "../scope/scope.js"; /** One completion candidate, already pruned to the typed prefix (2026-07-12 ruling) and applied at * the caret / `CompletionResult.replaceRange`. The `"template"` kind is a host candidate for a * jinja call slot (a dbt model for a ref's arg) — its own, separately-decided contract (the * consumer still filters those by the typed prefix; see complete.jinja-candidates.test.ts). */ export interface Completion { label: string; kind: "keyword" | "column" | "table" | "cte" | "namespace" | "function" | "template"; /** Extra display info, e.g. a column's type when the schema knows it. */ detail?: string; /** Long-form documentation for the candidate. completeAt's own resolution never fills this in — * it is set ONLY by a `decorate` hook (CompleteOptions.decorate) answering one. Absent when no * hook ran, or the hook answered nothing for this candidate. */ documentation?: string; } /** The caret-anchored span of the partial identifier/keyword the candidates were pruned against — * `text.slice(start, end)` is what's already typed. `start` includes an opening delimiter when the * caret sits inside a quoted/bracketed/backtick-quoted identifier (`"my_t`, `` `my_t ``, `[my_t`), * so an editor that replaces this span never leaves a stray leading quote. `end` never extends past * the caret (`offset`) — even where a dialect's lexer greedily swallows an unterminated quoted * identifier past the caret, only the already-typed portion is ever reported or matched. */ export interface ReplaceRange { start: number; end: number; } /** completeAt()'s result: an ordinary `Completion[]` (`.map`/`.filter`/iteration/`.length` all work * exactly as before — every existing consumer sees no change) carrying one optional extra * property, the same "array with named extras" shape TypeScript's own `RegExpMatchArray` uses for * `String.prototype.match`. `replaceRange` is present only when the caret sits inside a partially * typed word; an empty-prefix caret (a token boundary — nothing typed yet) returns a plain array * with no `replaceRange`, byte-identical to the pre-pruning contract. */ export interface CompletionResult extends Array { replaceRange?: ReplaceRange; } /** The decoration hook's answer for ONE candidate — display text supplied from real structure, * merged onto the Completion completeAt is about to return. Every field optional: an absent field * leaves that part of the candidate as completeAt itself produced it (a schema-fed column's own * `detail` survives unless the hook overrides it); returning nothing at all leaves the candidate * wholly undecorated. */ export interface CandidateDecoration { detail?: string; documentation?: string; } /** * The STRUCTURAL identity of a candidate completeAt is about to return — anvil's decoration-hook ask * (channel, 2026-07-20): "hand back STRUCTURAL identity, not just the label string". Discriminated by * the candidate's own `kind`. Every extra field is present ONLY when completeAt's own resolution * already produced it (never-wrong: nothing here is synthesized or re-derived) — e.g. a "column" * candidate from the broken-input FROM/JOIN token-stream fallback (fromRelationColumns / * qualifiedFallbackColumns) carries no `source`, because no ResolvedSource exists on that path. * * `"template"` covers the jinja call-slot candidates (a dbt model name for a `ref('|` arg, a source * name for `source('|`) — the "table candidate that resolves through a templated call" from the * ask: its `call` is the SAME TemplateCall (`JinjaSlot.call`) `templateCandidates` was already asked * with, so a consumer names the model/source without re-parsing the call text. */ export type CandidateIdentity = { kind: "keyword"; } | { kind: "function"; } | { kind: "namespace"; } | { kind: "table"; } | { kind: "cte"; /** The CTE name's own declaration span (`CteDef.nameCst`, falling back to the whole * `CteDef.cst` when the name has no real token) — pins the RIGHT declaration even when * another CTE of the same name shadows it in a nested scope. Absent only when the CTE * itself has no real token to key on (a broken/nameless mid-edit CTE). */ declarationSpan?: PartSpan; } | { kind: "column"; /** The resolved scope source this column came from (the same ResolvedSource * scope/qualify already produced — a table/CTE/subquery/lateral/relation/graphtable/pivot). * Absent when the column came from the broken-input token-stream fallback, which has no * ResolvedSource to carry. */ source?: ResolvedSource; } | { kind: "template"; call: TemplateCall; }; /** Per-candidate decoration hook (`CompleteOptions.decorate`): completeAt calls this once for each * candidate it is about to return, with the candidate as built so far and its structural identity, * and merges the answer's `detail`/`documentation` onto it. The candidate SET is unaffected — this * only supplies display text, never adds/removes/reorders a candidate. Total-safe: a throwing hook * degrades to the undecorated candidate (never breaks completeAt), the same total-by-contract * posture every other completeAt internal failure gets (SQLLENS_DEBUG=1 rethrows — src/debug.ts). */ export type DecorateCandidate = (candidate: Completion, identity: CandidateIdentity) => CandidateDecoration | undefined | void; /** completeAt's options — currently just the decoration hook. Additive: every existing 3-arg call * site keeps compiling and behaving byte-identically (no `opts`, no decoration). */ export interface CompleteOptions { decorate?: DecorateCandidate; } /** * Completion candidates for the caret at `offset` in `doc`, pruned to the identifier/keyword * fragment already typed there (case-insensitive, dialect-fold-aware; plain prefix match — never * fuzzy). Schema-aware when a `Schema` is given (table names + column types). NEVER throws: on * broken / mid-edit input it still returns the keyword candidates the walk can reach. */ export declare function completeAt(doc: SqlDocument, offset: number, schema?: SchemaProvider, opts?: CompleteOptions): CompletionResult; /** @deprecated Use completeAt — same function, uniform cursor-verb naming. */ export declare const complete: typeof completeAt;