Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions docs/vnext/codemirror-adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# vNext CodeMirror Adapter

Status: experimental

Import: `@marimo-team/codemirror-sql/vnext/codemirror`

`sqlEditor` connects one caller-owned vNext language service to one or more
CodeMirror 6 views. Each view owns a document session, cancellation state,
catalog-refresh intent, and revision subscription. Destroying a view disposes
that view's session but never disposes the shared service.

```ts
import { EditorView } from "@codemirror/view";
import {
createSqlLanguageService,
duckdbDialect,
} from "@marimo-team/codemirror-sql/vnext";
import {
sqlEditor,
} from "@marimo-team/codemirror-sql/vnext/codemirror";

const service = createSqlLanguageService({
dialects: [duckdbDialect()],
});
const support = sqlEditor({
initialContext: {
catalog: { scope: "connection-incarnation:1" },
dialect: "duckdb",
},
service,
});
const view = new EditorView({
doc: "SELECT * FROM ",
extensions: [support.extension],
});

support.setContext(view, {
catalog: { scope: "connection-incarnation:2" },
dialect: "duckdb",
});

view.destroy();
service.dispose();
```

The adapter installs one coherent `autocompletion` configuration. Additional
sources belong in `autocomplete.externalSources`; consumers should not install
a second independently configured `autocompletion` extension.

## Atomic inputs

Context and document changes can share one CodeMirror transaction:

```ts
view.dispatch({
changes,
effects: support.contextEffect.of(nextContext),
});
```

CodeMirror's composite `ChangeSet` is converted to sorted, non-overlapping
pre-update UTF-16 changes and sent to the session exactly once.

Embedded-language ranges are explicit. Their effect payload is the complete
region set in the resulting document's coordinates:

```ts
view.dispatch({
changes,
effects: support.embeddedRegionsEffect.of(nextRegions),
});
```

When the current document contains embedded regions, every document-changing
transaction must include the complete resulting region set. The adapter fails
closed instead of guessing how host-language delimiters map through edits.
Region-only updates may use `setEmbeddedRegions`.

The adapter's own completion edit is the narrow exception: it maps unrelated
regions through the exact edit and includes the complete mapped set in the same
transaction. A completion edit that overlaps an embedded region is ignored.

## Completion currency

The adapter does not use `validFor` or result mapping in this first slice.
Every result and completion application rechecks the session revision,
document identity, selection, and context generation.

Loading completion results retain one bounded, token-correlated refresh intent,
including empty results that open no menu. Only a session event carrying the
exact task token can schedule a refresh. A newer request, document/context/
region/selection change, Escape, configured blur, visibility loss, lease
expiry, or view destruction cancels the intent. Refresh and close operations
are always queued; provider and session callbacks never synchronously dispatch
CodeMirror transactions.
3 changes: 3 additions & 0 deletions docs/vnext/session-primitives.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ opaque revisions, dialect registration, lifecycle management, and
parser-independent relation completion. Diagnostics, hover, navigation, and
general expression completion are not yet available.

CodeMirror consumers should use the separate
[vNext CodeMirror adapter](./codemirror-adapter.md).

See [source coordinates](./source-coordinates.md) for the shared UTF-16 range
contract and the internal immutable source-snapshot model.

Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@
"default": "./dist/vnext/index.js"
}
},
"./vnext/codemirror": {
"import": {
"types": "./dist/vnext/codemirror/index.d.ts",
"default": "./dist/vnext/codemirror/index.js"
}
},
"./data/common-keywords.json": "./src/data/common-keywords.json",
"./data/duckdb-keywords.json": "./src/data/duckdb-keywords.json"
},
Expand Down
10 changes: 10 additions & 0 deletions scripts/package-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ import {
type SqlEmbeddedRegion,
type SqlTextRange,
} from "@marimo-team/codemirror-sql/vnext";
import { sqlEditor } from "@marimo-team/codemirror-sql/vnext/codemirror";
import commonKeywords from "@marimo-team/codemirror-sql/data/common-keywords.json" with { type: "json" };
import duckdbKeywords from "@marimo-team/codemirror-sql/data/duckdb-keywords.json" with { type: "json" };

Expand All @@ -246,6 +247,10 @@ const parser = new NodeSqlParser();
const service = createSqlLanguageService<HostContext>({
dialects: [duckdbDialect()],
});
const editorSupport = sqlEditor({
initialContext: { dialect: "duckdb", engine: "local" },
service,
});
const embeddedRegions: readonly SqlEmbeddedRegion[] = [
{ from: 14, language: "python", to: 18 },
];
Expand All @@ -262,6 +267,7 @@ session.update({
});

void extensions;
void editorSupport.extension;
void parser;
void range;
void session;
Expand All @@ -278,6 +284,7 @@ void duckdbKeywords;
import * as api from "@marimo-team/codemirror-sql";
import * as dialects from "@marimo-team/codemirror-sql/dialects";
import * as vnext from "@marimo-team/codemirror-sql/vnext";
import * as vnextCodeMirror from "@marimo-team/codemirror-sql/vnext/codemirror";
import commonKeywords from "@marimo-team/codemirror-sql/data/common-keywords.json" with { type: "json" };
import duckdbKeywords from "@marimo-team/codemirror-sql/data/duckdb-keywords.json" with { type: "json" };

Expand All @@ -296,6 +303,9 @@ if (
) {
throw new Error("vNext package exports are incomplete");
}
if (typeof vnextCodeMirror.sqlEditor !== "function") {
throw new Error("vNext CodeMirror package exports are incomplete");
}
for (const dialect of [
vnext.bigQueryDialect(),
vnext.dremioDialect(),
Expand Down
Loading
Loading