sqllens 1.5.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/api.d.ts +6 -2
  2. package/dist/api.js +9 -2
  3. package/dist/bigquery/lower.js +28 -2
  4. package/dist/bigquery/reserved.generated.d.ts +2 -0
  5. package/dist/bigquery/reserved.generated.js +325 -0
  6. package/dist/completion/complete.d.ts +95 -7
  7. package/dist/completion/complete.js +188 -42
  8. package/dist/completion/config.js +25 -4
  9. package/dist/databricks/behavior.js +3 -1
  10. package/dist/databricks/infer.d.ts +4 -0
  11. package/dist/databricks/infer.js +278 -41
  12. package/dist/databricks/lower.js +456 -37
  13. package/dist/databricks/reserved.generated.d.ts +2 -0
  14. package/dist/databricks/reserved.generated.js +426 -0
  15. package/dist/debug.d.ts +2 -0
  16. package/dist/debug.js +20 -0
  17. package/dist/dialect-behavior/behavior.d.ts +8 -1
  18. package/dist/dialect-symbols.d.ts +46 -0
  19. package/dist/dialect-symbols.js +86 -0
  20. package/dist/document/document.d.ts +52 -21
  21. package/dist/document/document.js +328 -51
  22. package/dist/document/node-at.js +8 -5
  23. package/dist/document/split.js +3 -1
  24. package/dist/duckdb/lower.js +99 -34
  25. package/dist/duckdb/reserved.generated.d.ts +2 -0
  26. package/dist/duckdb/reserved.generated.js +560 -0
  27. package/dist/generated/databricks/DatabricksParser.js +5852 -5822
  28. package/dist/generated/duckdb/DuckdbParser.js +15042 -15030
  29. package/dist/generated/mysql/MysqlLexer.js +5131 -5128
  30. package/dist/generated/mysql/MysqlParser.js +1616 -1604
  31. package/dist/generated/postgres/PostgresParser.js +2305 -2292
  32. package/dist/generated/redshift/RedshiftLexer.js +3478 -3465
  33. package/dist/generated/redshift/RedshiftParser.js +17788 -17463
  34. package/dist/generated/snowflake/SnowflakeLexer.js +5683 -5673
  35. package/dist/generated/snowflake/SnowflakeParser.js +20361 -19877
  36. package/dist/generated/tsql/TSqlLexer.js +6241 -6178
  37. package/dist/generated/tsql/TSqlParser.js +13255 -11071
  38. package/dist/index.d.ts +2 -2
  39. package/dist/index.js +1 -1
  40. package/dist/infer/coerce.d.ts +5 -0
  41. package/dist/infer/coerce.js +39 -4
  42. package/dist/infer/infer.js +175 -18
  43. package/dist/infer/nullability.js +3 -3
  44. package/dist/infer/types.d.ts +11 -0
  45. package/dist/infer/types.js +73 -0
  46. package/dist/ir/ir.d.ts +78 -2
  47. package/dist/ir/walk.d.ts +8 -1
  48. package/dist/ir/walk.js +19 -3
  49. package/dist/lineage/hops.js +10 -3
  50. package/dist/lineage/lineage.js +7 -2
  51. package/dist/minijinja/apply-tags.js +3 -1
  52. package/dist/minijinja/parse.js +3 -1
  53. package/dist/minijinja/variants.js +3 -1
  54. package/dist/mysql/lower.js +36 -8
  55. package/dist/mysql/reserved.generated.d.ts +2 -0
  56. package/dist/mysql/reserved.generated.js +1120 -0
  57. package/dist/postgres/infer.d.ts +0 -1
  58. package/dist/postgres/infer.js +74 -15
  59. package/dist/postgres/lower.js +79 -12
  60. package/dist/postgres/reserved.generated.d.ts +2 -0
  61. package/dist/postgres/reserved.generated.js +534 -0
  62. package/dist/qualify/check-calls.js +8 -2
  63. package/dist/qualify/qualify.js +3 -1
  64. package/dist/redshift/lower.js +111 -13
  65. package/dist/redshift/reserved.generated.d.ts +2 -0
  66. package/dist/redshift/reserved.generated.js +827 -0
  67. package/dist/references/references.js +46 -1
  68. package/dist/scope/clauses.d.ts +19 -0
  69. package/dist/scope/clauses.js +204 -0
  70. package/dist/scope/frame.d.ts +16 -0
  71. package/dist/scope/frame.js +51 -0
  72. package/dist/scope/scope.d.ts +30 -1
  73. package/dist/scope/scope.js +48 -4
  74. package/dist/scope/setop-arms.d.ts +16 -0
  75. package/dist/scope/setop-arms.js +32 -0
  76. package/dist/scope/walk.js +10 -5
  77. package/dist/session.d.ts +9 -2
  78. package/dist/session.js +12 -2
  79. package/dist/signature/signature.js +3 -1
  80. package/dist/snowflake/lower.js +161 -19
  81. package/dist/snowflake/reserved.generated.d.ts +2 -0
  82. package/dist/snowflake/reserved.generated.js +938 -0
  83. package/dist/sqlite/lower.js +25 -2
  84. package/dist/sqlite/reserved.generated.d.ts +2 -0
  85. package/dist/sqlite/reserved.generated.js +155 -0
  86. package/dist/symbols/symbols.d.ts +11 -1
  87. package/dist/symbols/symbols.js +205 -61
  88. package/dist/trino/lower.js +19 -4
  89. package/dist/trino/reserved.generated.d.ts +2 -0
  90. package/dist/trino/reserved.generated.js +313 -0
  91. package/dist/tsql/lower.js +336 -5
  92. package/dist/tsql/reserved.generated.d.ts +2 -0
  93. package/dist/tsql/reserved.generated.js +1185 -0
  94. package/package.json +3 -3
package/dist/api.d.ts CHANGED
@@ -116,14 +116,18 @@ export type { Token, TokenRole } from "./token/token.js";
116
116
  export { SqlDocument, type DocumentAnalysis, type StatementCell, type DocumentVariant, type UnionCte, } from "./document/document.js";
