Skip to content

Commit c901544

Browse files
committed
feat(vnext): expose statement boundaries
1 parent e052af6 commit c901544

10 files changed

Lines changed: 838 additions & 12 deletions

File tree

docs/vnext/session-primitives.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,47 @@ CodeMirror consumers should use the separate
1414
See [source coordinates](./source-coordinates.md) for the shared UTF-16 range
1515
contract and the internal immutable source-snapshot model.
1616

17+
Statement boundaries are available as a synchronous structural query:
18+
19+
```ts
20+
const result = session.statementBoundaryAt({
21+
affinity: "left",
22+
position: cursorOffset,
23+
});
24+
25+
if (
26+
session.isCurrent(result.revision) &&
27+
result.boundary.boundaryQuality === "exact" &&
28+
result.boundary.hasCode
29+
) {
30+
executeRange(result.boundary.source);
31+
}
32+
```
33+
34+
The immutable result carries the current session revision. Exact boundaries
35+
expose their full extent, source range, optional terminator, lexical end state,
36+
and a nullable range from the first through last SQL code token. Source ranges
37+
retain attached
38+
whitespace and comments; they are factual lexical boundaries, not pre-trimmed
39+
visual selections. The `code` range excludes leading and trailing separator
40+
trivia so presentation layers do not need to re-lex the document. Opaque
41+
procedural, custom-delimiter, and resource-limited
42+
regions expose only their extent and reason, so consumers cannot mistake them
43+
for safely executable SQL.
44+
45+
Viewport consumers can retrieve every structural boundary in one half-open
46+
range without probing or re-lexing the document:
47+
48+
```ts
49+
const visible = session.statementBoundariesIntersecting({
50+
from: viewport.from,
51+
to: viewport.to,
52+
});
53+
```
54+
55+
The query runs in logarithmic lookup time plus the number of intersecting
56+
boundaries and returns a frozen, revision-stamped array.
57+
1758
## Example
1859

1960
```ts

scripts/package-smoke.mjs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,13 @@ session.update({
207207
baseRevision: session.revision,
208208
document: { kind: "replace", text: "SELECT * FROM {next_df}" },
209209
});
210+
const statement = session.statementBoundaryAt({ affinity: "left", position: 0 });
211+
if (
212+
statement.boundary.boundaryQuality !== "exact" ||
213+
!statement.boundary.hasCode
214+
) {
215+
throw new Error("The packaged vNext statement boundary was unavailable");
216+
}
210217
service.dispose();
211218
`,
212219
);
@@ -229,6 +236,7 @@ import {
229236
duckdbDialect,
230237
type SqlDocumentContext,
231238
type SqlEmbeddedRegion,
239+
type SqlStatementBoundaryAtResult,
232240
type SqlTextRange,
233241
} from "@marimo-team/codemirror-sql/vnext";
234242
import { sqlEditor } from "@marimo-team/codemirror-sql/vnext/codemirror";
@@ -265,12 +273,17 @@ session.update({
265273
baseRevision: session.revision,
266274
document: { kind: "changes", changes: [] },
267275
});
276+
const statement: SqlStatementBoundaryAtResult = session.statementBoundaryAt({
277+
affinity: "left",
278+
position: 0,
279+
});
268280
269281
void extensions;
270282
void editorSupport.extension;
271283
void parser;
272284
void range;
273285
void session;
286+
void statement;
274287
void BigQueryDialect;
275288
void DremioDialect;
276289
void commonKeywords;
@@ -349,6 +362,20 @@ const updatedRevision = session.update({
349362
if (session.isCurrent(originalRevision) || !session.isCurrent(updatedRevision)) {
350363
throw new Error("The packaged vNext session violated revision identity");
351364
}
365+
const statement = session.statementBoundaryAt({ affinity: "left", position: 0 });
366+
const visible = session.statementBoundariesIntersecting({
367+
from: 0,
368+
to: 23,
369+
});
370+
if (
371+
statement.revision !== updatedRevision ||
372+
statement.boundary.boundaryQuality !== "exact" ||
373+
!statement.boundary.hasCode ||
374+
visible.revision !== updatedRevision ||
375+
visible.boundaries.length !== 1
376+
) {
377+
throw new Error("The packaged vNext statement boundary is invalid");
378+
}
352379
service.dispose();
353380
`,
354381
);

0 commit comments

Comments
 (0)