sqlcomplete

package
v0.0.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package sqlcomplete answers "what may stand where the caret is" for a ClickHouse buffer (ADR-0190).

It is built on one commitment, §SD1: a candidate is offered only from a source of truth — a registry, the grammar's own vocabulary, the parsed statement's names, or a catalog answer for the buffer's endpoint. Where none exists the engine offers nothing and says why. There is no "plausible" band below an "exact" one, and no ranking that quietly promotes a guess.

The three inputs

What the engine does not do

It does not rank, it does not fuzzy-match, and it does not lex or parse. The match state is a case-sensitive prefix and equality test over the token the site reports, computed per frame; anything needing the tree arrives through Request.Scope a quiescence window later.

Index

Constants

View Source
const Sentinel = "__caret__"

Sentinel is the identifier a repair puts where the caret is.

It has to be something no buffer contains and something grammar1 accepts as an identifier wherever a partial token can sit — which rules out a comment marker or a punctuation glyph. The leading and trailing underscores are what keep it out of a user's own naming.

Variables

View Source
var PackageProps = packageprops.Props{
	WASMWASI:         packageprops.WASMCompiles,
	WASMJS:           packageprops.WASMCompiles,
	WASMFreestanding: packageprops.WASMCompiles,
}

PackageProps records this package's curated properties (ADR-0080). Seeded by `boxer code analysis golang wasmsurvey props generate`; curate by hand. The same group's `props verify` reconciles it.

Functions

func Repair

func Repair(stmt string, site highlight.CaretSite, caret int) (attempts []string, sentinelAt []int)

Repair rewrites the caret's statement into something the parser accepts (ADR-0190 §SD3).

The repair is deterministic rather than heuristic, because the site already knows what is open: the token being completed is replaced by Sentinel, an unterminated literal is closed around it, and the brackets the walk reports unclosed get their closers.

Two attempts, in order:

  1. the tail after the caret is kept, which is what a caret moved back into a finished statement needs;
  2. the tail is cut, which is what a statement whose tail is itself unfinished needs — and then every bracket open AT the caret is closed, not only those the whole statement left open.

sentinelAt is where the sentinel starts in each attempt, so a consumer that finds several matches in a pathological buffer can pick the right one.

Types

type Catalog

type Catalog struct {
	Databases ItemsFn
	// Tables answers a named database's tables, views and table functions, or
	// the buffer's default database when the argument is empty.
	Tables ItemsOfFn
	// Columns answers a table's columns, or the statement's own sources' when
	// the argument is empty.
	Columns ItemsOfFn
	// ColumnType answers one column's type, for the typer. table may be
	// empty, meaning the buffer's single source.
	ColumnType func(table string, column string) (t chtype.Type, ok bool)

	Functions            ItemsFn
	Settings             ItemsFn
	TypeNames            ItemsFn
	TimeZones            ItemsFn
	Dictionaries         ItemsFn
	DictionaryAttributes ItemsOfFn
	Formats              ItemsFn
	// EnumValues answers a column's Enum members, derived from its type
	// string (§SD12 B11).
	EnumValues ItemsOfFn
}

Catalog is the endpoint-dependent half: what `system.*` answers for the buffer this engine serves. Routed per buffer because two buffers may point at different endpoints, and because ad-hoc datasets contribute tables no `system.tables` enumerates (ADR-0190 §SD12).

type Engine

type Engine struct {
	// Vocab is the host's function registry — what the rosters declare.
	Vocab *sqlvocab.Registry
	// Builtins is the curated ClickHouse table; nil means [sqlvocab.Builtins].
	Builtins []sqlvocab.Function
	// Providers resolve a domain to candidates.
	Providers Providers
	// Typer answers the type-dependent domains; nil means one is built on
	// first use over Providers.
	Typer *Typer
	// NamedTupleAccess reports that the buffer's pipeline accepts `expr.name`
	// on a named tuple (ADR-0190 §SD11). Until it does, offering the fields of
	// a call receiver would offer a spelling that does not parse, so §SD7 gates
	// that one receiver on this.
	NamedTupleAccess bool
	// contains filtered or unexported fields
}

Engine answers completion requests for one buffer.

Render-thread-only, like the typer it owns: the memo is unsynchronised and each buffer has its own engine.

func (*Engine) Complete

func (inst *Engine) Complete(req Request) (res Result)

Complete answers one request.

The resolution order is member access, then the innermost call frame whose signature is known, then the clause. It stops at the first that answers: an argument position's domain is what belongs there, and a clause rule that overrode it would be the coarse answer ADR-0190 was written to replace.

func (*Engine) Validate

func (inst *Engine) Validate(stmt string, scope *Scope, caret int) (findings []Finding)

Validate walks the statement's literals and reports what each resolves to.

caret is excluded from the walk — pass a negative offset to validate a statement with no caret in it at all, which is what a consumer checking a buffer it is not editing wants.

type Finding

type Finding struct {
	// Range is the literal's content, quotes excluded — what a tint covers.
	Range highlight.Range
	// Text is the content.
	Text string
	// Domain is the domain the position declares.
	Domain sqlvocab.Domain
	// Resolved is true when the domain has a member equal to Text.
	Resolved bool
	// Callee is the enclosing call, for a message.
	Callee string
}