117
117
  export { LineIndex } from "./document/line-index.js";
118
118
  export type { StatementCellSpan } from "./document/split.js";
119
- export { complete, completeAt, type Completion } from "./completion/complete.js";
119
+ export { complete, completeAt, type Completion, type CompletionResult, type ReplaceRange, type CompleteOptions, type CandidateIdentity, type CandidateDecoration, type DecorateCandidate, } from "./completion/complete.js";
120
120
  export { jinjaSlotAt, type JinjaSlot } from "./completion/jinja-slot.js";
121
121
  export { signatureAt, type SignatureHelpInfo, type SignatureLabel } from "./signature/signature.js";
122
122
  export { SIGNATURES, lookupSignature, hasSignature, type FnSignature, type ParamSig } from "./signature/signatures.js";
123
123
  export { renderSignature, type RenderSignatureOptions } from "./signature/render.js";
124
124
  export { FN_DOCS, lookupFnDoc, type FnDoc } from "./signature/docs.js";
125
125
  export { referencesAt, type Occurrence, type Occurrences } from "./references/references.js";
126
- export { dialectSymbols, type DialectSymbols } from "./dialect-symbols.js";
126
+ export { frameAt, type Frame } from "./scope/frame.js";
127
+ export { clausesOf, type ClauseInfo, type ClauseKind } from "./scope/clauses.js";
128
+ export { setOpArmsOf, type SetOpArm, type SetOpArms } from "./scope/setop-arms.js";
129
+ export { dialectSymbols, dialectVocabulary, type DialectSymbols, type DialectVocabulary } from "./dialect-symbols.js";
130
+ export { reservedKeywords, type KeywordEntry, type KeywordReservation } from "./dialect-symbols.js";
127
131
  export { CallbackSchema, type SchemaProvider, type TableResolver } from "./qualify/schema-provider.js";
128
132
  export { DERIVED_DIALECTS, resolveDialect } from "./derived-dialects.js";
129
133
  export { foldIdentifier, displayName } from "./dialect-behavior/public-fold.js";
package/dist/api.js CHANGED
@@ -204,7 +204,7 @@ export { SqlDocument, } from "./document/document.js";
204
204
  export { LineIndex } from "./document/line-index.js";
205
205
  // Scope-aware completion over a SqlDocument — the broken-input editor feature (keywords + schema
206
206
  // tables/columns + function names at the caret). Total: never throws.
207
- export { complete, completeAt } from "./completion/complete.js";
207
+ export { complete, completeAt, } from "./completion/complete.js";
208
208
  // The NEUTRAL half of jinja completion: which call + arg slot the caret sits in inside a jinja tag
209
209
  // (`{{ ref('cu│` → { callee: "ref", argIndex: 0, prefix: "cu" }). Carries no dbt vocabulary; a
210
210
  // consumer maps callee + argIndex to a role and supplies candidates. Reuses the parsed tags.
@@ -223,9 +223,16 @@ export { FN_DOCS, lookupFnDoc } from "./signature/docs.js";
223
223
  // cursor offset. The core primitive behind LSP references / documentHighlight / codeLens / rename.
224
224
  // Total: never throws; returns null off-symbol.
225
225
  export { referencesAt } from "./references/references.js";
226
+ // Frame / clause / set-op-arm geometry: the SQL debugger's marker-planting primitives, which frame
227
+ // (CTE) a document position sits in, that frame's ordered clause list, and set-op arm spans. Total:
228
+ // never throws; frameAt/clausesOf/setOpArmsOf all degrade to undefined/[] off-symbol or off-shape.
229
+ export { frameAt } from "./scope/frame.js";
230
+ export { clausesOf } from "./scope/clauses.js";
231
+ export { setOpArmsOf } from "./scope/setop-arms.js";
226
232
  // Per-dialect function/keyword/type-name membership sets — for lint-style "is this identifier a
227
233
  // known X for this dialect?" checks. Computed once per dialect and cached.
228
- export { dialectSymbols } from "./dialect-symbols.js";
234
+ export { dialectSymbols, dialectVocabulary } from "./dialect-symbols.js";
235
+ export { reservedKeywords } from "./dialect-symbols.js";
229
236
  // The catalog interface the whole pipeline resolves against, plus its resolve-on-demand
230
237
  // implementation. `Schema` (a full upfront mapping) and `CallbackSchema` (a host-driven
231
238
  // lazy resolver whose prime() bumps a version to invalidate SqlDocument.analyze's memo) both
