feat: curated SQL function list — Tab completion (#15) + typing-time typo hint (#16)

Add src/dsl/sql_functions.rs (KNOWN_SQL_FUNCTIONS) as the shared source
of truth at sql_expr_ident slots:

- #15: offer the functions as Tab candidates under a new
  CandidateKind::Function + ninth Theme colour tok_function (blue,
  distinct from keyword/identifier/type).
- #16: restore the column-typo flag the #6 fix had dropped wholesale —
  invalid_ident_at_cursor now bails only when the partial prefix-matches
  a known function, else falls through to the schema-column check.

A column named like a function (e.g. `count`) is deduped (column wins).
`cast` is excluded — CAST(x AS type) is not a plain-call shape.
The no-validation-allowlist posture stands: the list drives completion +
the typo hint only, never parse-time acceptance.

Docs: ADR-0022 Amendment 6, ADR-0031 status note, README index,
requirements I3/I4 + refreshed test baseline.
This commit is contained in:
claude@clouddev1
2026-05-31 11:49:10 +00:00
parent 01ec926ec8
commit 6d8c9eea36
10 changed files with 570 additions and 25 deletions
+33
View File
@@ -1344,6 +1344,39 @@ mod tests {
}
}
#[test]
fn advanced_select_genuine_column_typo_before_from_warns_at_typing_time() {
// Issue #16: the gap the issue-#6 trade-off opened. While the
// user types `select Agx` (no FROM yet, so the schema-existence
// diagnostic stays silent), a genuine column typo must warn at
// typing time via the restored `invalid_ident` path — `Agx`
// matches neither a schema column nor a known function name.
use crate::completion::{SchemaCache, TableColumn};
use crate::dsl::types::Type;
let mut cache = SchemaCache::default();
cache.tables.push("Customers".to_string());
let tc = vec![TableColumn {
name: "Age".to_string(),
user_type: Type::Int,
not_null: false,
has_default: false,
}];
for c in &tc {
cache.columns.push(c.name.clone());
}
cache.table_columns.insert("Customers".to_string(), tc);
let input = "select Agx";
match ambient_hint_in_mode(input, input.len(), None, &cache, Mode::Advanced) {
Some(AmbientHint::Prose(p)) => assert!(
p.contains("No such") && p.contains("Agx"),
"a genuine column typo before FROM must warn at typing time; got: {p:?}",
),
other => panic!(
"`select Agx` must surface a typing-time typo hint; got: {other:?}",
),
}
}
#[test]
fn advanced_partial_typing_does_not_leak_bare_double_in_prose() {
// Issue #5 (prose half): at `create table Orders (count` (no