Finding is one literal and what the engine could say about it.

type Item

type Item struct {
	// Text is the candidate's identity — the value, unquoted: `SysMem`,
	// `TotalBytes`, `system.parts`. It is what a match compares against and
	// what the pane's first column shows.
	Text string
	// Insert is the spelling that names it at the caret's position. It equals
	// Text inside a string literal, and carries the quotes when the position
	// takes a literal and none has been opened. Filled by the engine, not by
	// the provider, because only the engine knows where the caret is.
	Insert string
	Kind   ItemKindE
	// Type is the candidate's ClickHouse type where it has one — a component
	// field, a column. Empty otherwise.
	Type string
	// Doc is one line, for the pane's last column.
	Doc string
	// Source names the provider, so a row can say where it came from (§SD1's
	// provenance requirement).
	Source string
	// Marks are the provisioning marks the Vocabulary tab shows — `✓`,
	// `MISSING`, a dependency note. Carried through rather than filtered on:
	// hiding a MISSING function would hide the provisioning fact (§SD8).
	Marks []string
}

Item is one candidate.

type ItemKindE

type ItemKindE uint8

ItemKindE says what a candidate is, for the pane's kind column and for an embedder choosing an icon. It is presentation, not semantics: the domain the candidate came from is what says where it is valid.

const (
	ItemUnspecified ItemKindE = iota
	ItemComponentKind
	ItemField
	ItemTable
	ItemDatabase
	ItemColumn
	ItemFunction
	ItemKeyword
	ItemSection
	ItemMembership
	ItemChannel
	ItemSupportRole
	ItemAspect
	ItemCanonicalType
	ItemGloss
	ItemGlossKey
	ItemIdentityTag
	ItemTypeName
	ItemTimeZone
	ItemSetting
	ItemDictionary
	ItemFormat
	ItemAlias
	ItemEnumValue
	ItemParam
)

func (ItemKindE) String

func (inst ItemKindE) String() string

type ItemsFn

type ItemsFn func() (items []Item, ready bool)

