@coldsmirk/inkstone-codemirror 0.9.0 → 0.10.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.
@@ -0,0 +1,15 @@
1
+ import { StateEffect, StateField } from "@codemirror/state";
2
+ //#region src/sql-schema.ts
3
+ const setSqlSchema = StateEffect.define();
4
+ const sqlSchemaField = StateField.define({
5
+ create() {
6
+ return null;
7
+ },
8
+ update(value, transaction) {
9
+ let next = value;
10
+ for (const effect of transaction.effects) if (effect.is(setSqlSchema)) next = effect.value;
11
+ return next;
12
+ }
13
+ });
14
+ //#endregion
15
+ export { sqlSchemaField as n, setSqlSchema as t };
@@ -0,0 +1,29 @@
1
+ import { n as sqlSchemaField } from "./sql-schema-DiPx5-bu.js";
2
+ import { LanguageSupport } from "@codemirror/language";
3
+ import { schemaCompletionSource } from "@codemirror/lang-sql";
4
+ //#region src/sql-support.ts
5
+ function withSqlSchema(base, dialect) {
6
+ const delegates = /* @__PURE__ */ new WeakMap();
7
+ const source = (context) => {
8
+ const schema = context.state.field(sqlSchemaField, false) ?? null;
9
+ if (schema === null) return null;
10
+ let delegate = delegates.get(schema);
11
+ if (delegate === void 0) {
12
+ delegate = schemaCompletionSource({
13
+ dialect,
14
+ schema: schema.tables,
15
+ defaultTable: schema.defaultTable,
16
+ defaultSchema: schema.defaultSchema
17
+ });
18
+ delegates.set(schema, delegate);
19
+ }
20
+ return delegate(context);
21
+ };
22
+ return new LanguageSupport(base.language, [
23
+ base.support,
24
+ sqlSchemaField,
25
+ base.language.data.of({ autocomplete: source })
26
+ ]);
27
+ }
28
+ //#endregion
29
+ export { withSqlSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coldsmirk/inkstone-codemirror",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Framework-agnostic CodeMirror 6 plumbing: a controlled editor host (create-once, external-value reconcile, in-place theme swap) and the standard document extension bundle.",
5
5
  "keywords": [
6
6
  "codemirror",
@@ -58,6 +58,7 @@
58
58
  "@codemirror/lang-yaml": "^6.1.3",
59
59
  "@codemirror/legacy-modes": "^6.5.3",
60
60
  "@codemirror/lint": "^6.9.7",
61
+ "@codemirror/merge": "^6.12.2",
61
62
  "@lezer/common": "^1.5.2"
62
63
  },
63
64
  "devDependencies": {
@@ -1,418 +0,0 @@
1
- import { i as resolveMembers, n as minijinjaContextField, o as typeAtPath, r as normalizeContext, t as elementType } from "./context-D86o2jK3.js";
2
- import { LanguageSupport, syntaxTree } from "@codemirror/language";
3
- import { jinja, jinjaLanguage } from "@codemirror/lang-jinja";
4
- import { linter } from "@codemirror/lint";
5
- //#region src/minijinja.ts
6
- const MINIJINJA_TAGS = /* @__PURE__ */ new Set([
7
- "autoescape",
8
- "endautoescape",
9
- "block",
10
- "endblock",
11
- "call",
12
- "endcall",
13
- "filter",
14
- "endfilter",
15
- "for",
16
- "endfor",
17
- "if",
18
- "elif",
19
- "else",
20
- "endif",
21
- "macro",
22
- "endmacro",
23
- "set",
24
- "endset",
25
- "with",
26
- "endwith",
27
- "raw",
28
- "endraw",
29
- "extends",
30
- "include",
31
- "import",
32
- "from",
33
- "do",
34
- "break",
35
- "continue"
36
- ]);
37
- const MINIJINJA_FILTERS = [
38
- "abs",
39
- "attr",
40
- "batch",
41
- "bool",
42
- "capitalize",
43
- "chain",
44
- "count",
45
- "d",
46
- "default",
47
- "dictsort",
48
- "e",
49
- "escape",
50
- "first",
51
- "float",
52
- "format",
53
- "groupby",
54
- "indent",
55
- "int",
56
- "items",
57
- "join",
58
- "last",
59
- "length",
60
- "lines",
61
- "list",
62
- "lower",
63
- "map",
64
- "max",
65
- "min",
66
- "pprint",
67
- "reject",
68
- "rejectattr",
69
- "replace",
70
- "reverse",
71
- "round",
72
- "safe",
73
- "select",
74
- "selectattr",
75
- "slice",
76
- "sort",
77
- "split",
78
- "string",
79
- "sum",
80
- "title",
81
- "tojson",
82
- "trim",
83
- "unique",
84
- "upper",
85
- "urlencode",
86
- "zip"
87
- ];
88
- const MINIJINJA_TESTS = [
89
- "boolean",
90
- "defined",
91
- "divisibleby",
92
- "endingwith",
93
- "eq",
94
- "equalto",
95
- "escaped",
96
- "even",
97
- "false",
98
- "filter",
99
- "float",
100
- "ge",
101
- "greaterthan",
102
- "in",
103
- "int",
104
- "integer",
105
- "iterable",
106
- "le",
107
- "lessthan",
108
- "lower",
109
- "lt",
110
- "gt",
111
- "mapping",
112
- "ne",
113
- "none",
114
- "number",
115
- "odd",
116
- "safe",
117
- "sameas",
118
- "sequence",
119
- "startingwith",
120
- "string",
121
- "test",
122
- "true",
123
- "undefined",
124
- "upper"
125
- ];
126
- const MINIJINJA_FUNCTIONS = [
127
- "debug",
128
- "dict",
129
- "namespace",
130
- "range"
131
- ];
132
- function toCompletions(labels, type) {
133
- return [...labels].toSorted().map((label) => {
134
- return {
135
- label,
136
- type
137
- };
138
- });
139
- }
140
- const tagCompletions = toCompletions(MINIJINJA_TAGS, "keyword");
141
- const filterCompletions = toCompletions(MINIJINJA_FILTERS, "function");
142
- const testCompletions = toCompletions(MINIJINJA_TESTS, "function");
143
- const functionCompletions = toCompletions(MINIJINJA_FUNCTIONS, "function");
144
- function memberCompletions(members, type) {
145
- return members.map((member) => {
146
- return {
147
- label: member.name,
148
- type,
149
- detail: `${irTypeToString(member.type)}${member.nullable ? "?" : ""}`,
150
- info: member.doc
151
- };
152
- });
153
- }
154
- function irTypeToString(type) {
155
- switch (type.kind) {
156
- case "object": return "object";
157
- case "array": return `${irTypeToString(type.element)}[]`;
158
- case "enum": return type.values.map((value) => typeof value === "string" ? `"${value}"` : String(value)).join(" | ");
159
- case "ref": return type.name;
160
- default: return type.kind;
161
- }
162
- }
163
- function walkBase(state, node) {
164
- if (!node) return null;
165
- if (node.name === "VariableName" || node.name === "loop") return [state.sliceDoc(node.from, node.to)];
166
- if (node.name === "MemberExpression") {
167
- const inner = walkBase(state, node.firstChild);
168
- const property = node.lastChild;
169
- if (inner === null || !property || property.name !== "PropertyName") return null;
170
- return [...inner, state.sliceDoc(property.from, property.to)];
171
- }
172
- return null;
173
- }
174
- function contextTarget(state, pos) {
175
- const node = syntaxTree(state).resolveInner(pos, -1);
176
- if (node.name === "PropertyName" && node.parent?.name === "MemberExpression") {
177
- const path = walkBase(state, node.parent.firstChild);
178
- return path ? {
179
- path,
180
- from: node.from
181
- } : null;
182
- }
183
- if (node.name === "." && node.parent?.name === "MemberExpression") {
184
- const path = walkBase(state, node.parent.firstChild);
185
- return path ? {
186
- path,
187
- from: pos
188
- } : null;
189
- }
190
- if (node.name === "Definition") return null;
191
- const word = state.wordAt(pos);
192
- return {
193
- path: [],
194
- from: word ? word.from : pos
195
- };
196
- }
197
- const LOOP_TYPE = {
198
- kind: "object",
199
- fields: {
200
- index: loopField("number"),
201
- index0: loopField("number"),
202
- revindex: loopField("number"),
203
- revindex0: loopField("number"),
204
- first: loopField("boolean"),
205
- last: loopField("boolean"),
206
- length: loopField("number"),
207
- depth: loopField("number"),
208
- depth0: loopField("number"),
209
- previtem: loopField("any"),
210
- nextitem: loopField("any"),
211
- cycle: loopField("any"),
212
- changed: loopField("any")
213
- }
214
- };
215
- function loopField(kind) {
216
- return {
217
- type: { kind },
218
- required: true,
219
- nullable: false
220
- };
221
- }
222
- const ROOT_SCOPE = -1;
223
- function scopeBindings(state, pos, ir) {
224
- const bindings = /* @__PURE__ */ new Map();
225
- const enclosing = [];
226
- for (let node = syntaxTree(state).resolveInner(pos, -1); node; node = node.parent) if (node.name === "ForStatement" || node.name === "MacroStatement" || node.name === "WithStatement") enclosing.push(node);
227
- const scopes = enclosing.toReversed();
228
- const sets = collectSetBindings(state, pos, scopes);
229
- const applySets = (scopeKey) => {
230
- const definitions = sets.get(scopeKey) ?? [];
231
- for (const definition of definitions) bindings.set(state.sliceDoc(definition.from, definition.to), { kind: "any" });
232
- };
233
- applySets(ROOT_SCOPE);
234
- for (const node of scopes) {
235
- if (node.name === "ForStatement") collectForBindings(state, node, pos, ir, bindings);
236
- else if (node.name === "MacroStatement") collectMacroBindings(state, node, pos, bindings);
237
- else collectWithBindings(state, node, pos, ir, bindings);
238
- applySets(node.from);
239
- }
240
- return bindings;
241
- }
242
- function collectForBindings(state, forNode, pos, ir, bindings) {
243
- const tag = forNode.firstChild;
244
- if (!tag || !tagBodyStarted(tag, pos)) return;
245
- const definitions = [];
246
- let iterable = null;
247
- let seenIn = false;
248
- for (let child = tag.firstChild; child; child = child.nextSibling) if (child.name === "in") seenIn = true;
249
- else if (!seenIn && child.name === "Definition") definitions.push(child);
250
- else if (seenIn && !iterable && child.name !== "%}") iterable = child;
251
- let element = null;
252
- if (definitions.length === 1 && iterable) {
253
- const path = walkBase(state, iterable);
254
- const iterableType = path ? typeAtPath(ir, path, bindings) : null;
255
- element = iterableType ? elementType(ir, iterableType) : null;
256
- }
257
- bindings.set("loop", LOOP_TYPE);
258
- for (const definition of definitions) bindings.set(state.sliceDoc(definition.from, definition.to), element ?? { kind: "any" });
259
- }
260
- function collectWithBindings(state, withNode, pos, ir, bindings) {
261
- const tag = withNode.firstChild;
262
- if (!tag || tag.name !== "Tag") return;
263
- const inBody = tagBodyStarted(tag, pos);
264
- let targets = [];
265
- for (let child = tag.firstChild; child; child = child.nextSibling) {
266
- if (child.name === "Definition") {
267
- targets.push(child);
268
- continue;
269
- }
270
- if (child.name !== "AssignOp" || targets.length === 0) continue;
271
- const value = child.nextSibling;
272
- const delimiter = value?.nextSibling;
273
- if (inBody || delimiter?.name === "," && delimiter.to <= pos) {
274
- const path = targets.length === 1 && value ? walkBase(state, value) : null;
275
- const type = path ? typeAtPath(ir, path, bindings) : null;
276
- for (const target of targets) bindings.set(state.sliceDoc(target.from, target.to), type ?? { kind: "any" });
277
- }
278
- targets = [];
279
- }
280
- }
281
- function collectMacroBindings(state, macroNode, pos, bindings) {
282
- const tag = macroNode.firstChild;
283
- if (!tag || !tagBodyStarted(tag, pos)) return;
284
- const paramList = tag.getChild("ParamList");
285
- if (!paramList) return;
286
- for (let child = paramList.firstChild; child; child = child.nextSibling) if (child.name === "Definition") bindings.set(state.sliceDoc(child.from, child.to), { kind: "any" });
287
- }
288
- function tagBodyStarted(tag, pos) {
289
- return tag.name === "Tag" && tag.getChild("%}") !== null && pos >= tag.to;
290
- }
291
- function collectSetBindings(state, pos, scopes) {
292
- const scopeStarts = new Set(scopes.map((scope) => scope.from));
293
- const sets = /* @__PURE__ */ new Map();
294
- syntaxTree(state).iterate({
295
- to: pos,
296
- enter: (node) => {
297
- if (node.name !== "Tag" || node.to > pos) return;
298
- const keyword = node.node.firstChild?.nextSibling;
299
- if (!keyword || state.sliceDoc(keyword.from, keyword.to) !== "set") return;
300
- const definitions = [];
301
- let assignment = false;
302
- for (let child = keyword.nextSibling; child; child = child.nextSibling) {
303
- if (child.name === "AssignOp") {
304
- assignment = true;
305
- break;
306
- }
307
- if (child.name === "Definition") definitions.push(child);
308
- }
309
- if (definitions.length === 0) return;
310
- const statement = node.node.parent;
311
- if (assignment) {
312
- if (!node.node.getChild("%}")) return;
313
- } else if (statement?.name !== "SetStatement" || statement.to > pos || !statement.getChild("EndTag")) return;
314
- let scopeKey = ROOT_SCOPE;
315
- for (let { parent } = node.node; parent; parent = parent.parent) if (parent.name === "ForStatement" || parent.name === "MacroStatement" || parent.name === "WithStatement") {
316
- if (!scopeStarts.has(parent.from)) return;
317
- scopeKey = parent.from;
318
- break;
319
- }
320
- const bucket = sets.get(scopeKey);
321
- if (bucket) bucket.push(...definitions);
322
- else sets.set(scopeKey, definitions);
323
- }
324
- });
325
- return sets;
326
- }
327
- function jinjaContext(state, pos, side) {
328
- for (let node = syntaxTree(state).resolveInner(pos, side); node; node = node.parent) {
329
- const { name } = node;
330
- if (name === "Comment") return "comment";
331
- if (name === "StringLiteral" || name === "NumberLiteral") return null;
332
- if (name === "Interpolation" || name === "Tag" || name === "EndTag") return "expr";
333
- }
334
- return null;
335
- }
336
- function minijinjaCompletion(context) {
337
- const word = context.matchBefore(/\w*/);
338
- if (!word) return null;
339
- const { from } = word;
340
- const before = context.state.sliceDoc(Math.max(0, from - 24), from);
341
- let side = from === context.pos ? 1 : -1;
342
- if (side === 1 && context.pos === context.state.doc.length) {
343
- const left = syntaxTree(context.state).resolveInner(context.pos, -1);
344
- if (left.name !== "}}" && left.name !== "%}" && left.name !== "#}") side = -1;
345
- }
346
- if (jinjaContext(context.state, context.pos, side) !== "expr") return null;
347
- if (/\{%[-+]?\s*$/.test(before)) return {
348
- from,
349
- options: tagCompletions,
350
- validFor: /^\w*$/
351
- };
352
- if (/\|\s*$/.test(before)) return {
353
- from,
354
- options: filterCompletions,
355
- validFor: /^\w*$/
356
- };
357
- if (/\bis\s+(?:not\s+)?$/.test(before)) return {
358
- from,
359
- options: testCompletions,
360
- validFor: /^\w*$/
361
- };
362
- const ir = context.state.field(minijinjaContextField, false);
363
- if (ir) {
364
- const target = contextTarget(context.state, context.pos);
365
- if (!target) return null;
366
- const scope = scopeBindings(context.state, context.pos, ir);
367
- const members = resolveMembers(ir, target.path, scope);
368
- if (target.path.length > 0) return members.length > 0 ? {
369
- from: target.from,
370
- options: memberCompletions(members, "property"),
371
- validFor: /^\w*$/
372
- } : null;
373
- if (target.from === context.pos && !context.explicit) return null;
374
- return {
375
- from: target.from,
376
- options: [...memberCompletions(members, "variable"), ...functionCompletions],
377
- validFor: /^\w*$/
378
- };
379
- }
380
- if (from === context.pos && !context.explicit) return null;
381
- return {
382
- from,
383
- options: functionCompletions,
384
- validFor: /^\w*$/
385
- };
386
- }
387
- function minijinjaDiagnostics(state) {
388
- const diagnostics = [];
389
- const { doc } = state;
390
- syntaxTree(state).iterate({ enter: (node) => {
391
- if (node.name !== "Tag" && node.name !== "EndTag") return;
392
- const keywordNode = node.node.firstChild?.nextSibling;
393
- if (!keywordNode || keywordNode.type.isError) return;
394
- const keyword = doc.sliceString(keywordNode.from, keywordNode.to);
395
- if (!MINIJINJA_TAGS.has(keyword)) diagnostics.push({
396
- from: keywordNode.from,
397
- to: keywordNode.to,
398
- severity: "error",
399
- message: `"${keyword}" is not a MiniJinja tag.`
400
- });
401
- } });
402
- return diagnostics;
403
- }
404
- const minijinjaLinter = linter((view) => minijinjaDiagnostics(view.state));
405
- function minijinja(config = {}) {
406
- const { base, context } = config;
407
- const language = base ? jinja({ base }).language : jinjaLanguage;
408
- const contextField = context ? minijinjaContextField.init(() => normalizeContext(context)) : minijinjaContextField;
409
- const support = [
410
- language.data.of({ autocomplete: minijinjaCompletion }),
411
- minijinjaLinter,
412
- contextField
413
- ];
414
- if (base) support.push(base.support);
415
- return new LanguageSupport(language, support);
416
- }
417
- //#endregion
418
- export { minijinja, minijinjaCompletion, minijinjaDiagnostics };