@@ -1482,9 +1482,11 @@ function lowerLeaf(node) {
1482
1482
  case P.RULE_floating_point_literal:
1483
1483
  case P.RULE_date_or_time_literal:
1484
1484
  case P.RULE_range_literal:
1485
+ return { kind: "literal", text: node.getText(), cst: node };
1485
1486
  case P.RULE_parameter_expression:
1487
+ return lowerParameterExpression(node);
1486
1488
  case P.RULE_system_variable_expression:
1487
- return { kind: "literal", text: node.getText(), cst: node };
1489
+ return lowerSystemVariableExpression(node);
1488
1490
  case P.RULE_identifier:
1489
1491
  return { kind: "column", parts: [identText(node)], partSpans: partSpansOf([node]), cst: node };
1490
1492
  case P.RULE_path_expression:
@@ -1844,7 +1846,12 @@ function columnsOf(expr, acc, clause) {
1844
1846
  break;
1845
1847
  case "subscript":
1846
1848
  columnsOf(expr.base, acc, clause);
1847
- columnsOf(expr.index, acc, clause);
1849
+ if (expr.index)
1850
+ columnsOf(expr.index, acc, clause);
1851
+ if (expr.end)
1852
+ columnsOf(expr.end, acc, clause);
1853
+ if (expr.step)
1854
+ columnsOf(expr.step, acc, clause);
1848
1855
  break;
1849
1856
  case "lambda":
1850
1857
  columnsOf(expr.body, acc, clause);
@@ -1949,6 +1956,25 @@ function extractExpressionSubqueries(select, fromQueries) {
1949
1956
  return out;
1950
1957
  }
1951
1958
  // --- name helpers ----------------------------------------------------------------
1959
+ /** parameter_expression: named_parameter_expression | QUESTION_SYMBOL
1960
+ * (cloud.google.com/bigquery/docs/parameterized-queries). Bare `?` carries no name/ordinal —
1961
+ * BigQuery's positional form has no explicit index, so `ordinal` stays a consumer derivation,
1962
+ * never fabricated here. `@name` resolves through named_parameter_expression's single
1963
+ * dot_identifier; reusing pathParts is safe since that rule holds exactly one dot_identifier
1964
+ * and no identifier head, so it degenerates to a one-element (dot-split, backtick-stripped) path. */
1965
+ function lowerParameterExpression(node) {
1966
+ const named = firstOfRule(node, P.RULE_named_parameter_expression);
1967
+ if (!named)
1968
+ return { kind: "parameter", text: node.getText(), cst: node };
1969
+ return { kind: "parameter", text: node.getText(), name: pathParts(named).join("."), cst: node };
1970
+ }
1971
+ /** system_variable_expression: ATAT_SYMBOL dot_identifier (DOT_SYMBOL dot_identifier)* — a script-
1972
+ * level system variable (cloud.google.com/bigquery/docs/reference/system-variables), e.g.
1973
+ * `@@dataset_id` or the dotted `@@a.b`. `name` is the dotted path with the `@@` sigil stripped;
1974
+ * pathParts already handles this shape (no identifier head, one-or-more dot_identifier tail). */
1975
+ function lowerSystemVariableExpression(node) {
1976
+ return { kind: "variable", text: node.getText(), name: pathParts(node).join("."), system: true, cst: node };
1977
+ }
1952
1978
  /** path_expression: identifier (DOT dot_identifier)* — the dotted parts. The head is an identifier;
1953
1979
  * later parts are dot_identifier (which may be a reserved keyword after the dot). A single
1954
1980
  * backtick-quoted identifier may itself hold a dotted path (`proj.ds.t`), so split each part on `.`. */
@@ -0,0 +1,2 @@
1
+ import type { KeywordReservation } from "../dialect-symbols.js";
2
+ export declare const BIGQUERY_RESERVED: Record<string, KeywordReservation>;
@@ -0,0 +1,325 @@
1
+ export const BIGQUERY_RESERVED = {
2
+ ABORT: { reserved: false, alias: true, column: true, table: true },
3
+ ACCESS: { reserved: false, alias: true, column: true, table: true },
4
+ ACTION: { reserved: false, alias: true, column: true, table: true },
5
+ ACYCLIC: { reserved: false, alias: true, column: true, table: true },
6
+ ADD: { reserved: false, alias: true, column: true, table: true },
7
+ AFTER: { reserved: false, alias: true, column: true, table: true },
8
+ AGGREGATE: { reserved: false, alias: true, column: true, table: true },
9
+ ALL: { reserved: true, alias: false, column: false, table: false },
10
+ ALTER: { reserved: false, alias: true, column: true, table: true },
11
+ ALWAYS: { reserved: false, alias: true, column: true, table: true },
12
+ ANALYZE: { reserved: false, alias: true, column: true, table: true },
13
+ AND: { reserved: true, alias: false, column: false, table: false },
14
+ ANY: { reserved: true, alias: false, column: false, table: false },
15
+ APPROX: { reserved: false, alias: true, column: true, table: true },
16
+ ARE: { reserved: false, alias: true, column: true, table: true },
17
+ ARRAY: { reserved: true, alias: false, column: false, table: false },
18
+ AS: { reserved: true, alias: false, column: false, table: false },
19
+ ASC: { reserved: true, alias: false, column: false, table: false },
20
+ ASCENDING: { reserved: false, alias: true, column: true, table: true },
21
+ ASSERT: { reserved: false, alias: true, column: true, table: true },
22
+ ASSERT_ROWS_MODIFIED: { reserved: true, alias: false, column: false, table: false },
23
+ AT: { reserved: false, alias: true, column: true, table: true },
24
+ BATCH: { reserved: false, alias: true, column: true, table: true },
25
+ BEGIN: { reserved: false, alias: true, column: true, table: true },
26
+ BETWEEN: { reserved: true, alias: false, column: false, table: false },
27
+ BIGDECIMAL: { reserved: false, alias: true, column: true, table: true },
28
+ BIGNUMERIC: { reserved: false, alias: true, column: true, table: true },
29
+ BREAK: { reserved: false, alias: true, column: true, table: true },
30
+ BY: { reserved: true, alias: false, column: false, table: false },
31
+ CALL: { reserved: false, alias: true, column: true, table: true },
32
+ CASCADE: { reserved: false, alias: true, column: true, table: true },
33
+ CASE: { reserved: true, alias: false, column: false, table: false },
34
+ CAST: { reserved: true, alias: false, column: false, table: false },
35
+ CHEAPEST: { reserved: false, alias: true, column: true, table: true },
36
+ CHECK: { reserved: false, alias: true, column: true, table: true },
37
+ CLAMPED: { reserved: false, alias: true, column: true, table: true },
38
+ CLONE: { reserved: false, alias: true, column: true, table: true },
39
+ CLUSTER: { reserved: false, alias: true, column: true, table: true },
40
+ COLLATE: { reserved: true, alias: false, column: false, table: false },
41
+ COLUMN: { reserved: false, alias: true, column: true, table: true },
42
+ COLUMNS: { reserved: false, alias: true, column: true, table: true },
43
+ COMMIT: { reserved: false, alias: true, column: true, table: true },
44
+ CONFLICT: { reserved: false, alias: true, column: true, table: true },
45
+ CONNECTION: { reserved: false, alias: true, column: true, table: true },
46
+ CONSTANT: { reserved: false, alias: true, column: true, table: true },
47
+ CONSTRAINT: { reserved: false, alias: true, column: true, table: true },
48
+ CONTINUE: { reserved: false, alias: true, column: true, table: true },
49
+ COPY: { reserved: false, alias: true, column: true, table: true },
50
+ CORRESPONDING: { reserved: false, alias: true, column: true, table: true },
51
+ COST: { reserved: false, alias: true, column: true, table: true },
52
+ CREATE: { reserved: true, alias: false, column: false, table: false },
53
+ CROSS: { reserved: true, alias: false, column: false, table: false },
54
+ CUBE: { reserved: true, alias: false, column: false, table: false },
55
+ CURRENT: { reserved: true, alias: false, column: false, table: false },
56
+ CYCLE: { reserved: false, alias: true, column: true, table: true },
57
+ DATA: { reserved: false, alias: true, column: true, table: true },
58
+ DATABASE: { reserved: false, alias: true, column: true, table: true },
59
+ DATE: { reserved: false, alias: true, column: true, table: true },
60
+ DATETIME: { reserved: false, alias: true, column: true, table: true },
61
+ DECIMAL: { reserved: false, alias: true, column: true, table: true },
62
+ DECLARE: { reserved: false, alias: true, column: true, table: true },
63
+ DEFAULT: { reserved: true, alias: false, column: false, table: false },
64
+ DEFINE: { reserved: true, alias: false, column: false, table: false },
65
+ DEFINER: { reserved: false, alias: true, column: true, table: true },
66
+ DELETE: { reserved: false, alias: true, column: true, table: true },
67
+ DELETION: { reserved: false, alias: true, column: true, table: true },
68
+ DELTA: { reserved: false, alias: true, column: true, table: true },
69
+ DEPTH: { reserved: false, alias: true, column: true, table: true },
70
+ DESC: { reserved: true, alias: false, column: false, table: false },
71
+ DESCENDING: { reserved: false, alias: true, column: true, table: true },
72
+ DESCRIBE: { reserved: false, alias: true, column: true, table: true },
73
+ DESCRIPTOR: { reserved: false, alias: true, column: true, table: true },
74
+ DESTINATION: { reserved: false, alias: true, column: true, table: true },
75
+ DETERMINISTIC: { reserved: false, alias: true, column: true, table: true },
76
+ DIFFERENTIAL_PRIVACY: { reserved: false, alias: true, column: true, table: true },
77
+ DISTINCT: { reserved: true, alias: false, column: false, table: false },
78
+ DO: { reserved: false, alias: true, column: true, table: true },
79
+ DROP: { reserved: false, alias: true, column: true, table: true },
80
+ DYNAMIC: { reserved: false, alias: true, column: true, table: true },
81
+ EDGE: { reserved: false, alias: true, column: true, table: true },
82
+ ELSE: { reserved: true, alias: false, column: false, table: false },
83
+ ELSEIF: { reserved: false, alias: true, column: true, table: true },
84
+ END: { reserved: true, alias: false, column: false, table: false },
85
+ ENFORCED: { reserved: false, alias: true, column: true, table: true },
86
+ ENUM: { reserved: true, alias: false, column: false, table: false },
87
+ EPSILON: { reserved: false, alias: true, column: true, table: true },
88
+ ERROR: { reserved: false, alias: true, column: true, table: true },
89
+ EXCEPT: { reserved: true, alias: false, column: false, table: false },
90
+ EXCEPTION: { reserved: false, alias: true, column: true, table: true },
91
+ EXCLUDE: { reserved: true, alias: false, column: false, table: false },
92
+ EXECUTE: { reserved: false, alias: true, column: true, table: true },
93
+ EXISTS: { reserved: true, alias: false, column: false, table: false },
94
+ EXPLAIN: { reserved: false, alias: true, column: true, table: true },
95
+ EXPORT: { reserved: false, alias: true, column: true, table: true },
96
+ EXTEND: { reserved: false, alias: true, column: true, table: true },
97
+ EXTERNAL: { reserved: false, alias: true, column: true, table: true },
98
+ EXTRACT: { reserved: true, alias: false, column: false, table: false },
99
+ FALSE: { reserved: false, alias: false, column: true, table: false },
100
+ FILES: { reserved: false, alias: true, column: true, table: true },
101
+ FILL: { reserved: false, alias: true, column: true, table: true },
102
+ FILTER: { reserved: false, alias: true, column: true, table: true },
103
+ FIRST: { reserved: false, alias: true, column: true, table: true },
104
+ FOLLOWING: { reserved: true, alias: false, column: false, table: false },
105
+ FOR: { reserved: true, alias: false, column: false, table: false },
106
+ FOREIGN: { reserved: false, alias: true, column: true, table: true },
107
+ FORK: { reserved: false, alias: true, column: true, table: true },
108
+ FORMAT: { reserved: false, alias: true, column: true, table: true },
109
+ FROM: { reserved: true, alias: false, column: false, table: false },
110
+ FULL: { reserved: true, alias: false, column: false, table: false },
111
+ FUNCTION: { reserved: false, alias: true, column: true, table: true },
112
+ GENERATED: { reserved: false, alias: true, column: true, table: true },
113
+ GRANT: { reserved: false, alias: true, column: true, table: true },
114
+ GRAPH: { reserved: false, alias: true, column: true, table: true },
115
+ GRAPH_TABLE: { reserved: false, alias: true, column: true, table: true },
116
+ GROUP: { reserved: true, alias: false, column: false, table: false },
117
+ GROUPING: { reserved: true, alias: false, column: false, table: false },
118
+ GROUP_ROWS: { reserved: false, alias: true, column: true, table: true },
119
+ HASH: { reserved: true, alias: false, column: false, table: false },
120
+ HAVING: { reserved: true, alias: false, column: false, table: false },
121
+ HIDDEN: { reserved: false, alias: true, column: true, table: true },
122
+ IDENTITY: { reserved: false, alias: true, column: true, table: true },
123
+ IF: { reserved: true, alias: false, column: false, table: false },
124
+ IGNORE: { reserved: true, alias: false, column: false, table: false },
125
+ IMMEDIATE: { reserved: false, alias: true, column: true, table: true },
126
+ IMMUTABLE: { reserved: false, alias: true, column: true, table: true },
127
+ IMPORT: { reserved: false, alias: true, column: true, table: true },
128
+ IN: { reserved: true, alias: false, column: false, table: false },
129
+ INCLUDE: { reserved: false, alias: true, column: true, table: true },
130
+ INCREMENT: { reserved: false, alias: true, column: true, table: true },
131
+ INDEX: { reserved: false, alias: true, column: true, table: true },
132
+ INNER: { reserved: true, alias: false, column: false, table: false },
133
+ INOUT: { reserved: false, alias: true, column: true, table: true },
134
+ INPUT: { reserved: false, alias: true, column: true, table: true },
135
+ INSERT: { reserved: false, alias: true, column: true, table: true },
136
+ INTERLEAVE: { reserved: false, alias: true, column: true, table: true },
137
+ INTERSECT: { reserved: true, alias: false, column: false, table: false },
138
+ INTERVAL: { reserved: true, alias: false, column: false, table: false },
139
+ INTO: { reserved: true, alias: false, column: false, table: false },
140
+ INVOKER: { reserved: false, alias: true, column: true, table: true },
141
+ IS: { reserved: true, alias: false, column: false, table: false },
142
+ ISOLATION: { reserved: false, alias: true, column: true, table: true },
143
+ ITERATE: { reserved: false, alias: true, column: true, table: true },
144
+ JOIN: { reserved: true, alias: false, column: false, table: false },
145
+ JSON: { reserved: false, alias: true, column: true, table: true },
146
+ KEY: { reserved: false, alias: true, column: true, table: true },
147
+ KW_MATCH_RECOGNIZE_NONRESERVED: { reserved: false, alias: true, column: true, table: true },
148
+ LABEL: { reserved: false, alias: true, column: true, table: true },
149
+ LABELED: { reserved: false, alias: true, column: true, table: true },
150
+ LANGUAGE: { reserved: false, alias: true, column: true, table: true },
151
+ LAST: { reserved: false, alias: true, column: true, table: true },
152
+ LATERAL: { reserved: true, alias: false, column: false, table: false },
153
+ LEAVE: { reserved: false, alias: true, column: true, table: true },
154
+ LEFT: { reserved: true, alias: false, column: false, table: false },
155
+ LET: { reserved: false, alias: true, column: true, table: true },
156
+ LEVEL: { reserved: false, alias: true, column: true, table: true },
157
+ LIKE: { reserved: true, alias: false, column: false, table: false },
158
+ LIMIT: { reserved: true, alias: false, column: false, table: false },
159
+ LOAD: { reserved: false, alias: true, column: true, table: true },
160
+ LOG: { reserved: false, alias: true, column: true, table: true },
161
+ LOOKUP: { reserved: true, alias: false, column: false, table: false },
162
+ LOOP: { reserved: false, alias: true, column: true, table: true },
163
+ MACRO: { reserved: false, alias: true, column: true, table: true },
164
+ MAP: { reserved: false, alias: true, column: true, table: true },
165
+ MATCH: { reserved: false, alias: true, column: true, table: true },
166
+ MATCHED: { reserved: false, alias: true, column: true, table: true },
167
+ MATCH_RECOGNIZE: { reserved: true, alias: false, column: false, table: false },
168
+ MATERIALIZED: { reserved: false, alias: true, column: true, table: true },
169
+ MAX: { reserved: false, alias: true, column: true, table: true },
170
+ MAXVALUE: { reserved: false, alias: true, column: true, table: true },
171
+ MAX_GROUPS_CONTRIBUTED: { reserved: false, alias: true, column: true, table: true },
172
+ MEASURES: { reserved: false, alias: true, column: true, table: true },
173
+ MERGE: { reserved: true, alias: false, column: false, table: false },
174
+ MESSAGE: { reserved: false, alias: true, column: true, table: true },
175
+ METADATA: { reserved: false, alias: true, column: true, table: true },
176
+ MIN: { reserved: false, alias: true, column: true, table: true },
177
+ MINVALUE: { reserved: false, alias: true, column: true, table: true },
178
+ MODEL: { reserved: false, alias: true, column: true, table: true },
179
+ MODULE: { reserved: false, alias: true, column: true, table: true },
180
+ NAME: { reserved: false, alias: true, column: true, table: true },
181
+ NATURAL: { reserved: true, alias: false, column: false, table: false },
182
+ NEW: { reserved: true, alias: false, column: false, table: false },
183
+ NEXT: { reserved: false, alias: true, column: true, table: true },
184
+ NO: { reserved: true, alias: false, column: false, table: false },
185
+ NODE: { reserved: false, alias: true, column: true, table: true },
186
+ NOT: { reserved: true, alias: false, column: false, table: false },
187
+ NOTHING: { reserved: true, alias: false, column: false, table: false },
188
+ NULL: { reserved: false, alias: false, column: true, table: false },
189
+ NULLS: { reserved: true, alias: false, column: false, table: false },
190
+ NULL_FILTERED: { reserved: false, alias: true, column: true, table: true },
191
+ NUMERIC: { reserved: false, alias: true, column: true, table: true },
192
+ OF: { reserved: true, alias: false, column: false, table: false },
193
+ OFFSET: { reserved: false, alias: true, column: true, table: true },
194
+ ON: { reserved: true, alias: false, column: false, table: false },
195
+ ONLY: { reserved: false, alias: true, column: true, table: true },
196
+ OPTIONAL: { reserved: false, alias: true, column: true, table: true },
197
+ OPTIONS: { reserved: false, alias: true, column: true, table: true },
198
+ OR: { reserved: true, alias: false, column: false, table: false },
199
+ ORDER: { reserved: true, alias: false, column: false, table: false },
200
+ OUT: { reserved: false, alias: true, column: true, table: true },
201
+ OUTER: { reserved: true, alias: false, column: false, table: false },
202
+ OUTPUT: { reserved: false, alias: true, column: true, table: true },
203
+ OVER: { reserved: true, alias: false, column: false, table: false },
204
+ OVERWRITE: { reserved: false, alias: true, column: true, table: true },
205
+ PARENT: { reserved: false, alias: true, column: true, table: true },
206
+ PARTITION: { reserved: true, alias: false, column: false, table: false },
207
+ PARTITIONS: { reserved: false, alias: true, column: true, table: true },
208
+ PAST: { reserved: false, alias: true, column: true, table: true },
209
+ PATH: { reserved: false, alias: true, column: true, table: true },
210
+ PATHS: { reserved: false, alias: true, column: true, table: true },
211
+ PATTERN: { reserved: false, alias: true, column: true, table: true },
212
+ PER: { reserved: false, alias: true, column: true, table: true },
213
+ PERCENT: { reserved: false, alias: true, column: true, table: true },
214
+ PIVOT: { reserved: false, alias: true, column: true, table: true },
215
+ POLICIES: { reserved: false, alias: true, column: true, table: true },
216
+ POLICY: { reserved: false, alias: true, column: true, table: true },
217
+ PRECEDING: { reserved: true, alias: false, column: false, table: false },
218
+ PRIMARY: { reserved: false, alias: true, column: true, table: true },
219
+ PRIVACY_UNIT_COLUMN: { reserved: false, alias: true, column: true, table: true },
220
+ PRIVATE: { reserved: false, alias: true, column: true, table: true },
221
+ PRIVILEGE: { reserved: false, alias: true, column: true, table: true },
222
+ PRIVILEGES: { reserved: false, alias: true, column: true, table: true },
223
+ PROCEDURE: { reserved: false, alias: true, column: true, table: true },
224
+ PROJECT: { reserved: false, alias: true, column: true, table: true },
225
+ PROPERTIES: { reserved: false, alias: true, column: true, table: true },
226
+ PROPERTY: { reserved: false, alias: true, column: true, table: true },
227
+ PROTO: { reserved: true, alias: false, column: false, table: false },
228
+ PUBLIC: { reserved: false, alias: true, column: true, table: true },
229
+ QUALIFY: { reserved: true, alias: false, column: false, table: false },
230
+ RAISE: { reserved: false, alias: true, column: true, table: true },
231
+ RANGE: { reserved: true, alias: false, column: false, table: false },
232
+ READ: { reserved: false, alias: true, column: true, table: true },
233
+ RECURSIVE: { reserved: true, alias: false, column: false, table: false },
234
+ REFERENCES: { reserved: false, alias: true, column: true, table: true },
235
+ REMOTE: { reserved: false, alias: true, column: true, table: true },
236
+ REMOVE: { reserved: false, alias: true, column: true, table: true },
237
+ RENAME: { reserved: false, alias: true, column: true, table: true },
238
+ REPEAT: { reserved: false, alias: true, column: true, table: true },
239
+ REPEATABLE: { reserved: false, alias: true, column: true, table: true },
240
+ REPLACE: { reserved: false, alias: true, column: true, table: true },
241
+ REPLACE_FIELDS: { reserved: false, alias: true, column: true, table: true },
242
+ REPLICA: { reserved: false, alias: true, column: true, table: true },
243
+ REPORT: { reserved: false, alias: true, column: true, table: true },
244
+ RESPECT: { reserved: true, alias: false, column: false, table: false },
245
+ RESTRICT: { reserved: false, alias: true, column: true, table: true },
246
+ RESTRICTION: { reserved: false, alias: true, column: true, table: true },
247
+ RETURN: { reserved: false, alias: true, column: true, table: true },
248
+ RETURNS: { reserved: false, alias: true, column: true, table: true },
249
+ REVOKE: { reserved: false, alias: true, column: true, table: true },
250
+ RIGHT: { reserved: true, alias: false, column: false, table: false },
251
+ ROLLBACK: { reserved: false, alias: true, column: true, table: true },
252
+ ROLLUP: { reserved: true, alias: false, column: false, table: false },
253
+ ROW: { reserved: false, alias: true, column: true, table: true },
254
+ ROWS: { reserved: true, alias: false, column: false, table: false },
255
+ RUN: { reserved: false, alias: true, column: true, table: true },
256
+ SAFE_CAST: { reserved: false, alias: true, column: true, table: true },
257
+ SCHEMA: { reserved: false, alias: true, column: true, table: true },
258
+ SEARCH: { reserved: false, alias: true, column: true, table: true },
259
+ SECURITY: { reserved: false, alias: true, column: true, table: true },
260
+ SELECT: { reserved: true, alias: false, column: false, table: false },
261
+ SEQUENCE: { reserved: false, alias: true, column: true, table: true },
262
+ SET: { reserved: true, alias: false, column: false, table: false },
263
+ SETS: { reserved: false, alias: true, column: true, table: true },
264
+ SHORTEST: { reserved: true, alias: false, column: false, table: false },
265
+ SHOW: { reserved: false, alias: true, column: true, table: true },
266
+ SIMPLE: { reserved: false, alias: true, column: true, table: true },
267
+ SKIP: { reserved: false, alias: true, column: true, table: true },
268
+ SLASH: { reserved: true, alias: false, column: false, table: false },
269
+ SNAPSHOT: { reserved: false, alias: true, column: true, table: true },
270
+ SOME: { reserved: true, alias: false, column: false, table: false },
271
+ SOURCE: { reserved: false, alias: true, column: true, table: true },
272
+ SQL: { reserved: false, alias: true, column: true, table: true },
273
+ STABLE: { reserved: false, alias: true, column: true, table: true },
274
+ START: { reserved: false, alias: true, column: true, table: true },
275
+ STATIC_DESCRIBE: { reserved: false, alias: true, column: true, table: true },
276
+ STORED: { reserved: false, alias: true, column: true, table: true },
277
+ STORING: { reserved: false, alias: true, column: true, table: true },
278
+ STRICT: { reserved: false, alias: true, column: true, table: true },
279
+ STRUCT: { reserved: true, alias: false, column: false, table: false },
280
+ SYSTEM: { reserved: false, alias: true, column: true, table: true },
281
+ SYSTEM_TIME: { reserved: false, alias: true, column: true, table: true },
282
+ TABLE: { reserved: false, alias: true, column: true, table: true },
283
+ TABLES: { reserved: false, alias: true, column: true, table: true },
284
+ TABLESAMPLE: { reserved: true, alias: false, column: false, table: false },
285
+ TARGET: { reserved: false, alias: true, column: true, table: true },
286
+ TEE: { reserved: false, alias: true, column: true, table: true },
287
+ TEMP: { reserved: false, alias: true, column: true, table: true },
288
+ TEMPORARY: { reserved: false, alias: true, column: true, table: true },
289
+ THEN: { reserved: true, alias: false, column: false, table: false },
290
+ TIME: { reserved: false, alias: true, column: true, table: true },
291
+ TIMESTAMP: { reserved: false, alias: true, column: true, table: true },
292
+ TO: { reserved: true, alias: false, column: false, table: false },
293
+ TRAIL: { reserved: false, alias: true, column: true, table: true },
294
+ TRANSACTION: { reserved: false, alias: true, column: true, table: true },
295
+ TRANSFORM: { reserved: false, alias: true, column: true, table: true },
296
+ TRUE: { reserved: false, alias: false, column: true, table: false },
297
+ TRUNCATE: { reserved: false, alias: true, column: true, table: true },
298
+ TYPE: { reserved: false, alias: true, column: true, table: true },
299
+ UNBOUNDED: { reserved: true, alias: false, column: false, table: false },
300
+ UNDROP: { reserved: false, alias: true, column: true, table: true },
301
+ UNION: { reserved: true, alias: false, column: false, table: false },
302
+ UNIQUE: { reserved: false, alias: true, column: true, table: true },
303
+ UNKNOWN: { reserved: false, alias: true, column: true, table: true },
304
+ UNNEST: { reserved: true, alias: false, column: false, table: false },
305
+ UNPIVOT: { reserved: false, alias: true, column: true, table: true },
306
+ UNTIL: { reserved: false, alias: true, column: true, table: true },
307
+ UPDATE: { reserved: false, alias: true, column: true, table: true },
308
+ USING: { reserved: true, alias: false, column: false, table: false },
309
+ VALUE: { reserved: false, alias: true, column: true, table: true },
310
+ VALUES: { reserved: false, alias: true, column: true, table: true },
311
+ VECTOR: { reserved: false, alias: true, column: true, table: true },
312
+ VIEW: { reserved: false, alias: true, column: true, table: true },
313
+ VIEWS: { reserved: false, alias: true, column: true, table: true },
314
+ VOLATILE: { reserved: false, alias: true, column: true, table: true },
315
+ WALK: { reserved: false, alias: true, column: true, table: true },
316
+ WEIGHT: { reserved: false, alias: true, column: true, table: true },
317
+ WHEN: { reserved: true, alias: false, column: false, table: false },
318
+ WHERE: { reserved: true, alias: false, column: false, table: false },
319
+ WHILE: { reserved: false, alias: true, column: true, table: true },
320
+ WINDOW: { reserved: true, alias: false, column: false, table: false },
321
+ WITH: { reserved: true, alias: false, column: false, table: false },
322
+ WRITE: { reserved: false, alias: true, column: true, table: true },
323
+ YIELD: { reserved: false, alias: true, column: true, table: true },
324
+ ZONE: { reserved: false, alias: true, column: true, table: true },
325
+ };
@@ -1,19 +1,107 @@
1
1
  import type { SqlDocument } from "../document/document.js";
2
+ import { type PartSpan } from "../ir/part-span.js";
2
3
  import type { SchemaProvider } from "../qualify/schema-provider.js";
3
- /** One completion candidate. The editor filters this list by the typed prefix and applies the
4
- * chosen label at the caret; we only produce the labels, anchored at the caret offset. The
5
- * `"template"` kind is a host candidate for a jinja call slot (a dbt model for a ref's arg). */
4
+ import { type TemplateCall } from "../qualify/template-provider.js";
5
+ import { type ResolvedSource } from "../scope/scope.js";
6
+ /** One completion candidate, already pruned to the typed prefix (2026-07-12 ruling) and applied at
7
+ * the caret / `CompletionResult.replaceRange`. The `"template"` kind is a host candidate for a
8
+ * jinja call slot (a dbt model for a ref's arg) — its own, separately-decided contract (the
9
+ * consumer still filters those by the typed prefix; see complete.jinja-candidates.test.ts). */
6
10
  export interface Completion {
7
11
  label: string;
8
12
  kind: "keyword" | "column" | "table" | "cte" | "namespace" | "function" | "template";
9
13
  /** Extra display info, e.g. a column's type when the schema knows it. */
10
14
  detail?: string;
15
+ /** Long-form documentation for the candidate. completeAt's own resolution never fills this in —
16
+ * it is set ONLY by a `decorate` hook (CompleteOptions.decorate) answering one. Absent when no
17
+ * hook ran, or the hook answered nothing for this candidate. */
18
+ documentation?: string;
19
+ }
20
+ /** The caret-anchored span of the partial identifier/keyword the candidates were pruned against —
21
+ * `text.slice(start, end)` is what's already typed. `start` includes an opening delimiter when the
22
+ * caret sits inside a quoted/bracketed/backtick-quoted identifier (`"my_t`, `` `my_t ``, `[my_t`),
23
+ * so an editor that replaces this span never leaves a stray leading quote. `end` never extends past
24
+ * the caret (`offset`) — even where a dialect's lexer greedily swallows an unterminated quoted
25
+ * identifier past the caret, only the already-typed portion is ever reported or matched. */
26
+ export interface ReplaceRange {
27
+ start: number;
28
+ end: number;
29
+ }
30
+ /** completeAt()'s result: an ordinary `Completion[]` (`.map`/`.filter`/iteration/`.length` all work
31
+ * exactly as before — every existing consumer sees no change) carrying one optional extra
32
+ * property, the same "array with named extras" shape TypeScript's own `RegExpMatchArray` uses for
33
+ * `String.prototype.match`. `replaceRange` is present only when the caret sits inside a partially
34
+ * typed word; an empty-prefix caret (a token boundary — nothing typed yet) returns a plain array
35
+ * with no `replaceRange`, byte-identical to the pre-pruning contract. */
36
+ export interface CompletionResult extends Array<Completion> {
37
+ replaceRange?: ReplaceRange;
38
+ }
39
+ /** The decoration hook's answer for ONE candidate — display text supplied from real structure,
40
+ * merged onto the Completion completeAt is about to return. Every field optional: an absent field
41
+ * leaves that part of the candidate as completeAt itself produced it (a schema-fed column's own
42
+ * `detail` survives unless the hook overrides it); returning nothing at all leaves the candidate
43
+ * wholly undecorated. */
44
+ export interface CandidateDecoration {
45
+ detail?: string;
46
+ documentation?: string;
47
+ }
48
+ /**
49
+ * The STRUCTURAL identity of a candidate completeAt is about to return — anvil's decoration-hook ask
50
+ * (channel, 2026-07-20): "hand back STRUCTURAL identity, not just the label string". Discriminated by
51
+ * the candidate's own `kind`. Every extra field is present ONLY when completeAt's own resolution
52
+ * already produced it (never-wrong: nothing here is synthesized or re-derived) — e.g. a "column"
53
+ * candidate from the broken-input FROM/JOIN token-stream fallback (fromRelationColumns /
54
+ * qualifiedFallbackColumns) carries no `source`, because no ResolvedSource exists on that path.
55
+ *
56
+ * `"template"` covers the jinja call-slot candidates (a dbt model name for a `ref('|` arg, a source
57
+ * name for `source('|`) — the "table candidate that resolves through a templated call" from the
58
+ * ask: its `call` is the SAME TemplateCall (`JinjaSlot.call`) `templateCandidates` was already asked
59
+ * with, so a consumer names the model/source without re-parsing the call text.
60
+ */
61
+ export type CandidateIdentity = {
62
+ kind: "keyword";
63
+ } | {
64
+ kind: "function";
65
+ } | {
66
+ kind: "namespace";
67
+ } | {
68
+ kind: "table";
69
+ } | {
70
+ kind: "cte";
71
+ /** The CTE name's own declaration span (`CteDef.nameCst`, falling back to the whole
72
+ * `CteDef.cst` when the name has no real token) — pins the RIGHT declaration even when
73
+ * another CTE of the same name shadows it in a nested scope. Absent only when the CTE
74
+ * itself has no real token to key on (a broken/nameless mid-edit CTE). */
75
+ declarationSpan?: PartSpan;
76
+ } | {
77
+ kind: "column";
78
+ /** The resolved scope source this column came from (the same ResolvedSource
79
+ * scope/qualify already produced — a table/CTE/subquery/lateral/relation/graphtable/pivot).
80
+ * Absent when the column came from the broken-input token-stream fallback, which has no
81
+ * ResolvedSource to carry. */
82
+ source?: ResolvedSource;
83
+ } | {
84
+ kind: "template";
85
+ call: TemplateCall;
86
+ };
87
+ /** Per-candidate decoration hook (`CompleteOptions.decorate`): completeAt calls this once for each
88
+ * candidate it is about to return, with the candidate as built so far and its structural identity,
89
+ * and merges the answer's `detail`/`documentation` onto it. The candidate SET is unaffected — this
90
+ * only supplies display text, never adds/removes/reorders a candidate. Total-safe: a throwing hook
91
+ * degrades to the undecorated candidate (never breaks completeAt), the same total-by-contract
92
+ * posture every other completeAt internal failure gets (SQLLENS_DEBUG=1 rethrows — src/debug.ts). */
93
+ export type DecorateCandidate = (candidate: Completion, identity: CandidateIdentity) => CandidateDecoration | undefined | void;
94
+ /** completeAt's options — currently just the decoration hook. Additive: every existing 3-arg call
95
+ * site keeps compiling and behaving byte-identically (no `opts`, no decoration). */
96
+ export interface CompleteOptions {
97
+ decorate?: DecorateCandidate;
11
98
  }
12
99
  /**
13
- * Completion candidates for the caret at `offset` in `doc`. Schema-aware when a `Schema` is given
14
- * (table names + column types). NEVER throws: on broken / mid-edit input it still returns the
15
- * keyword candidates the walk can reach.
100
+ * Completion candidates for the caret at `offset` in `doc`, pruned to the identifier/keyword
101
+ * fragment already typed there (case-insensitive, dialect-fold-aware; plain prefix match never
102
+ * fuzzy). Schema-aware when a `Schema` is given (table names + column types). NEVER throws: on
103
+ * broken / mid-edit input it still returns the keyword candidates the walk can reach.
16
104
  */
17
- export declare function completeAt(doc: SqlDocument, offset: number, schema?: SchemaProvider): Completion[];
105
+ export declare function completeAt(doc: SqlDocument, offset: number, schema?: SchemaProvider, opts?: CompleteOptions): CompletionResult;
18
106
  /** @deprecated Use completeAt — same function, uniform cursor-verb naming. */
19
107
  export declare const complete: typeof completeAt;