ItemsFn answers a closed domain. ready=false means "not known yet" — an ADR-0147 §SD6 probe that has not come back — and is deliberately distinct from an empty answer, which is a claim that the domain has no members. The engine says nothing either way, but says a different why (§SD1, and ADR-0174's `?`-never-`MISSING` rule).

type ItemsOfFn

type ItemsOfFn func(of string) (items []Item, ready bool)

ItemsOfFn answers a domain that depends on a sibling argument's value.

type MatchE

type MatchE uint8

MatchE is the state of the token under the caret against the candidates.

const (
	// MatchNone is nothing typed, or nothing that extends what was typed.
	MatchNone MatchE = iota
	// MatchPrefix is one or more candidates extending the typed text.
	MatchPrefix
	// MatchExact is a candidate equal to the token's whole text — the state
	// §SD9 tints the editor for.
	MatchExact
)

func (MatchE) String

func (inst MatchE) String() string

type Providers

type Providers struct {
	ComponentKinds ItemsFn
	// ComponentType answers the whole named Tuple a kind projects.
	//
	// A type rather than a list of rows, because that is what composes: the
	// typer needs the element's own type for
	// `tupleElement(tupleElement(LW_COMPONENT(k),'a'),'b')`, and the field
	// list falls out of the type. A provider keyed on a kind and answering
	// rows would serve the LW_COMPONENT spelling and nothing else.
	ComponentType func(kind string) (t chtype.Type, ok bool)

	IntrospectionTables ItemsFn
	Sections            ItemsFn
	SectionColumns      ItemsOfFn
	ExtractionTokens    ItemsOfFn
	Memberships         ItemsFn
	Channels            ItemsFn
	SupportRoles        ItemsFn
	Aspects             ItemsFn
	CanonicalTypes      ItemsFn
	Glosses             ItemsFn
	GlossKeys           ItemsOfFn
	IdentityTags        ItemsFn
	StatementParams     ItemsFn
	// Expressions answers a free expression position — the columns of the
	// statement's single source, the functions the endpoint has, the names
	// this build's vocabulary declares. Its argument is that source, qualified,
	// or empty when the statement has none or more than one.
	//
	// It exists because "any expression" is not one catalogue: a column and a
	// function are both valid at a SELECT position, and a provider returning
	// only one of them would be exactly as wrong as returning neither.
	Expressions ItemsOfFn

	// Catalog is the server's own vocabulary for this buffer's endpoint —
	// every entry an ADR-0147 §SD6 probe. Empty until M2.
	Catalog Catalog
}

Providers is the host's wiring, per buffer (ADR-0147 §SD7). A nil field is a domain this host cannot resolve; the engine reports that rather than offering nothing without a reason.

type Request

type Request struct {
	// Site is the per-frame answer from the lex tier.
	Site highlight.CaretSite
	// Scope is the sentinel parse's answer, nil until it arrives.
	Scope *Scope
	// Statement is the buffer the site's ranges index.
	Statement string
	// Caret is the caret's byte offset into Statement.
	Caret int
}

Request is one caret's question.

type Result

type Result struct {
	// Domain is the argument domain the engine resolved, zero when it
	// resolved none.
	Domain sqlvocab.Domain
	// Items are the candidates, in the provider's own order.
	Items []Item
	// Partial is the byte range in Statement a completion replaces.
	Partial highlight.Range
	// Match is the state of what was typed against Items.
	Match MatchE
	// Exact is the index in Items of the candidate equal to the token's whole
	// text, or -1.
	Exact int
	// Prefix are the indices of the candidates extending the typed text — all
	// of them when nothing has been typed.
	Prefix []int
	// Silent says why there is nothing to offer, for the pane to show instead
	// of an empty table. Empty when Items is non-empty.
	//
	// Every path that offers nothing sets it: ADR-0190 §SD1 commits to silence
	// over guessing, and a silence with no reason is indistinguishable from a
	// bug.
	Silent string
	// Callee is the enclosing call the domain came from, for the pane's
	// heading. Empty when the domain came from the clause or a member access.
	Callee string
	// Ordinal is the argument position within Callee, or -1.
	Ordinal int
}

Result is what the pane and the editor tint render.

func (Result) Empty

func (inst Result) Empty() bool

Empty reports whether there is nothing to show.

func (Result) ExactItem

func (inst Result) ExactItem() (it Item, ok bool)

ExactItem is the exactly-matching candidate.

func (Result) TabCompletion

func (inst Result) TabCompletion(typed string) (suffix string, ok bool)

TabCompletion is what one captured Tab inserts (ADR-0190 §SD10): the suffix of the unique prefix match, or — when several match — the longest common prefix beyond what has been typed.

Shell-style, deliberately. A list with several candidates is not a menu here: the pane already shows them, so the key's job is to type the part they agree on and leave the choice visible. When they agree on nothing more, ok is false and the key inserts nothing, which is what a shell does too.

typed is what precedes the caret; the caller must have checked that the caret is at the token's end (a suffix insert is only valid there).

type Scope

type Scope struct {
	// Frame is the tree's own view of the caret's call. For a comma-separated
	// call it must agree with the site's; for a keyword-syntax call
	// (`CAST(x AS T)`) it is the only one there is.
	Frame *highlight.CallFrame
	// Aliases maps an alias to the source text of the expression it names, so
	// the typer can recurse into it.
	Aliases map[string]string
	CTEs    []string
	Tables  []TableRef
	Windows []string
	// Clause is the clause the sentinel landed in, spelled as
	// [highlight.CaretSite.Clause] spells it so the two tiers do not disagree
	// about the same word.
	Clause string
}

Scope is what the statement's own tree adds to the site: the names the statement itself introduces (ADR-0190 §SD3).

It arrives one quiescence window behind the buffer, from a sentinel parse on a worker, because the parser costs 5–18 ms while its DFA warms (ADR-0084). Everything the site alone can answer is answered per frame without it, so a nil Scope narrows what the engine knows rather than stopping it.

func ParseScope

func ParseScope(stmt string, site highlight.CaretSite, caret int) (sc *Scope, err error)

ParseScope repairs the statement and parses it, returning what the tree adds to the site.

The first attempt that parses wins. When neither does — a JOIN position, for one, where the grammar wants ON or USING — the error says so and the site alone stays the model, which is what §SD3 accepts.

func (*Scope) AliasOf

func (inst *Scope) AliasOf(name string) (expr string, ok bool)

AliasOf resolves an alias to its defining expression.

func (*Scope) LookupTable

func (inst *Scope) LookupTable(name string) (ref TableRef, ok bool)

LookupTable resolves a table alias, or a table named directly, to its source.

type TableRef

type TableRef struct {
	Database string
	Name     string
	Alias    string
}

TableRef is one source in the statement's FROM.

type Typer

type Typer struct {
	// Providers is where the registry answers come from — the component
	// registry, the column probe.
	Providers *Providers
	// Scope is the statement's alias map, nil until the scope tier answers.
	Scope *Scope
	// contains filtered or unexported fields
}

Typer maps an expression to a ClickHouse type when it can (ADR-0190 §SD5).

It is a closed list of shapes on purpose. Outside it the answer is *unknown*, and unknown yields nothing — a wrong element list read off a guessed type is exactly the failure §SD1 exists to prevent. The rungs below the list are the server (`DESCRIBE (SELECT …)`, M4), not a heuristic.

It works on the lex tier, not on a parse: the expression texts it sees are argument slices of a buffer being typed, and the parser costs 5–18 ms while its DFA warms (ADR-0084), which is not a per-frame budget. The shapes it recognises — a literal, a call with a literal argument, a cast — are syntactically shallow enough that lexing is the honest tool for them.

Render-thread-only: the memo is unsynchronised, and each buffer's engine has its own.

func (*Typer) TypeOf

func (inst *Typer) TypeOf(expr string) (t chtype.Type, ok bool)

TypeOf answers the type of an expression's source text.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL