@coldsmirk/inkstone-codemirror 0.8.3 → 0.9.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/dist/context-D86o2jK3.js +382 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +32 -30
- package/dist/{minijinja-HpehSBbW.js → minijinja-D_5erBa7.js} +81 -18
- package/package.json +4 -12
- package/dist/context-BDibyUwD.cjs +0 -270
- package/dist/context-Dx6bPnKq.js +0 -235
- package/dist/index.cjs +0 -310
- package/dist/index.d.cts +0 -554
- package/dist/minijinja-DN9iLNbL.cjs +0 -357
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { StateEffect, StateField } from "@codemirror/state";
|
|
2
|
+
//#region src/context.ts
|
|
3
|
+
const ANY = { kind: "any" };
|
|
4
|
+
const NULL = { kind: "null" };
|
|
5
|
+
const MAX_DEREF = 100;
|
|
6
|
+
const setMinijinjaContext = StateEffect.define();
|
|
7
|
+
const minijinjaContextField = StateField.define({
|
|
8
|
+
create() {
|
|
9
|
+
return null;
|
|
10
|
+
},
|
|
11
|
+
update(value, transaction) {
|
|
12
|
+
let next = value;
|
|
13
|
+
for (const effect of transaction.effects) if (effect.is(setMinijinjaContext)) next = effect.value;
|
|
14
|
+
return next;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
function normalizeContext(input) {
|
|
18
|
+
if ("schema" in input) return fromJsonSchema(input.schema);
|
|
19
|
+
return {
|
|
20
|
+
root: fromSample(input.sample),
|
|
21
|
+
defs: {}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function resolveMembers(ir, path, scope) {
|
|
25
|
+
if (path.length === 0) {
|
|
26
|
+
const top = membersOf(ir, ir.root);
|
|
27
|
+
if (!scope || scope.size === 0) return top;
|
|
28
|
+
return [...[...scope].map(([name, type]) => {
|
|
29
|
+
return {
|
|
30
|
+
name,
|
|
31
|
+
type,
|
|
32
|
+
required: true,
|
|
33
|
+
nullable: false
|
|
34
|
+
};
|
|
35
|
+
}), ...top];
|
|
36
|
+
}
|
|
37
|
+
const type = typeAtPath(ir, path, scope);
|
|
38
|
+
return type ? membersOf(ir, type) : [];
|
|
39
|
+
}
|
|
40
|
+
function typeAtPath(ir, path, scope) {
|
|
41
|
+
const [first, ...rest] = path;
|
|
42
|
+
if (first === void 0) return deref(ir, ir.root);
|
|
43
|
+
let type = scope?.get(first) ?? stepInto(ir, ir.root, first);
|
|
44
|
+
for (const key of rest) {
|
|
45
|
+
if (type === null) return null;
|
|
46
|
+
type = stepInto(ir, type, key);
|
|
47
|
+
}
|
|
48
|
+
return type === null ? null : deref(ir, type);
|
|
49
|
+
}
|
|
50
|
+
function elementType(ir, type) {
|
|
51
|
+
const resolved = deref(ir, type);
|
|
52
|
+
return resolved.kind === "array" ? deref(ir, resolved.element) : null;
|
|
53
|
+
}
|
|
54
|
+
function followRefs(type, lookup) {
|
|
55
|
+
let current = type;
|
|
56
|
+
for (let i = 0; i < MAX_DEREF && current.kind === "ref"; i++) {
|
|
57
|
+
const next = lookup(current.name);
|
|
58
|
+
if (!next) return null;
|
|
59
|
+
current = next;
|
|
60
|
+
}
|
|
61
|
+
return current.kind === "ref" ? null : current;
|
|
62
|
+
}
|
|
63
|
+
function deref(ir, type) {
|
|
64
|
+
return followRefs(type, (name) => ir.defs[name] ?? null) ?? ANY;
|
|
65
|
+
}
|
|
66
|
+
function stepInto(ir, type, key) {
|
|
67
|
+
const resolved = deref(ir, type);
|
|
68
|
+
if (resolved.kind !== "object") return null;
|
|
69
|
+
const field = resolved.fields[key];
|
|
70
|
+
return field ? field.type : null;
|
|
71
|
+
}
|
|
72
|
+
function membersOf(ir, type) {
|
|
73
|
+
const resolved = deref(ir, type);
|
|
74
|
+
if (resolved.kind !== "object") return [];
|
|
75
|
+
return Object.entries(resolved.fields).map(([name, field]) => {
|
|
76
|
+
return {
|
|
77
|
+
name,
|
|
78
|
+
type: field.type,
|
|
79
|
+
required: field.required,
|
|
80
|
+
nullable: field.nullable,
|
|
81
|
+
doc: field.doc
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const REF_PREFIXES = ["#/$defs/", "#/definitions/"];
|
|
86
|
+
function fromJsonSchema(schema) {
|
|
87
|
+
if (!isObject(schema)) return {
|
|
88
|
+
root: ANY,
|
|
89
|
+
defs: {}
|
|
90
|
+
};
|
|
91
|
+
const context = {
|
|
92
|
+
raw: {
|
|
93
|
+
...asObject(schema.definitions),
|
|
94
|
+
...asObject(schema.$defs)
|
|
95
|
+
},
|
|
96
|
+
defs: Object.create(null),
|
|
97
|
+
resolving: /* @__PURE__ */ new Set(),
|
|
98
|
+
tainted: /* @__PURE__ */ new Set(),
|
|
99
|
+
cyclic: /* @__PURE__ */ new Set(),
|
|
100
|
+
dependents: /* @__PURE__ */ new Map(),
|
|
101
|
+
converting: []
|
|
102
|
+
};
|
|
103
|
+
for (const name of Object.keys(context.raw)) defType(name, context);
|
|
104
|
+
if (context.tainted.size > 0) {
|
|
105
|
+
const queue = [...context.tainted];
|
|
106
|
+
const pending = new Set(queue);
|
|
107
|
+
let head = 0;
|
|
108
|
+
let budget = (Object.keys(context.raw).length + 1) ** 2;
|
|
109
|
+
while (head < queue.length && budget > 0) {
|
|
110
|
+
budget -= 1;
|
|
111
|
+
const name = queue[head];
|
|
112
|
+
head += 1;
|
|
113
|
+
if (name === void 0) break;
|
|
114
|
+
pending.delete(name);
|
|
115
|
+
context.converting.push(name);
|
|
116
|
+
const next = convert(context.raw[name], context);
|
|
117
|
+
context.converting.pop();
|
|
118
|
+
if (JSON.stringify(next) !== JSON.stringify(context.defs[name])) {
|
|
119
|
+
context.defs[name] = next;
|
|
120
|
+
const stale = [...context.dependents.get(name) ?? []].filter((reader) => !pending.has(reader));
|
|
121
|
+
for (const dependent of stale) {
|
|
122
|
+
pending.add(dependent);
|
|
123
|
+
queue.push(dependent);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
root: convert(schema, context),
|
|
130
|
+
defs: context.defs
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function defType(name, context) {
|
|
134
|
+
const reader = context.converting.at(-1);
|
|
135
|
+
if (reader !== void 0 && reader !== name) {
|
|
136
|
+
const readers = context.dependents.get(name);
|
|
137
|
+
if (readers) readers.add(reader);
|
|
138
|
+
else context.dependents.set(name, /* @__PURE__ */ new Set([reader]));
|
|
139
|
+
}
|
|
140
|
+
if (Object.hasOwn(context.defs, name)) return context.defs[name] ?? null;
|
|
141
|
+
if (context.resolving.has(name)) {
|
|
142
|
+
for (const pending of context.converting) context.tainted.add(pending);
|
|
143
|
+
const cycleStart = context.converting.lastIndexOf(name);
|
|
144
|
+
if (cycleStart === -1) context.cyclic.add(name);
|
|
145
|
+
else for (const member of context.converting.slice(cycleStart)) context.cyclic.add(member);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
if (!Object.hasOwn(context.raw, name)) return null;
|
|
149
|
+
context.resolving.add(name);
|
|
150
|
+
context.converting.push(name);
|
|
151
|
+
const type = convert(context.raw[name], context);
|
|
152
|
+
context.converting.pop();
|
|
153
|
+
context.resolving.delete(name);
|
|
154
|
+
context.defs[name] = type;
|
|
155
|
+
return type;
|
|
156
|
+
}
|
|
157
|
+
function convert(node, context) {
|
|
158
|
+
if (typeof node === "boolean" || !isObject(node)) return ANY;
|
|
159
|
+
const ref = refName(node.$ref);
|
|
160
|
+
if (ref !== null) return {
|
|
161
|
+
kind: "ref",
|
|
162
|
+
name: ref
|
|
163
|
+
};
|
|
164
|
+
if (Array.isArray(node.allOf)) {
|
|
165
|
+
const branches = node.allOf.map((entry) => convert(entry, context));
|
|
166
|
+
return requireFields(mergeAll(isObject(node.properties) ? [...branches, objectType(node, context)] : branches, context, "all"), node.required, context);
|
|
167
|
+
}
|
|
168
|
+
const union = node.anyOf ?? node.oneOf;
|
|
169
|
+
if (Array.isArray(union)) {
|
|
170
|
+
const viable = union.filter((branch) => unionBranchKind(branch, context) !== "impossible");
|
|
171
|
+
const nonNull = viable.filter((branch) => unionBranchKind(branch, context) !== "null");
|
|
172
|
+
if (viable.length === 0) return ANY;
|
|
173
|
+
if (nonNull.length === 0) return NULL;
|
|
174
|
+
const merged = nonNull.length === 1 ? convert(nonNull[0], context) : mergeAll(nonNull.map((entry) => convert(entry, context)), context, "any");
|
|
175
|
+
return requireFields(isObject(node.properties) ? mergeAll([merged, objectType(node, context)], context, "all") : merged, node.required, context);
|
|
176
|
+
}
|
|
177
|
+
if (Array.isArray(node.enum)) return {
|
|
178
|
+
kind: "enum",
|
|
179
|
+
values: node.enum.filter(isLiteral)
|
|
180
|
+
};
|
|
181
|
+
if ("const" in node && isLiteral(node.const)) return {
|
|
182
|
+
kind: "enum",
|
|
183
|
+
values: [node.const]
|
|
184
|
+
};
|
|
185
|
+
const type = primaryType(node);
|
|
186
|
+
if (isObjectShape(node)) return objectType(node, context);
|
|
187
|
+
if (type === "array") return {
|
|
188
|
+
kind: "array",
|
|
189
|
+
element: convert(Array.isArray(node.items) ? node.items[0] : node.items, context)
|
|
190
|
+
};
|
|
191
|
+
if (type === "string") return { kind: "string" };
|
|
192
|
+
if (type === "integer" || type === "number") return { kind: "number" };
|
|
193
|
+
if (type === "boolean") return { kind: "boolean" };
|
|
194
|
+
if (type === "null") return NULL;
|
|
195
|
+
return ANY;
|
|
196
|
+
}
|
|
197
|
+
function isObjectShape(node) {
|
|
198
|
+
return primaryType(node) === "object" || isObject(node.properties);
|
|
199
|
+
}
|
|
200
|
+
function objectType(node, context) {
|
|
201
|
+
const properties = asObject(node.properties);
|
|
202
|
+
const required = new Set(Array.isArray(node.required) ? node.required.filter((name) => typeof name === "string") : []);
|
|
203
|
+
const fields = Object.create(null);
|
|
204
|
+
for (const [name, sub] of Object.entries(properties)) fields[name] = {
|
|
205
|
+
type: convert(sub, context),
|
|
206
|
+
required: required.has(name),
|
|
207
|
+
nullable: isNullable(sub, context),
|
|
208
|
+
doc: description(sub)
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
kind: "object",
|
|
212
|
+
fields
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function mergeAll(types, context, mode) {
|
|
216
|
+
const [only] = types;
|
|
217
|
+
if (only && types.length === 1) return only;
|
|
218
|
+
const fields = Object.create(null);
|
|
219
|
+
const appearances = /* @__PURE__ */ new Map();
|
|
220
|
+
let branches = 0;
|
|
221
|
+
for (const type of types) {
|
|
222
|
+
const resolved = derefFully(type, context);
|
|
223
|
+
if (resolved?.kind === "object") {
|
|
224
|
+
branches += 1;
|
|
225
|
+
for (const [name, field] of Object.entries(resolved.fields)) {
|
|
226
|
+
const existing = fields[name];
|
|
227
|
+
fields[name] = existing ? mergeField(existing, field, mode) : field;
|
|
228
|
+
appearances.set(name, (appearances.get(name) ?? 0) + 1);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (branches > 0) {
|
|
233
|
+
if (mode === "any") {
|
|
234
|
+
for (const [name, field] of Object.entries(fields)) if (field.required && appearances.get(name) !== types.length) fields[name] = {
|
|
235
|
+
...field,
|
|
236
|
+
required: false
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
kind: "object",
|
|
241
|
+
fields
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return types.find((type) => type.kind !== "any") ?? ANY;
|
|
245
|
+
}
|
|
246
|
+
function requireFields(type, required, context) {
|
|
247
|
+
const names = Array.isArray(required) ? required.filter((name) => typeof name === "string") : [];
|
|
248
|
+
if (names.length === 0) return type;
|
|
249
|
+
const resolved = type.kind === "ref" ? derefNonCyclic(type, context) : type;
|
|
250
|
+
if (resolved === null || resolved.kind !== "object") return type;
|
|
251
|
+
const fields = Object.assign(Object.create(null), resolved.fields);
|
|
252
|
+
let changed = false;
|
|
253
|
+
for (const name of names) {
|
|
254
|
+
const field = fields[name];
|
|
255
|
+
if (field && !field.required) {
|
|
256
|
+
fields[name] = {
|
|
257
|
+
...field,
|
|
258
|
+
required: true
|
|
259
|
+
};
|
|
260
|
+
changed = true;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return changed ? {
|
|
264
|
+
kind: "object",
|
|
265
|
+
fields
|
|
266
|
+
} : type;
|
|
267
|
+
}
|
|
268
|
+
function mergeField(existing, incoming, mode) {
|
|
269
|
+
return {
|
|
270
|
+
type: incoming.type,
|
|
271
|
+
required: mode === "all" ? existing.required || incoming.required : existing.required && incoming.required,
|
|
272
|
+
nullable: mode === "all" ? existing.nullable && incoming.nullable : existing.nullable || incoming.nullable,
|
|
273
|
+
doc: incoming.doc ?? existing.doc
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function derefFully(type, context) {
|
|
277
|
+
return followRefs(type, (name) => defType(name, context));
|
|
278
|
+
}
|
|
279
|
+
function derefNonCyclic(type, context) {
|
|
280
|
+
let current = type;
|
|
281
|
+
for (let i = 0; i < MAX_DEREF && current.kind === "ref"; i++) {
|
|
282
|
+
if (context.cyclic.has(current.name)) return null;
|
|
283
|
+
const next = defType(current.name, context);
|
|
284
|
+
if (!next) return null;
|
|
285
|
+
current = next;
|
|
286
|
+
}
|
|
287
|
+
return current.kind === "ref" ? null : current;
|
|
288
|
+
}
|
|
289
|
+
function fromSample(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
290
|
+
if (value === null || value === void 0) return ANY;
|
|
291
|
+
if (typeof value === "object") {
|
|
292
|
+
if (ancestors.has(value)) return ANY;
|
|
293
|
+
ancestors.add(value);
|
|
294
|
+
const type = Array.isArray(value) ? sampleArrayType(value, ancestors) : sampleObjectType(value, ancestors);
|
|
295
|
+
ancestors.delete(value);
|
|
296
|
+
return type;
|
|
297
|
+
}
|
|
298
|
+
if (typeof value === "string") return { kind: "string" };
|
|
299
|
+
if (typeof value === "number") return { kind: "number" };
|
|
300
|
+
if (typeof value === "boolean") return { kind: "boolean" };
|
|
301
|
+
return ANY;
|
|
302
|
+
}
|
|
303
|
+
function sampleArrayType(value, ancestors) {
|
|
304
|
+
return {
|
|
305
|
+
kind: "array",
|
|
306
|
+
element: value.length > 0 ? fromSample(value[0], ancestors) : ANY
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function sampleObjectType(value, ancestors) {
|
|
310
|
+
const fields = Object.create(null);
|
|
311
|
+
for (const [name, member] of Object.entries(value)) fields[name] = {
|
|
312
|
+
type: fromSample(member, ancestors),
|
|
313
|
+
required: true,
|
|
314
|
+
nullable: member === null
|
|
315
|
+
};
|
|
316
|
+
return {
|
|
317
|
+
kind: "object",
|
|
318
|
+
fields
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function refName(ref) {
|
|
322
|
+
if (typeof ref !== "string") return null;
|
|
323
|
+
for (const prefix of REF_PREFIXES) if (ref.startsWith(prefix)) return decodeURIComponent(ref.slice(prefix.length));
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
function primaryType(node) {
|
|
327
|
+
const { type } = node;
|
|
328
|
+
if (typeof type === "string") return type;
|
|
329
|
+
if (Array.isArray(type)) {
|
|
330
|
+
const nonNull = type.find((entry) => typeof entry === "string" && entry !== "null");
|
|
331
|
+
if (typeof nonNull === "string") return nonNull;
|
|
332
|
+
return type.includes("null") ? "null" : null;
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
function isNullable(node, context) {
|
|
337
|
+
const pending = [node];
|
|
338
|
+
const seenRefs = /* @__PURE__ */ new Set();
|
|
339
|
+
while (pending.length > 0) {
|
|
340
|
+
const current = pending.pop();
|
|
341
|
+
if (!isObject(current)) continue;
|
|
342
|
+
const { type } = current;
|
|
343
|
+
if (type === "null" || Array.isArray(type) && type.includes("null")) return true;
|
|
344
|
+
const ref = refName(current.$ref);
|
|
345
|
+
if (ref !== null && !seenRefs.has(ref) && Object.hasOwn(context.raw, ref)) {
|
|
346
|
+
seenRefs.add(ref);
|
|
347
|
+
pending.push(context.raw[ref]);
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
const union = current.anyOf ?? current.oneOf;
|
|
351
|
+
if (Array.isArray(union)) pending.push(...union);
|
|
352
|
+
}
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
function unionBranchKind(branch, context) {
|
|
356
|
+
const seenRefs = /* @__PURE__ */ new Set();
|
|
357
|
+
let current = branch;
|
|
358
|
+
for (let i = 0; i < MAX_DEREF; i++) {
|
|
359
|
+
if (current === false) return "impossible";
|
|
360
|
+
if (!isObject(current)) return "other";
|
|
361
|
+
if (current.type === "null" || Array.isArray(current.type) && current.type.length === 1 && current.type[0] === "null") return "null";
|
|
362
|
+
const ref = refName(current.$ref);
|
|
363
|
+
if (ref === null || seenRefs.has(ref) || !Object.hasOwn(context.raw, ref)) return "other";
|
|
364
|
+
seenRefs.add(ref);
|
|
365
|
+
current = context.raw[ref];
|
|
366
|
+
}
|
|
367
|
+
return "other";
|
|
368
|
+
}
|
|
369
|
+
function description(node) {
|
|
370
|
+
return isObject(node) && typeof node.description === "string" ? node.description : void 0;
|
|
371
|
+
}
|
|
372
|
+
function isObject(value) {
|
|
373
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
374
|
+
}
|
|
375
|
+
function asObject(value) {
|
|
376
|
+
return isObject(value) ? value : {};
|
|
377
|
+
}
|
|
378
|
+
function isLiteral(value) {
|
|
379
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
380
|
+
}
|
|
381
|
+
//#endregion
|
|
382
|
+
export { setMinijinjaContext as a, resolveMembers as i, minijinjaContextField as n, typeAtPath as o, normalizeContext as r, elementType as t };
|
package/dist/index.d.ts
CHANGED
|
@@ -84,9 +84,12 @@ declare function normalizeContext(input: EditorContext): IrSchema;
|
|
|
84
84
|
*/
|
|
85
85
|
declare function resolveMembers(ir: IrSchema, path: readonly string[], scope?: ReadonlyMap<string, IrType>): Member[];
|
|
86
86
|
/**
|
|
87
|
-
* The type at a declared property path, or null if the path leaves the walkable shape.
|
|
87
|
+
* The type at a declared property path, or null if the path leaves the walkable shape. As in
|
|
88
|
+
* {@link resolveMembers}, the first segment may resolve against a `scope` binding before falling
|
|
89
|
+
* back to the root — so a nested `{% for child in parent.children %}` can walk the `parent` its
|
|
90
|
+
* enclosing loop bound.
|
|
88
91
|
*/
|
|
89
|
-
declare function typeAtPath(ir: IrSchema, path: readonly string[]): IrType | null;
|
|
92
|
+
declare function typeAtPath(ir: IrSchema, path: readonly string[], scope?: ReadonlyMap<string, IrType>): IrType | null;
|
|
90
93
|
//#endregion
|
|
91
94
|
//#region src/controlled-host.d.ts
|
|
92
95
|
interface ControlledEditorOptions {
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as setMinijinjaContext, i as resolveMembers, n as minijinjaContextField, o as typeAtPath, r as normalizeContext } from "./context-
|
|
1
|
+
import { a as setMinijinjaContext, i as resolveMembers, n as minijinjaContextField, o as typeAtPath, r as normalizeContext } from "./context-D86o2jK3.js";
|
|
2
2
|
import { Compartment, EditorState } from "@codemirror/state";
|
|
3
3
|
import { EditorView, drawSelection, highlightActiveLine, keymap, lineNumbers, placeholder } from "@codemirror/view";
|
|
4
4
|
import { autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap } from "@codemirror/autocomplete";
|
|
@@ -69,6 +69,8 @@ const searchPhrasesZhCn = EditorState.phrases.of({
|
|
|
69
69
|
close: "关闭",
|
|
70
70
|
"current match": "当前匹配",
|
|
71
71
|
"on line": "所在行",
|
|
72
|
+
"replaced $ matches": "已替换 $ 处匹配",
|
|
73
|
+
"replaced match on line $": "已替换第 $ 行的匹配",
|
|
72
74
|
"Go to line": "跳转到行",
|
|
73
75
|
go: "跳转"
|
|
74
76
|
});
|
|
@@ -262,35 +264,35 @@ const loaders = {
|
|
|
262
264
|
xu: legacy(() => import("@codemirror/legacy-modes/mode/mscgen").then((mod) => mod.xu)),
|
|
263
265
|
yacas: legacy(() => import("@codemirror/legacy-modes/mode/yacas").then((mod) => mod.yacas)),
|
|
264
266
|
z80: legacy(() => import("@codemirror/legacy-modes/mode/z80").then((mod) => mod.z80)),
|
|
265
|
-
minijinja: () => import("./minijinja-
|
|
266
|
-
"minijinja-html": () => Promise.all([import("./minijinja-
|
|
267
|
-
"minijinja-xml": () => Promise.all([import("./minijinja-
|
|
268
|
-
"minijinja-json": () => Promise.all([import("./minijinja-
|
|
269
|
-
"minijinja-yaml": () => Promise.all([import("./minijinja-
|
|
270
|
-
"minijinja-css": () => Promise.all([import("./minijinja-
|
|
271
|
-
"minijinja-scss": () => Promise.all([import("./minijinja-
|
|
272
|
-
"minijinja-sass": () => Promise.all([import("./minijinja-
|
|
273
|
-
"minijinja-less": () => Promise.all([import("./minijinja-
|
|
274
|
-
"minijinja-markdown": () => Promise.all([import("./minijinja-
|
|
275
|
-
"minijinja-sql": () => Promise.all([import("./minijinja-
|
|
276
|
-
"minijinja-mysql": () => Promise.all([import("./minijinja-
|
|
277
|
-
"minijinja-pgsql": () => Promise.all([import("./minijinja-
|
|
278
|
-
"minijinja-sqlite": () => Promise.all([import("./minijinja-
|
|
279
|
-
"minijinja-mssql": () => Promise.all([import("./minijinja-
|
|
280
|
-
"minijinja-mariadb": () => Promise.all([import("./minijinja-
|
|
281
|
-
"minijinja-plsql": () => Promise.all([import("./minijinja-
|
|
282
|
-
"minijinja-cassandra": () => Promise.all([import("./minijinja-
|
|
283
|
-
"minijinja-hive": () => Promise.all([import("./minijinja-
|
|
284
|
-
"minijinja-sparksql": () => Promise.all([import("./minijinja-
|
|
285
|
-
"minijinja-gql": () => Promise.all([import("./minijinja-
|
|
286
|
-
"minijinja-gpsql": () => Promise.all([import("./minijinja-
|
|
287
|
-
"minijinja-esper": () => Promise.all([import("./minijinja-
|
|
288
|
-
"minijinja-toml": () => Promise.all([import("./minijinja-
|
|
289
|
-
"minijinja-ini": () => Promise.all([import("./minijinja-
|
|
290
|
-
"minijinja-env": () => Promise.all([import("./minijinja-
|
|
291
|
-
"minijinja-shell": () => Promise.all([import("./minijinja-
|
|
292
|
-
"minijinja-dockerfile": () => Promise.all([import("./minijinja-
|
|
293
|
-
"minijinja-nginx": () => Promise.all([import("./minijinja-
|
|
267
|
+
minijinja: () => import("./minijinja-D_5erBa7.js").then((mod) => mod.minijinja()),
|
|
268
|
+
"minijinja-html": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-html")]).then(([mj, mod]) => mj.minijinja({ base: mod.html() })),
|
|
269
|
+
"minijinja-xml": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-xml")]).then(([mj, mod]) => mj.minijinja({ base: mod.xml() })),
|
|
270
|
+
"minijinja-json": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-json")]).then(([mj, mod]) => mj.minijinja({ base: mod.json() })),
|
|
271
|
+
"minijinja-yaml": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-yaml")]).then(([mj, mod]) => mj.minijinja({ base: mod.yaml() })),
|
|
272
|
+
"minijinja-css": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-css")]).then(([mj, mod]) => mj.minijinja({ base: mod.css() })),
|
|
273
|
+
"minijinja-scss": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-sass")]).then(([mj, mod]) => mj.minijinja({ base: mod.sass() })),
|
|
274
|
+
"minijinja-sass": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-sass")]).then(([mj, mod]) => mj.minijinja({ base: mod.sass({ indented: true }) })),
|
|
275
|
+
"minijinja-less": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-less")]).then(([mj, mod]) => mj.minijinja({ base: mod.less() })),
|
|
276
|
+
"minijinja-markdown": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-markdown")]).then(([mj, mod]) => mj.minijinja({ base: mod.markdown() })),
|
|
277
|
+
"minijinja-sql": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-sql")]).then(([mj, mod]) => mj.minijinja({ base: mod.sql() })),
|
|
278
|
+
"minijinja-mysql": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-sql")]).then(([mj, mod]) => mj.minijinja({ base: mod.sql({ dialect: mod.MySQL }) })),
|
|
279
|
+
"minijinja-pgsql": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-sql")]).then(([mj, mod]) => mj.minijinja({ base: mod.sql({ dialect: mod.PostgreSQL }) })),
|
|
280
|
+
"minijinja-sqlite": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-sql")]).then(([mj, mod]) => mj.minijinja({ base: mod.sql({ dialect: mod.SQLite }) })),
|
|
281
|
+
"minijinja-mssql": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-sql")]).then(([mj, mod]) => mj.minijinja({ base: mod.sql({ dialect: mod.MSSQL }) })),
|
|
282
|
+
"minijinja-mariadb": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-sql")]).then(([mj, mod]) => mj.minijinja({ base: mod.sql({ dialect: mod.MariaSQL }) })),
|
|
283
|
+
"minijinja-plsql": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-sql")]).then(([mj, mod]) => mj.minijinja({ base: mod.sql({ dialect: mod.PLSQL }) })),
|
|
284
|
+
"minijinja-cassandra": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/lang-sql")]).then(([mj, mod]) => mj.minijinja({ base: mod.sql({ dialect: mod.Cassandra }) })),
|
|
285
|
+
"minijinja-hive": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/sql")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.hive)) })),
|
|
286
|
+
"minijinja-sparksql": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/sql")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.sparkSQL)) })),
|
|
287
|
+
"minijinja-gql": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/sql")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.gql)) })),
|
|
288
|
+
"minijinja-gpsql": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/sql")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.gpSQL)) })),
|
|
289
|
+
"minijinja-esper": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/sql")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.esper)) })),
|
|
290
|
+
"minijinja-toml": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/toml")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.toml)) })),
|
|
291
|
+
"minijinja-ini": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/properties")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.properties)) })),
|
|
292
|
+
"minijinja-env": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/properties")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.properties)) })),
|
|
293
|
+
"minijinja-shell": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/shell")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.shell)) })),
|
|
294
|
+
"minijinja-dockerfile": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/dockerfile")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.dockerFile)) })),
|
|
295
|
+
"minijinja-nginx": () => Promise.all([import("./minijinja-D_5erBa7.js"), import("@codemirror/legacy-modes/mode/nginx")]).then(([mj, mod]) => mj.minijinja({ base: new LanguageSupport(StreamLanguage.define(mod.nginx)) }))
|
|
294
296
|
};
|
|
295
297
|
const codeMirrorLanguages = Object.keys(loaders).toSorted();
|
|
296
298
|
function loadLanguage(language) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as resolveMembers, n as minijinjaContextField, o as typeAtPath, r as normalizeContext, t as elementType } from "./context-
|
|
1
|
+
import { i as resolveMembers, n as minijinjaContextField, o as typeAtPath, r as normalizeContext, t as elementType } from "./context-D86o2jK3.js";
|
|
2
2
|
import { LanguageSupport, syntaxTree } from "@codemirror/language";
|
|
3
3
|
import { jinja, jinjaLanguage } from "@codemirror/lang-jinja";
|
|
4
4
|
import { linter } from "@codemirror/lint";
|
|
@@ -219,51 +219,110 @@ function loopField(kind) {
|
|
|
219
219
|
nullable: false
|
|
220
220
|
};
|
|
221
221
|
}
|
|
222
|
+
const ROOT_SCOPE = -1;
|
|
222
223
|
function scopeBindings(state, pos, ir) {
|
|
223
224
|
const bindings = /* @__PURE__ */ new Map();
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
+
}
|
|
227
240
|
return bindings;
|
|
228
241
|
}
|
|
229
242
|
function collectForBindings(state, forNode, pos, ir, bindings) {
|
|
230
243
|
const tag = forNode.firstChild;
|
|
231
|
-
if (!tag || tag
|
|
244
|
+
if (!tag || !tagBodyStarted(tag, pos)) return;
|
|
232
245
|
const definitions = [];
|
|
233
246
|
let iterable = null;
|
|
234
247
|
let seenIn = false;
|
|
235
248
|
for (let child = tag.firstChild; child; child = child.nextSibling) if (child.name === "in") seenIn = true;
|
|
236
249
|
else if (!seenIn && child.name === "Definition") definitions.push(child);
|
|
237
250
|
else if (seenIn && !iterable && child.name !== "%}") iterable = child;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (only && definitions.length === 1 && iterable) {
|
|
251
|
+
let element = null;
|
|
252
|
+
if (definitions.length === 1 && iterable) {
|
|
241
253
|
const path = walkBase(state, iterable);
|
|
242
|
-
const iterableType = path ? typeAtPath(ir, path) : null;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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 = [];
|
|
246
279
|
}
|
|
247
|
-
for (const definition of definitions) bindings.set(state.sliceDoc(definition.from, definition.to), { kind: "any" });
|
|
248
280
|
}
|
|
249
281
|
function collectMacroBindings(state, macroNode, pos, bindings) {
|
|
250
282
|
const tag = macroNode.firstChild;
|
|
251
|
-
if (!tag || tag
|
|
283
|
+
if (!tag || !tagBodyStarted(tag, pos)) return;
|
|
252
284
|
const paramList = tag.getChild("ParamList");
|
|
253
285
|
if (!paramList) return;
|
|
254
286
|
for (let child = paramList.firstChild; child; child = child.nextSibling) if (child.name === "Definition") bindings.set(state.sliceDoc(child.from, child.to), { kind: "any" });
|
|
255
287
|
}
|
|
256
|
-
function
|
|
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();
|
|
257
294
|
syntaxTree(state).iterate({
|
|
258
295
|
to: pos,
|
|
259
296
|
enter: (node) => {
|
|
260
297
|
if (node.name !== "Tag" || node.to > pos) return;
|
|
261
298
|
const keyword = node.node.firstChild?.nextSibling;
|
|
262
299
|
if (!keyword || state.sliceDoc(keyword.from, keyword.to) !== "set") return;
|
|
263
|
-
const
|
|
264
|
-
|
|
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);
|
|
265
323
|
}
|
|
266
324
|
});
|
|
325
|
+
return sets;
|
|
267
326
|
}
|
|
268
327
|
function jinjaContext(state, pos, side) {
|
|
269
328
|
for (let node = syntaxTree(state).resolveInner(pos, side); node; node = node.parent) {
|
|
@@ -279,7 +338,11 @@ function minijinjaCompletion(context) {
|
|
|
279
338
|
if (!word) return null;
|
|
280
339
|
const { from } = word;
|
|
281
340
|
const before = context.state.sliceDoc(Math.max(0, from - 24), from);
|
|
282
|
-
|
|
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
|
+
}
|
|
283
346
|
if (jinjaContext(context.state, context.pos, side) !== "expr") return null;
|
|
284
347
|
if (/\{%[-+]?\s*$/.test(before)) return {
|
|
285
348
|
from,
|