@coldsmirk/inkstone-sql 0.13.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.
- package/README.md +68 -0
- package/dist/assist-gLTZKSD3.d.ts +401 -0
- package/dist/index.d.ts +380 -0
- package/dist/index.js +1531 -0
- package/dist/monaco.d.ts +31 -0
- package/dist/monaco.js +128 -0
- package/dist/react.d.ts +158 -0
- package/dist/react.js +135 -0
- package/package.json +81 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# @coldsmirk/inkstone-sql
|
|
2
|
+
|
|
3
|
+
Schema-aware SQL editing intelligence for Monaco: lexical statement reading under per-dialect noise profiles, catalog-driven completion / hover / signature help, unified-placeholder awareness, `sql-formatter` integration, and a drop-in React `<SqlEditor>`.
|
|
4
|
+
|
|
5
|
+
Part of [inkstone](https://github.com/coldsmirk/inkstone). Extracted from dragoman's SQL console; built for the [**polyglot**](https://github.com/coldsmirk/polyglot) unified-placeholder surface — the SQL its scanner rebinds (`?` positional / `:name` named, `??` and `::` escapes) across `postgres` / `mysql` / `oracle` / `sqlserver` / `dm` (KingbaseES rides `postgres`, OceanBase's MySQL mode rides `mysql`).
|
|
6
|
+
|
|
7
|
+
## Entries
|
|
8
|
+
|
|
9
|
+
- **`@coldsmirk/inkstone-sql`** — the framework-free core. Zero editor dependencies; runs anywhere.
|
|
10
|
+
- **`@coldsmirk/inkstone-sql/monaco`** — `registerSqlLanguage(monaco, modelPath, hooks)`: the Monaco providers (completion, hover, signature help, formatting) over an assist read per-request through a hook. Peer: `monaco-editor`.
|
|
11
|
+
- **`@coldsmirk/inkstone-sql/react`** — `<SqlEditor>`: a controlled SQL editor built on `@coldsmirk/inkstone-react`'s `<MonacoEditor>`, with a run key (Ctrl/Cmd-Enter handing over the live caret), a runnable-statement gutter mark, caret tracking, and an insert/format handle for schema trees and toolbars. Peers: `monaco-editor`, `react`.
|
|
12
|
+
|
|
13
|
+
## The shape
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createSqlAssist, readStatement, runnableStatement, statementMarkers } from "@coldsmirk/inkstone-sql";
|
|
17
|
+
|
|
18
|
+
// The catalog is YOURS to fetch — and it MUST be a cache-backed read (a query cache, a memo
|
|
19
|
+
// your schema-refresh invalidates), never a fresh fetch: completion reads it on every
|
|
20
|
+
// keystroke, columns once per referenced table, and the package deliberately holds no cache of
|
|
21
|
+
// its own.
|
|
22
|
+
const assist = createSqlAssist("postgres", {
|
|
23
|
+
tables: () => cachedTables(), // CatalogTable[]
|
|
24
|
+
columns: (schema, table) => cachedColumns(schema, table), // CatalogColumn[]
|
|
25
|
+
functions: () => cachedFunctions() // CatalogFunction[]
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// What one run should send: the selection where there is one, else the caret's statement.
|
|
29
|
+
const runnable = runnableStatement(text, from, to, "postgres");
|
|
30
|
+
|
|
31
|
+
if (runnable.kind === "one") {
|
|
32
|
+
// Markers are read off the BLANKED form — `select '?'` binds nothing — and under the kind's
|
|
33
|
+
// own binding rules: `markers.refusal` mirrors the engine scanner's own faults (mixed forms,
|
|
34
|
+
// `??` on mysql/dm, a hand-written `$1` beside a minted marker), so the editor can say so
|
|
35
|
+
// before the round trip does.
|
|
36
|
+
const markers = statementMarkers(runnable.blanked, "postgres");
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Everything the intelligence answers goes through one lexical reader taken under the connection kind's own noise profile, so completion, hover, signature help and the parameter grid can never disagree about what is prose and what is SQL.
|
|
41
|
+
|
|
42
|
+
## The mirror invariant
|
|
43
|
+
|
|
44
|
+
`noise.ts` (which runs of a statement are strings / comments / quoted names, per kind) and the marker grammar mirror [**polyglot's engine scanner**](https://github.com/coldsmirk/polyglot) (`internal/scan`) flag for flag. The scanner decides what actually binds; a reader that diverged would prompt for a parameter the engine refuses, or send a statement short of one it binds. **A scanner change requires a matching release of this package**, and the per-kind cases in `reader.test.ts` are the invariant's guard.
|
|
45
|
+
|
|
46
|
+
## Structured refusals
|
|
47
|
+
|
|
48
|
+
Every refusal and every status is a **code**, never a sentence: a parameter that cannot bind comes back as `{ ok: false, form, key, refusal }` with `refusal` one of `"empty" | "not-a-boolean" | "not-a-number" | "not-finite" | "unsafe-integer"`; a statement the engine's scanner would fault carries `StatementMarkers.refusal` (`"mixed-forms" | "question-escape-unsupported" | "native-marker-collision"`); a multi-statement selection reads as `{ kind: "several" }`; a join suggestion's rationale is `JoinReason` (`"named-foreign-key" | "shared-key"`). The consuming app owns the wording and the language for all of them. The one deliberate exception is reference prose: hover cards and the curated function summaries read as English documentation, the way a vendor's manual does.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pnpm add @coldsmirk/inkstone-sql monaco-editor
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Every peer is optional, because the root entry needs none of them: `/monaco` needs `monaco-editor`, and `/react` additionally needs `react` (>= 19) and `@coldsmirk/inkstone-react`. Never import `monaco-editor` statically — take the module from `onMount` / `ensureMonacoHost()`; type-only imports erase and are fine.
|
|
57
|
+
|
|
58
|
+
## Styling the gutter mark
|
|
59
|
+
|
|
60
|
+
`<SqlEditor highlight={span}>` marks the runnable statement with a line decoration classed `inkstone-sql-runnable` (override via `highlightClassName`). The host owns the stylesheet:
|
|
61
|
+
|
|
62
|
+
```css
|
|
63
|
+
.inkstone-sql-runnable {
|
|
64
|
+
width: 3px !important;
|
|
65
|
+
margin-left: 3px;
|
|
66
|
+
background: var(--your-accent);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
//#region src/catalog.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The vocabulary the whole package speaks: which database dialect a surface is editing for, and
|
|
4
|
+
* the catalog shapes every assist reads. The host owns how these are fetched (an HTTP endpoint, an
|
|
5
|
+
* IPC command, a fixture) — the package only ever *reads* them through {@link SqlCatalog}.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* One supported database dialect. A kind is added when a kind-specific *behavior* exists, not per
|
|
9
|
+
* brand name: KingbaseES connects and completes as `postgres`, an OceanBase MySQL deployment as
|
|
10
|
+
* `mysql`, and 达梦 (`dm`) carries Oracle's idiom wherever it documents Oracle compatibility.
|
|
11
|
+
*/
|
|
12
|
+
type SqlKind = "postgres" | "mysql" | "oracle" | "sqlserver" | "dm";
|
|
13
|
+
/**
|
|
14
|
+
* One table or view as the catalog reports it. Names carry the vendor's own casing — that casing
|
|
15
|
+
* is information, and everything downstream preserves it.
|
|
16
|
+
*/
|
|
17
|
+
interface CatalogTable {
|
|
18
|
+
/**
|
|
19
|
+
* Empty where the kind reports no schema (or the object sits in the session's own).
|
|
20
|
+
*/
|
|
21
|
+
schema: string;
|
|
22
|
+
name: string;
|
|
23
|
+
/**
|
|
24
|
+
* The wire's own vocabulary — `"table"` or `"view"` today, and append-only by contract, so
|
|
25
|
+
* the type stays open: a kind added on the wire must not fail to parse here. Everything
|
|
26
|
+
* downstream treats an unknown kind as a table.
|
|
27
|
+
*/
|
|
28
|
+
kind: "table" | "view" | (string & {});
|
|
29
|
+
/**
|
|
30
|
+
* The object's own catalog comment; empty when none is declared.
|
|
31
|
+
*/
|
|
32
|
+
comment: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* One column as the catalog reports it. The key flags overlap by construction (a primary key is
|
|
36
|
+
* also unique and indexed); surfaces choose how much of that to show.
|
|
37
|
+
*/
|
|
38
|
+
interface CatalogColumn {
|
|
39
|
+
name: string;
|
|
40
|
+
dataType: string;
|
|
41
|
+
nullable: boolean;
|
|
42
|
+
primaryKey: boolean;
|
|
43
|
+
foreignKey: boolean;
|
|
44
|
+
unique: boolean;
|
|
45
|
+
indexed: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* The column's own catalog comment; empty when none is declared.
|
|
48
|
+
*/
|
|
49
|
+
comment: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* One function as the catalog reports it — the estate's own routines and, where the kind's
|
|
53
|
+
* catalog describes them, the vendor's built-ins.
|
|
54
|
+
*/
|
|
55
|
+
interface CatalogFunction {
|
|
56
|
+
/**
|
|
57
|
+
* The owner or package a call must qualify with; empty where the kind reports none.
|
|
58
|
+
*/
|
|
59
|
+
schema: string;
|
|
60
|
+
name: string;
|
|
61
|
+
/**
|
|
62
|
+
* The call as the kind spells it — what signature help parses.
|
|
63
|
+
*/
|
|
64
|
+
signature: string;
|
|
65
|
+
/**
|
|
66
|
+
* The return type; empty where the kind's catalog does not report one.
|
|
67
|
+
*/
|
|
68
|
+
returns: string;
|
|
69
|
+
/**
|
|
70
|
+
* The function's own description; empty when none is declared.
|
|
71
|
+
*/
|
|
72
|
+
comment: string;
|
|
73
|
+
builtin: boolean;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/reader.d.ts
|
|
77
|
+
/**
|
|
78
|
+
* One table the statement names: `schema.table alias` with any part optional. A three-part T-SQL
|
|
79
|
+
* name keeps its last two — the catalog speaks schema and table.
|
|
80
|
+
*/
|
|
81
|
+
interface TableRef {
|
|
82
|
+
schema: string;
|
|
83
|
+
table: string;
|
|
84
|
+
alias: string;
|
|
85
|
+
/**
|
|
86
|
+
* The alias exactly as the statement wrote it — delimiters and all — or `""` where the ref
|
|
87
|
+
* declares none. Generated SQL must qualify by this text, never by re-spelling `alias`:
|
|
88
|
+
* unquoting is lossy (`"u"` and `u` both read `u`, and only one of them is what an Oracle
|
|
89
|
+
* statement means), so the operator's own text is the one spelling guaranteed to re-parse as
|
|
90
|
+
* the correlation they declared.
|
|
91
|
+
*/
|
|
92
|
+
aliasWritten: string;
|
|
93
|
+
/**
|
|
94
|
+
* The clause word that introduced it, which is what tells an `insert` target from a `join`.
|
|
95
|
+
*/
|
|
96
|
+
anchor: string;
|
|
97
|
+
/**
|
|
98
|
+
* Character offsets of the name itself, alias excluded — the reader orders refs by them.
|
|
99
|
+
*/
|
|
100
|
+
from: number;
|
|
101
|
+
to: number;
|
|
102
|
+
}
|
|
103
|
+
type StatementContext = {
|
|
104
|
+
kind: "none";
|
|
105
|
+
} | {
|
|
106
|
+
kind: "member";
|
|
107
|
+
parts: string[];
|
|
108
|
+
} | {
|
|
109
|
+
kind: "table";
|
|
110
|
+
} | {
|
|
111
|
+
kind: "insertColumns";
|
|
112
|
+
into: TableRef;
|
|
113
|
+
} | {
|
|
114
|
+
kind: "joinCondition";
|
|
115
|
+
joined: TableRef;
|
|
116
|
+
earlier: TableRef[];
|
|
117
|
+
} | {
|
|
118
|
+
kind: "expression";
|
|
119
|
+
};
|
|
120
|
+
interface StatementRead {
|
|
121
|
+
context: StatementContext;
|
|
122
|
+
/**
|
|
123
|
+
* Every table the statement names — the expression context needs them all, and an alias only
|
|
124
|
+
* resolves against this list.
|
|
125
|
+
*/
|
|
126
|
+
refs: TableRef[];
|
|
127
|
+
/**
|
|
128
|
+
* The partial word at the caret. Monaco filters on it; the engine reads it only to decide the
|
|
129
|
+
* case its own vocabulary answers in.
|
|
130
|
+
*/
|
|
131
|
+
typed: string;
|
|
132
|
+
/**
|
|
133
|
+
* The token standing immediately before the partial word, `""` at the start of a statement —
|
|
134
|
+
* what says whether a select-list item can begin here at all.
|
|
135
|
+
*/
|
|
136
|
+
previous: string;
|
|
137
|
+
/**
|
|
138
|
+
* The caret sits directly on a `*`, which the column-list expansion stands in for. A `*` with
|
|
139
|
+
* whitespace between it and the caret is not one: the expansion would then trail the star
|
|
140
|
+
* instead of replacing it, and `select * id, name` does not run.
|
|
141
|
+
*/
|
|
142
|
+
onStar: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* The clause the caret is writing in — the nearest clause head to its left, `""` at the start
|
|
145
|
+
* of a statement. `and` / `or` do not head a clause, so `on a = b and |` still reads `on`.
|
|
146
|
+
*/
|
|
147
|
+
clause: string;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* What the caret is asking for, and everything the answer needs. `null` means the caret sits
|
|
151
|
+
* inside a string or a comment, where the bench has nothing to say.
|
|
152
|
+
*/
|
|
153
|
+
declare function readStatement(text: string, cursor: number, kind: SqlKind): StatementRead | null;
|
|
154
|
+
/**
|
|
155
|
+
* What one run sends. The endpoint takes exactly one statement, so this is where
|
|
156
|
+
* a scratchpad of them narrows to the one the operator meant.
|
|
157
|
+
*/
|
|
158
|
+
type Runnable = {
|
|
159
|
+
kind: "one";
|
|
160
|
+
/**
|
|
161
|
+
* The statement itself, without the whitespace around it or the `;` that ended it.
|
|
162
|
+
*/
|
|
163
|
+
text: string;
|
|
164
|
+
/**
|
|
165
|
+
* The same span with everything the engine's own scanner skips whole — literals, comments and
|
|
166
|
+
* quoted names — overwritten by spaces, character for character, so offsets still line up with
|
|
167
|
+
* `text`. Anything reading the statement's syntax rather than its content must read this: in
|
|
168
|
+
* `select '?'` the `?` is prose, and a marker scan of the raw text would ask the operator for
|
|
169
|
+
* a value the engine then refuses as an extra argument.
|
|
170
|
+
*/
|
|
171
|
+
blanked: string;
|
|
172
|
+
from: number;
|
|
173
|
+
to: number;
|
|
174
|
+
/**
|
|
175
|
+
* The document holds no other statement, so nothing needs marking — the operator can only
|
|
176
|
+
* have meant this one.
|
|
177
|
+
*/
|
|
178
|
+
alone: boolean;
|
|
179
|
+
} | {
|
|
180
|
+
/**
|
|
181
|
+
* A selection covering more than one statement. One run cannot send it, and sending its
|
|
182
|
+
* first would run something other than what was selected.
|
|
183
|
+
*/
|
|
184
|
+
kind: "several";
|
|
185
|
+
} | {
|
|
186
|
+
kind: "none";
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* The statement a run should send: the selection where there is one, otherwise the statement the
|
|
190
|
+
* caret sits in. `from` and `to` are the selection's own offsets, equal where nothing is selected
|
|
191
|
+
* — a caret is a selection of nothing.
|
|
192
|
+
*/
|
|
193
|
+
declare function runnableStatement(text: string, from: number, to: number, kind: SqlKind): Runnable;
|
|
194
|
+
interface IdentifierRead {
|
|
195
|
+
/**
|
|
196
|
+
* The dotted name's parts, unquoted, ending with the part the offset landed on — hovering `id`
|
|
197
|
+
* in `u.id` reads `["u", "id"]`, hovering `u` reads `["u"]`.
|
|
198
|
+
*/
|
|
199
|
+
parts: string[];
|
|
200
|
+
from: number;
|
|
201
|
+
to: number;
|
|
202
|
+
refs: TableRef[];
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The name at `offset`, with the qualifiers standing to its left. `null` where the offset is not
|
|
206
|
+
* on a name at all — punctuation, whitespace, a string, a comment.
|
|
207
|
+
*/
|
|
208
|
+
declare function readIdentifier(text: string, offset: number, kind: SqlKind): IdentifierRead | null;
|
|
209
|
+
interface CallRead {
|
|
210
|
+
name: string;
|
|
211
|
+
/**
|
|
212
|
+
* Which argument the caret sits in, counted by the commas at this call's own paren depth.
|
|
213
|
+
*/
|
|
214
|
+
argument: number;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The innermost call whose parens are still open at the caret, named by whatever word opened them
|
|
218
|
+
* — `in (` reports `in`, and whether that is a function is the dialect's to say, not this file's.
|
|
219
|
+
* `null` where the caret is in no parens, or in ones punctuation opened.
|
|
220
|
+
*/
|
|
221
|
+
declare function readCall(text: string, cursor: number, kind: SqlKind): CallRead | null;
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region src/schema.d.ts
|
|
224
|
+
/**
|
|
225
|
+
* The catalog as the bench reads it: the very RPCs the schema tree makes, so an
|
|
226
|
+
* already-expanded table answers straight from the page's query cache.
|
|
227
|
+
*
|
|
228
|
+
* **All three functions must be cache-backed reads** — a query cache, a memo the host's
|
|
229
|
+
* schema-refresh action invalidates — never fresh fetches. Completion reads them on every
|
|
230
|
+
* keystroke, `columns` once per table the statement references, and the package deliberately
|
|
231
|
+
* holds no cache of its own ({@link SqlSchema}): wired to raw fetches, every character typed is
|
|
232
|
+
* an N+1 burst of catalog calls.
|
|
233
|
+
*/
|
|
234
|
+
interface SqlCatalog {
|
|
235
|
+
tables: () => Promise<CatalogTable[]>;
|
|
236
|
+
columns: (schema: string, table: string) => Promise<CatalogColumn[]>;
|
|
237
|
+
functions: () => Promise<CatalogFunction[]>;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* A table's qualified name as it *reads* — the rail's filter and the hover card's heading, which
|
|
241
|
+
* are documentation and not statement. Text on its way into the editor goes through
|
|
242
|
+
* `spelling.ts` instead, which delimits it where the kind demands it. Kinds whose catalog reports
|
|
243
|
+
* no schema (or whose objects sit in the session's own) get the bare name.
|
|
244
|
+
*/
|
|
245
|
+
declare function qualifiedName(table: CatalogTable): string;
|
|
246
|
+
/**
|
|
247
|
+
* One table the statement reads, resolved to the catalog's own spelling and loaded.
|
|
248
|
+
*/
|
|
249
|
+
interface SqlSource {
|
|
250
|
+
table: CatalogTable;
|
|
251
|
+
/**
|
|
252
|
+
* How the statement refers to it: its alias where it declared one, else the name it wrote —
|
|
253
|
+
* the identity completion and hover match a qualifier against, always unquoted.
|
|
254
|
+
*/
|
|
255
|
+
as: string;
|
|
256
|
+
/**
|
|
257
|
+
* The alias exactly as the statement wrote it, delimiters and all — `""` where the ref has
|
|
258
|
+
* none. Generated SQL qualifies by `spellQualifier`, which uses this or falls back to the
|
|
259
|
+
* catalog name in the kind's spelling: re-spelling the unquoted `as` would be lossy, since
|
|
260
|
+
* `"u"` and `u` both read `u` and only one of them is what an Oracle statement means.
|
|
261
|
+
*/
|
|
262
|
+
aliasWritten: string;
|
|
263
|
+
columns: CatalogColumn[];
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* The catalog behind one selected system, in the shape the reader's questions take.
|
|
267
|
+
*
|
|
268
|
+
* It holds no cache of its own — deliberately. The host's catalog queries are the bench's one
|
|
269
|
+
* cache: they dedupe an in-flight read, they never go stale on their own, and the host's
|
|
270
|
+
* schema-refresh action invalidates them. A memo here would be a second cache the refresh cannot reach,
|
|
271
|
+
* and completion would keep answering from a schema the tree has already reloaded.
|
|
272
|
+
*/
|
|
273
|
+
interface SqlSchema {
|
|
274
|
+
tables: () => Promise<CatalogTable[]>;
|
|
275
|
+
columns: (table: CatalogTable) => Promise<CatalogColumn[]>;
|
|
276
|
+
/**
|
|
277
|
+
* Every function this data source could be asked to call — the estate's own routines, and the
|
|
278
|
+
* kind's built-ins where its catalog describes them. Refused the same way the rest is: a kind
|
|
279
|
+
* that cannot answer leaves the curated vocabulary to speak alone.
|
|
280
|
+
*/
|
|
281
|
+
functions: () => Promise<CatalogFunction[]>;
|
|
282
|
+
/**
|
|
283
|
+
* The catalog's own entry for a name the statement wrote — Oracle's catalog says `ORDERS`
|
|
284
|
+
* however the statement spelled it, and every later read must use the catalog's spelling.
|
|
285
|
+
*/
|
|
286
|
+
find: (ref: {
|
|
287
|
+
schema: string;
|
|
288
|
+
table: string;
|
|
289
|
+
}) => Promise<CatalogTable | null>;
|
|
290
|
+
/**
|
|
291
|
+
* The table one bare name stands for where the statement stands: an alias it declared, a table
|
|
292
|
+
* it already reads, or — failing both — a table of that name in the catalog. Completion and
|
|
293
|
+
* hover both ask, and a name must never mean two things between them.
|
|
294
|
+
*/
|
|
295
|
+
resolve: (name: string, refs: TableRef[]) => Promise<CatalogTable | null>;
|
|
296
|
+
/**
|
|
297
|
+
* Every ref the statement names, resolved and loaded. Refs the catalog does not know (a CTE, a
|
|
298
|
+
* subquery's alias, a typo) simply drop out, as does any table whose columns refuse to load —
|
|
299
|
+
* the answer is thinner, never absent.
|
|
300
|
+
*/
|
|
301
|
+
sources: (refs: TableRef[]) => Promise<SqlSource[]>;
|
|
302
|
+
}
|
|
303
|
+
declare function createSqlSchema(catalog: SqlCatalog): SqlSchema;
|
|
304
|
+
//#endregion
|
|
305
|
+
//#region src/assist.d.ts
|
|
306
|
+
/**
|
|
307
|
+
* One suggestion. `label` is what the pane shows and what the editor filters the operator's
|
|
308
|
+
* typing against; `insertText` is what lands in the statement, which differs whenever the
|
|
309
|
+
* position needs more than the name (a qualified column, an expanded list, a whole join
|
|
310
|
+
* condition).
|
|
311
|
+
*/
|
|
312
|
+
interface SqlCompletionCandidate {
|
|
313
|
+
label: string;
|
|
314
|
+
/**
|
|
315
|
+
* Drives the pane's icon. `snippet` is the synthetic kind: a candidate that is an edit rather
|
|
316
|
+
* than a name — the `*` expansion, the insert skeleton, a join condition.
|
|
317
|
+
*/
|
|
318
|
+
kind: "keyword" | "function" | "schema" | "table" | "view" | "column" | "snippet";
|
|
319
|
+
insertText: string;
|
|
320
|
+
/**
|
|
321
|
+
* `insertText` is a snippet (`$0` marks the caret) rather than plain text.
|
|
322
|
+
*/
|
|
323
|
+
snippet?: boolean;
|
|
324
|
+
/**
|
|
325
|
+
* Characters immediately before the caret that this insert replaces, beyond the partial word
|
|
326
|
+
* the editor already replaces on its own. Only the `*` expansion uses it — it stands in for a
|
|
327
|
+
* `*` the operator has already typed, which is punctuation and so outside the editor's word
|
|
328
|
+
* range.
|
|
329
|
+
*/
|
|
330
|
+
replace?: number;
|
|
331
|
+
detail?: string;
|
|
332
|
+
documentation?: string;
|
|
333
|
+
/**
|
|
334
|
+
* Rank band. Lower bands sort first; within one band the engine's own array order decides, so
|
|
335
|
+
* a column list keeps the catalog's ordinal order instead of falling to the alphabet.
|
|
336
|
+
*/
|
|
337
|
+
sortGroup: number;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* What hovering a span of the statement says about it. Offsets are into the text handed to
|
|
341
|
+
* `hover`, so the editor can turn them into its own range without re-reading the SQL.
|
|
342
|
+
*/
|
|
343
|
+
interface SqlHoverCard {
|
|
344
|
+
from: number;
|
|
345
|
+
to: number;
|
|
346
|
+
/**
|
|
347
|
+
* Markdown blocks, rendered in order.
|
|
348
|
+
*/
|
|
349
|
+
markdown: string[];
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* One call's signature while the caret sits inside its parens.
|
|
353
|
+
*/
|
|
354
|
+
interface SqlSignatureHelp {
|
|
355
|
+
/**
|
|
356
|
+
* The whole call as it is written, e.g. `coalesce(value, fallback, …)`.
|
|
357
|
+
*/
|
|
358
|
+
label: string;
|
|
359
|
+
/**
|
|
360
|
+
* Each parameter's `[start, end)` within `label` — offsets rather than substrings, so a
|
|
361
|
+
* signature that repeats a parameter name still marks the right one.
|
|
362
|
+
*/
|
|
363
|
+
parameters: Array<[number, number]>;
|
|
364
|
+
/**
|
|
365
|
+
* The parameter the caret is in, already clamped onto a variadic tail.
|
|
366
|
+
*/
|
|
367
|
+
active: number;
|
|
368
|
+
documentation?: string;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* The editor's assist for one connection's schema and dialect: everything the surface can ask
|
|
372
|
+
* that needs to know either. One instance per selected connection — the catalog memo behind it
|
|
373
|
+
* lives exactly as long.
|
|
374
|
+
*/
|
|
375
|
+
interface SqlAssist {
|
|
376
|
+
complete: (text: string, offset: number) => Promise<SqlCompletionCandidate[]>;
|
|
377
|
+
hover: (text: string, offset: number) => Promise<SqlHoverCard | null>;
|
|
378
|
+
/**
|
|
379
|
+
* The vocabulary a signature comes from includes the estate's own routines, which only the
|
|
380
|
+
* catalog knows — so this waits on I/O like the rest, and answers from a memo after the first.
|
|
381
|
+
*/
|
|
382
|
+
signature: (text: string, offset: number) => Promise<SqlSignatureHelp | null>;
|
|
383
|
+
/**
|
|
384
|
+
* The whole statement text, reformatted — or `null` when the dialect cannot parse it, which
|
|
385
|
+
* leaves the operator's text untouched.
|
|
386
|
+
*/
|
|
387
|
+
format: (text: string) => Promise<string | null>;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Everything the SQL editor can ask about one connection's schema, over one shared catalog:
|
|
391
|
+
* completion, hover and signature help all read the same statement through `reader.ts` — under
|
|
392
|
+
* this kind's own lexical profile — and the same schema through `schema.ts`, so they can never
|
|
393
|
+
* disagree about what the operator is looking at, and the catalog is asked for nothing beyond
|
|
394
|
+
* what a schema tree already fetches.
|
|
395
|
+
*
|
|
396
|
+
* One instance per selected connection. The host memoizes it; another vendor's schema is another
|
|
397
|
+
* object, and nothing is held here that could outlive it.
|
|
398
|
+
*/
|
|
399
|
+
declare function createSqlAssist(kind: SqlKind, catalog: SqlCatalog): SqlAssist;
|
|
400
|
+
//#endregion
|
|
401
|
+
export { CatalogTable as C, CatalogFunction as S, readCall as _, createSqlAssist as a, runnableStatement as b, SqlSource as c, CallRead as d, IdentifierRead as f, TableRef as g, StatementRead as h, SqlSignatureHelp as i, createSqlSchema as l, StatementContext as m, SqlCompletionCandidate as n, SqlCatalog as o, Runnable as p, SqlHoverCard as r, SqlSchema as s, SqlAssist as t, qualifiedName as u, readIdentifier as v, SqlKind as w, CatalogColumn as x, readStatement as y };
|