@coldsmirk/inkstone-codemirror 0.8.2

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,357 @@
1
+ const require_context = require("./context-BDibyUwD.cjs");
2
+ let _codemirror_language = require("@codemirror/language");
3
+ let _codemirror_lang_jinja = require("@codemirror/lang-jinja");
4
+ let _codemirror_lint = require("@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 = (0, _codemirror_language.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
+ function scopeBindings(state, pos, ir) {
223
+ const bindings = /* @__PURE__ */ new Map();
224
+ for (let node = (0, _codemirror_language.syntaxTree)(state).resolveInner(pos, -1); node; node = node.parent) if (node.name === "ForStatement") collectForBindings(state, node, pos, ir, bindings);
225
+ else if (node.name === "MacroStatement") collectMacroBindings(state, node, pos, bindings);
226
+ collectSetBindings(state, pos, bindings);
227
+ return bindings;
228
+ }
229
+ function collectForBindings(state, forNode, pos, ir, bindings) {
230
+ const tag = forNode.firstChild;
231
+ if (!tag || tag.name !== "Tag" || pos < tag.to) return;
232
+ const definitions = [];
233
+ let iterable = null;
234
+ let seenIn = false;
235
+ for (let child = tag.firstChild; child; child = child.nextSibling) if (child.name === "in") seenIn = true;
236
+ else if (!seenIn && child.name === "Definition") definitions.push(child);
237
+ else if (seenIn && !iterable && child.name !== "%}") iterable = child;
238
+ if (!bindings.has("loop")) bindings.set("loop", LOOP_TYPE);
239
+ const [only] = definitions;
240
+ if (only && definitions.length === 1 && iterable) {
241
+ const path = walkBase(state, iterable);
242
+ const iterableType = path ? require_context.typeAtPath(ir, path) : null;
243
+ const element = iterableType ? require_context.elementType(ir, iterableType) : null;
244
+ bindings.set(state.sliceDoc(only.from, only.to), element ?? { kind: "any" });
245
+ return;
246
+ }
247
+ for (const definition of definitions) bindings.set(state.sliceDoc(definition.from, definition.to), { kind: "any" });
248
+ }
249
+ function collectMacroBindings(state, macroNode, pos, bindings) {
250
+ const tag = macroNode.firstChild;
251
+ if (!tag || tag.name !== "Tag" || pos < tag.to) return;
252
+ const paramList = tag.getChild("ParamList");
253
+ if (!paramList) return;
254
+ for (let child = paramList.firstChild; child; child = child.nextSibling) if (child.name === "Definition") bindings.set(state.sliceDoc(child.from, child.to), { kind: "any" });
255
+ }
256
+ function collectSetBindings(state, pos, bindings) {
257
+ (0, _codemirror_language.syntaxTree)(state).iterate({
258
+ to: pos,
259
+ enter: (node) => {
260
+ if (node.name !== "Tag" || node.to > pos) return;
261
+ const keyword = node.node.firstChild?.nextSibling;
262
+ if (!keyword || state.sliceDoc(keyword.from, keyword.to) !== "set") return;
263
+ const definition = keyword.nextSibling;
264
+ if (definition && definition.name === "Definition") bindings.set(state.sliceDoc(definition.from, definition.to), { kind: "any" });
265
+ }
266
+ });
267
+ }
268
+ function jinjaContext(state, pos, side) {
269
+ for (let node = (0, _codemirror_language.syntaxTree)(state).resolveInner(pos, side); node; node = node.parent) {
270
+ const { name } = node;
271
+ if (name === "Comment") return "comment";
272
+ if (name === "StringLiteral" || name === "NumberLiteral") return null;
273
+ if (name === "Interpolation" || name === "Tag" || name === "EndTag") return "expr";
274
+ }
275
+ return null;
276
+ }
277
+ function minijinjaCompletion(context) {
278
+ const word = context.matchBefore(/\w*/);
279
+ if (!word) return null;
280
+ const { from } = word;
281
+ const before = context.state.sliceDoc(Math.max(0, from - 24), from);
282
+ const side = from === context.pos ? 1 : -1;
283
+ if (jinjaContext(context.state, context.pos, side) !== "expr") return null;
284
+ if (/\{%[-+]?\s*$/.test(before)) return {
285
+ from,
286
+ options: tagCompletions,
287
+ validFor: /^\w*$/
288
+ };
289
+ if (/\|\s*$/.test(before)) return {
290
+ from,
291
+ options: filterCompletions,
292
+ validFor: /^\w*$/
293
+ };
294
+ if (/\bis\s+(?:not\s+)?$/.test(before)) return {
295
+ from,
296
+ options: testCompletions,
297
+ validFor: /^\w*$/
298
+ };
299
+ const ir = context.state.field(require_context.minijinjaContextField, false);
300
+ if (ir) {
301
+ const target = contextTarget(context.state, context.pos);
302
+ if (!target) return null;
303
+ const scope = scopeBindings(context.state, context.pos, ir);
304
+ const members = require_context.resolveMembers(ir, target.path, scope);
305
+ if (target.path.length > 0) return members.length > 0 ? {
306
+ from: target.from,
307
+ options: memberCompletions(members, "property"),
308
+ validFor: /^\w*$/
309
+ } : null;
310
+ if (target.from === context.pos && !context.explicit) return null;
311
+ return {
312
+ from: target.from,
313
+ options: [...memberCompletions(members, "variable"), ...functionCompletions],
314
+ validFor: /^\w*$/
315
+ };
316
+ }
317
+ if (from === context.pos && !context.explicit) return null;
318
+ return {
319
+ from,
320
+ options: functionCompletions,
321
+ validFor: /^\w*$/
322
+ };
323
+ }
324
+ function minijinjaDiagnostics(state) {
325
+ const diagnostics = [];
326
+ const { doc } = state;
327
+ (0, _codemirror_language.syntaxTree)(state).iterate({ enter: (node) => {
328
+ if (node.name !== "Tag" && node.name !== "EndTag") return;
329
+ const keywordNode = node.node.firstChild?.nextSibling;
330
+ if (!keywordNode || keywordNode.type.isError) return;
331
+ const keyword = doc.sliceString(keywordNode.from, keywordNode.to);
332
+ if (!MINIJINJA_TAGS.has(keyword)) diagnostics.push({
333
+ from: keywordNode.from,
334
+ to: keywordNode.to,
335
+ severity: "error",
336
+ message: `"${keyword}" is not a MiniJinja tag.`
337
+ });
338
+ } });
339
+ return diagnostics;
340
+ }
341
+ const minijinjaLinter = (0, _codemirror_lint.linter)((view) => minijinjaDiagnostics(view.state));
342
+ function minijinja(config = {}) {
343
+ const { base, context } = config;
344
+ const language = base ? (0, _codemirror_lang_jinja.jinja)({ base }).language : _codemirror_lang_jinja.jinjaLanguage;
345
+ const contextField = context ? require_context.minijinjaContextField.init(() => require_context.normalizeContext(context)) : require_context.minijinjaContextField;
346
+ const support = [
347
+ language.data.of({ autocomplete: minijinjaCompletion }),
348
+ minijinjaLinter,
349
+ contextField
350
+ ];
351
+ if (base) support.push(base.support);
352
+ return new _codemirror_language.LanguageSupport(language, support);
353
+ }
354
+ //#endregion
355
+ exports.minijinja = minijinja;
356
+ exports.minijinjaCompletion = minijinjaCompletion;
357
+ exports.minijinjaDiagnostics = minijinjaDiagnostics;
@@ -0,0 +1,355 @@
1
+ import { i as resolveMembers, n as minijinjaContextField, o as typeAtPath, r as normalizeContext, t as elementType } from "./context-Dx6bPnKq.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
+ function scopeBindings(state, pos, ir) {
223
+ const bindings = /* @__PURE__ */ new Map();
224
+ for (let node = syntaxTree(state).resolveInner(pos, -1); node; node = node.parent) if (node.name === "ForStatement") collectForBindings(state, node, pos, ir, bindings);
225
+ else if (node.name === "MacroStatement") collectMacroBindings(state, node, pos, bindings);
226
+ collectSetBindings(state, pos, bindings);
227
+ return bindings;
228
+ }
229
+ function collectForBindings(state, forNode, pos, ir, bindings) {
230
+ const tag = forNode.firstChild;
231
+ if (!tag || tag.name !== "Tag" || pos < tag.to) return;
232
+ const definitions = [];
233
+ let iterable = null;
234
+ let seenIn = false;
235
+ for (let child = tag.firstChild; child; child = child.nextSibling) if (child.name === "in") seenIn = true;
236
+ else if (!seenIn && child.name === "Definition") definitions.push(child);
237
+ else if (seenIn && !iterable && child.name !== "%}") iterable = child;
238
+ if (!bindings.has("loop")) bindings.set("loop", LOOP_TYPE);
239
+ const [only] = definitions;
240
+ if (only && definitions.length === 1 && iterable) {
241
+ const path = walkBase(state, iterable);
242
+ const iterableType = path ? typeAtPath(ir, path) : null;
243
+ const element = iterableType ? elementType(ir, iterableType) : null;
244
+ bindings.set(state.sliceDoc(only.from, only.to), element ?? { kind: "any" });
245
+ return;
246
+ }
247
+ for (const definition of definitions) bindings.set(state.sliceDoc(definition.from, definition.to), { kind: "any" });
248
+ }
249
+ function collectMacroBindings(state, macroNode, pos, bindings) {
250
+ const tag = macroNode.firstChild;
251
+ if (!tag || tag.name !== "Tag" || pos < tag.to) return;
252
+ const paramList = tag.getChild("ParamList");
253
+ if (!paramList) return;
254
+ for (let child = paramList.firstChild; child; child = child.nextSibling) if (child.name === "Definition") bindings.set(state.sliceDoc(child.from, child.to), { kind: "any" });
255
+ }
256
+ function collectSetBindings(state, pos, bindings) {
257
+ syntaxTree(state).iterate({
258
+ to: pos,
259
+ enter: (node) => {
260
+ if (node.name !== "Tag" || node.to > pos) return;
261
+ const keyword = node.node.firstChild?.nextSibling;
262
+ if (!keyword || state.sliceDoc(keyword.from, keyword.to) !== "set") return;
263
+ const definition = keyword.nextSibling;
264
+ if (definition && definition.name === "Definition") bindings.set(state.sliceDoc(definition.from, definition.to), { kind: "any" });
265
+ }
266
+ });
267
+ }
268
+ function jinjaContext(state, pos, side) {
269
+ for (let node = syntaxTree(state).resolveInner(pos, side); node; node = node.parent) {
270
+ const { name } = node;
271
+ if (name === "Comment") return "comment";
272
+ if (name === "StringLiteral" || name === "NumberLiteral") return null;
273
+ if (name === "Interpolation" || name === "Tag" || name === "EndTag") return "expr";
274
+ }
275
+ return null;
276
+ }
277
+ function minijinjaCompletion(context) {
278
+ const word = context.matchBefore(/\w*/);
279
+ if (!word) return null;
280
+ const { from } = word;
281
+ const before = context.state.sliceDoc(Math.max(0, from - 24), from);
282
+ const side = from === context.pos ? 1 : -1;
283
+ if (jinjaContext(context.state, context.pos, side) !== "expr") return null;
284
+ if (/\{%[-+]?\s*$/.test(before)) return {
285
+ from,
286
+ options: tagCompletions,
287
+ validFor: /^\w*$/
288
+ };
289
+ if (/\|\s*$/.test(before)) return {
290
+ from,
291
+ options: filterCompletions,
292
+ validFor: /^\w*$/
293
+ };
294
+ if (/\bis\s+(?:not\s+)?$/.test(before)) return {
295
+ from,
296
+ options: testCompletions,
297
+ validFor: /^\w*$/
298
+ };
299
+ const ir = context.state.field(minijinjaContextField, false);
300
+ if (ir) {
301
+ const target = contextTarget(context.state, context.pos);
302
+ if (!target) return null;
303
+ const scope = scopeBindings(context.state, context.pos, ir);
304
+ const members = resolveMembers(ir, target.path, scope);
305
+ if (target.path.length > 0) return members.length > 0 ? {
306
+ from: target.from,
307
+ options: memberCompletions(members, "property"),
308
+ validFor: /^\w*$/
309
+ } : null;
310
+ if (target.from === context.pos && !context.explicit) return null;
311
+ return {
312
+ from: target.from,
313
+ options: [...memberCompletions(members, "variable"), ...functionCompletions],
314
+ validFor: /^\w*$/
315
+ };
316
+ }
317
+ if (from === context.pos && !context.explicit) return null;
318
+ return {
319
+ from,
320
+ options: functionCompletions,
321
+ validFor: /^\w*$/
322
+ };
323
+ }
324
+ function minijinjaDiagnostics(state) {
325
+ const diagnostics = [];
326
+ const { doc } = state;
327
+ syntaxTree(state).iterate({ enter: (node) => {
328
+ if (node.name !== "Tag" && node.name !== "EndTag") return;
329
+ const keywordNode = node.node.firstChild?.nextSibling;
330
+ if (!keywordNode || keywordNode.type.isError) return;
331
+ const keyword = doc.sliceString(keywordNode.from, keywordNode.to);
332
+ if (!MINIJINJA_TAGS.has(keyword)) diagnostics.push({
333
+ from: keywordNode.from,
334
+ to: keywordNode.to,
335
+ severity: "error",
336
+ message: `"${keyword}" is not a MiniJinja tag.`
337
+ });
338
+ } });
339
+ return diagnostics;
340
+ }
341
+ const minijinjaLinter = linter((view) => minijinjaDiagnostics(view.state));
342
+ function minijinja(config = {}) {
343
+ const { base, context } = config;
344
+ const language = base ? jinja({ base }).language : jinjaLanguage;
345
+ const contextField = context ? minijinjaContextField.init(() => normalizeContext(context)) : minijinjaContextField;
346
+ const support = [
347
+ language.data.of({ autocomplete: minijinjaCompletion }),
348
+ minijinjaLinter,
349
+ contextField
350
+ ];
351
+ if (base) support.push(base.support);
352
+ return new LanguageSupport(language, support);
353
+ }
354
+ //#endregion
355
+ export { minijinja, minijinjaCompletion, minijinjaDiagnostics };