@monimesl/dot-cli 0.1.0-alpha.19 → 0.1.0-alpha.21
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/cli.js +381 -261
- package/dist/cli.js.map +1 -1
- package/dist/{server-uIyYmYnp.js → server-B9-F93mS.js} +539 -464
- package/dist/server-B9-F93mS.js.map +1 -0
- package/dist/templates/showcase/dot.ts +2 -0
- package/dist/templates/showcase/pages/basics/bars/controller.ts +1 -1
- package/dist/templates/showcase/pages/basics/control-flow/controller.ts +1 -1
- package/dist/templates/showcase/pages/basics/events/controller.ts +4 -4
- package/dist/templates/showcase/pages/basics/events/view.dml +2 -2
- package/dist/templates/showcase/pages/basics/hooks/controller.ts +6 -0
- package/dist/templates/showcase/pages/basics/hooks/view.dml +5 -0
- package/dist/templates/showcase/pages/basics/navigation/controller.ts +7 -0
- package/dist/templates/showcase/pages/basics/navigation/state.ts +2 -0
- package/dist/templates/showcase/pages/basics/navigation/view.dml +11 -0
- package/dist/templates/showcase/pages/basics/pick-genre/controller.ts +12 -0
- package/dist/templates/showcase/pages/basics/pick-genre/state.ts +3 -0
- package/dist/templates/showcase/pages/basics/pick-genre/view.dml +9 -0
- package/dist/templates/showcase/pages/components/carousel/controller.ts +1 -1
- package/dist/templates/showcase/pages/components/feedback/controller.ts +12 -2
- package/dist/templates/showcase/pages/components/feedback/state.ts +33 -0
- package/dist/templates/showcase/pages/components/feedback/view.dml +21 -1
- package/dist/templates/showcase/pages/components/forms/controller.ts +22 -22
- package/dist/templates/showcase/pages/components/forms/state.ts +3 -3
- package/dist/templates/showcase/pages/components/forms/view.dml +9 -2
- package/dist/templates/showcase/pages/components/icons/controller.ts +1 -1
- package/dist/templates/showcase/pages/components/images/controller.ts +3 -1
- package/dist/templates/showcase/pages/components/images/view.dml +13 -0
- package/dist/templates/showcase/pages/components/layout/controller.ts +2 -2
- package/dist/templates/showcase/pages/components/lists/controller.ts +1 -1
- package/dist/templates/showcase/pages/components/lists/view.dml +18 -0
- package/dist/templates/showcase/pages/components/money/controller.ts +6 -8
- package/dist/templates/showcase/pages/components/sheets/controller.ts +2 -2
- package/dist/templates/showcase/pages/components/tabs/controller.ts +22 -10
- package/dist/templates/showcase/pages/components/tabs/state.ts +1 -0
- package/dist/templates/showcase/pages/components/tabs/view.dml +4 -3
- package/dist/templates/showcase/pages/dash/cache/controller.ts +2 -2
- package/dist/templates/showcase/pages/dash/calls/controller.ts +7 -6
- package/dist/templates/showcase/pages/dash/calls/state.ts +1 -1
- package/dist/templates/showcase/pages/dash/calls/view.dml +3 -2
- package/dist/templates/showcase/pages/dash/contacts/controller.ts +30 -0
- package/dist/templates/showcase/pages/dash/contacts/state.ts +6 -0
- package/dist/templates/showcase/pages/dash/contacts/view.dml +33 -0
- package/dist/templates/showcase/pages/dash/files/controller.ts +15 -13
- package/dist/templates/showcase/pages/dash/files/state.ts +3 -6
- package/dist/templates/showcase/pages/dash/media/controller.ts +16 -13
- package/dist/templates/showcase/pages/dash/network/controller.ts +71 -16
- package/dist/templates/showcase/pages/dash/network/state.ts +10 -0
- package/dist/templates/showcase/pages/dash/network/view.dml +32 -4
- package/dist/templates/showcase/pages/dash/secrets/controller.ts +1 -1
- package/dist/templates/showcase/pages/dash/system/controller.ts +32 -5
- package/dist/templates/showcase/pages/dash/system/state.ts +6 -2
- package/dist/templates/showcase/pages/dash/system/view.dml +39 -1
- package/dist/templates/showcase/pages/dash/ui/controller.ts +23 -23
- package/dist/templates/showcase/pages/dash/ui/view.dml +1 -1
- package/dist/templates/showcase/pages/index/controller.ts +1 -1
- package/dist/templates/showcase/pages/index/state.ts +17 -10
- package/package.json +6 -6
- package/dist/server-uIyYmYnp.js.map +0 -1
|
@@ -10144,161 +10144,57 @@ function getWellformedEdit(textEdit) {
|
|
|
10144
10144
|
return textEdit;
|
|
10145
10145
|
}
|
|
10146
10146
|
//#endregion
|
|
10147
|
-
//#region src/lsp/
|
|
10148
|
-
const
|
|
10149
|
-
|
|
10150
|
-
|
|
10147
|
+
//#region src/lsp/descriptions.ts
|
|
10148
|
+
const CONTROL_FLOW_DOCS = {
|
|
10149
|
+
If: "Shows its children when `condition` is true.",
|
|
10150
|
+
ElseIf: "Goes after an `<If>` or `<ElseIf>`. Shows its children when every condition before it is false and `condition` is true.",
|
|
10151
|
+
Else: "Goes after an `<If>` or `<ElseIf>`. Shows its children when every condition before it is false.",
|
|
10152
|
+
For: "Repeats its children for each item in `each`. Inside, the item is named `item` and its position `index`. `key` tells items apart: a property of each item, `*this` for the item itself, or `*index` to match items by position."
|
|
10151
10153
|
};
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
const owner = shapeOf(expr.object, scope);
|
|
10167
|
-
if (!owner) return;
|
|
10168
|
-
const { property } = expr;
|
|
10169
|
-
if (property.type === "Literal" && typeof property.value === "string") return membersOf(owner)[property.value];
|
|
10170
|
-
return owner.kind === "list" ? owner.item : void 0;
|
|
10171
|
-
}
|
|
10172
|
-
default: return;
|
|
10173
|
-
}
|
|
10174
|
-
}
|
|
10175
|
-
function membersOf(shape) {
|
|
10176
|
-
switch (shape.kind) {
|
|
10177
|
-
case "object": return shape.properties;
|
|
10178
|
-
case "list": return { length: NUMBER };
|
|
10179
|
-
case "value": return {};
|
|
10154
|
+
const CONTROL_FLOW_ATTRIBUTE_DOCS = {
|
|
10155
|
+
condition: "A single `{{ }}` binding",
|
|
10156
|
+
each: "The list to repeat, as a single `{{ }}` binding",
|
|
10157
|
+
item: "The name for each item (default `item`)",
|
|
10158
|
+
index: "The name for each position (default `index`)",
|
|
10159
|
+
key: "A property of each item, `*this` or `*index`"
|
|
10160
|
+
};
|
|
10161
|
+
function describeComponent(component) {
|
|
10162
|
+
const lines = [`**<${component.name}>** · ${describeChildren(component)}`];
|
|
10163
|
+
const props = Object.entries(component.props);
|
|
10164
|
+
if (props.length > 0) lines.push("", ...props.map(([name, type]) => `- \`${name}\`: ${describeType(type)}`));
|
|
10165
|
+
if (component.events.length > 0) {
|
|
10166
|
+
const eventProps = component.events.map((event) => `\`${eventProp(event)}\``);
|
|
10167
|
+
lines.push("", `Events: ${eventProps.join(", ")}`);
|
|
10180
10168
|
}
|
|
10169
|
+
return lines.join("\n");
|
|
10181
10170
|
}
|
|
10182
|
-
function
|
|
10183
|
-
|
|
10184
|
-
}
|
|
10185
|
-
|
|
10186
|
-
const loops = /* @__PURE__ */ new Map();
|
|
10187
|
-
for (const variable of variables) if (variable.role === "index") loops.set(variable.name, NUMBER);
|
|
10188
|
-
else loops.set(variable.name, itemShape(variable.list, {
|
|
10189
|
-
state,
|
|
10190
|
-
loops
|
|
10191
|
-
}));
|
|
10192
|
-
return {
|
|
10193
|
-
state,
|
|
10194
|
-
loops
|
|
10195
|
-
};
|
|
10196
|
-
}
|
|
10197
|
-
function itemShape(each, scope) {
|
|
10198
|
-
const list = parseBinding(each.replace(/^\s*{{|}}\s*$/g, ""));
|
|
10199
|
-
const shape = list && shapeOf(list, scope);
|
|
10200
|
-
return shape?.kind === "list" ? shape.item : void 0;
|
|
10201
|
-
}
|
|
10202
|
-
function withLoop(scope, item, itemShape, index) {
|
|
10203
|
-
const loops = new Map(scope.loops).set(item, itemShape).set(index, NUMBER);
|
|
10204
|
-
return {
|
|
10205
|
-
state: scope.state,
|
|
10206
|
-
loops
|
|
10207
|
-
};
|
|
10208
|
-
}
|
|
10209
|
-
//#endregion
|
|
10210
|
-
//#region src/lsp/check-members.ts
|
|
10211
|
-
const BINDING = /{{([\s\S]*?)}}/g;
|
|
10212
|
-
function checkMembers(text, state) {
|
|
10213
|
-
let nodes;
|
|
10214
|
-
try {
|
|
10215
|
-
nodes = parseDmlToIR(text, {
|
|
10216
|
-
locations: true,
|
|
10217
|
-
whitespace: "preserve"
|
|
10218
|
-
});
|
|
10219
|
-
} catch {
|
|
10220
|
-
return [];
|
|
10221
|
-
}
|
|
10222
|
-
const problems = [];
|
|
10223
|
-
checkNodes(text, nodes, {
|
|
10224
|
-
state,
|
|
10225
|
-
loops: /* @__PURE__ */ new Map()
|
|
10226
|
-
}, problems);
|
|
10227
|
-
return problems;
|
|
10171
|
+
function describeType(type) {
|
|
10172
|
+
if (typeof type === "string") return type;
|
|
10173
|
+
if (isWordsOr(type)) return `${describeType(type.words)} | ${otherThanWords(type)}`;
|
|
10174
|
+
return type.map((value) => `"${value}"`).join(" | ");
|
|
10228
10175
|
}
|
|
10229
|
-
function
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
|
|
10233
|
-
checkBindings(text, node.content, from, scope, problems);
|
|
10234
|
-
continue;
|
|
10235
|
-
}
|
|
10236
|
-
for (const value of Object.values(node.attributes)) checkBindings(text, value, from, scope, problems);
|
|
10237
|
-
if (node.tag !== "For") {
|
|
10238
|
-
checkNodes(text, node.children, scope, problems);
|
|
10239
|
-
continue;
|
|
10240
|
-
}
|
|
10241
|
-
const { each = "", item = "item", index = "index", key } = node.attributes;
|
|
10242
|
-
const itemOfList = itemShape(each, scope);
|
|
10243
|
-
if (itemOfList?.kind === "object" && key !== void 0 && !key.startsWith("*") && !(key in itemOfList.properties)) problems.push({
|
|
10244
|
-
message: `The <For> \`key\` "${key}" is not a property of \`${itemOfList.type}\`. It has: ${describeMembers(itemOfList)}`,
|
|
10245
|
-
offset: offsetOf(text, `key="${key}"`, from)
|
|
10246
|
-
});
|
|
10247
|
-
checkNodes(text, node.children, withLoop(scope, item, itemOfList, index), problems);
|
|
10248
|
-
}
|
|
10176
|
+
function describeLoopVariable(variable, shape) {
|
|
10177
|
+
const list = variable.list ? ` \`${variable.list}\`` : "";
|
|
10178
|
+
if (variable.role === "item") return `each item of${list}${typeSuffix(shape)}`;
|
|
10179
|
+
return `the position in${list}`;
|
|
10249
10180
|
}
|
|
10250
|
-
function
|
|
10251
|
-
|
|
10252
|
-
const expr = parseBinding(source);
|
|
10253
|
-
if (!expr) continue;
|
|
10254
|
-
const bindingOffset = offsetOf(text, binding, from);
|
|
10255
|
-
for (const { name, owner } of unknownMembers(expr, scope)) problems.push({
|
|
10256
|
-
message: `\`${name}\` is not a property of \`${owner.type}\`. It has: ${describeMembers(owner)}`,
|
|
10257
|
-
offset: memberOffset(bindingOffset, binding, name)
|
|
10258
|
-
});
|
|
10259
|
-
}
|
|
10181
|
+
function typeSuffix(shape) {
|
|
10182
|
+
return shape ? `: \`${shape.type}\`` : "";
|
|
10260
10183
|
}
|
|
10261
|
-
function
|
|
10262
|
-
|
|
10263
|
-
|
|
10264
|
-
case "
|
|
10265
|
-
case "
|
|
10266
|
-
case "Member": {
|
|
10267
|
-
const inner = [...found(expr.object), ...found(expr.property)];
|
|
10268
|
-
const { property } = expr;
|
|
10269
|
-
if (property.type !== "Literal" || typeof property.value !== "string") return inner;
|
|
10270
|
-
const owner = shapeOf(expr.object, scope);
|
|
10271
|
-
if (owner && owner.kind !== "value" && !(property.value in membersOf(owner))) return [...inner, {
|
|
10272
|
-
name: property.value,
|
|
10273
|
-
owner
|
|
10274
|
-
}];
|
|
10275
|
-
return inner;
|
|
10276
|
-
}
|
|
10277
|
-
case "Unary": return found(expr.argument);
|
|
10278
|
-
case "Binary": return [...found(expr.left), ...found(expr.right)];
|
|
10279
|
-
case "Conditional": return [
|
|
10280
|
-
...found(expr.test),
|
|
10281
|
-
...found(expr.consequent),
|
|
10282
|
-
...found(expr.alternate)
|
|
10283
|
-
];
|
|
10184
|
+
function describeChildren(component) {
|
|
10185
|
+
switch (component.children) {
|
|
10186
|
+
case "none": return "no children";
|
|
10187
|
+
case "text": return "text and inline components";
|
|
10188
|
+
case "elements": return "any components";
|
|
10284
10189
|
}
|
|
10285
10190
|
}
|
|
10286
|
-
function offsetOf(text, written, from) {
|
|
10287
|
-
const at = text.indexOf(written, from);
|
|
10288
|
-
return at < 0 ? from : at;
|
|
10289
|
-
}
|
|
10290
|
-
function memberOffset(bindingOffset, binding, name) {
|
|
10291
|
-
const escapedName = name.replaceAll("$", "\\$");
|
|
10292
|
-
const match = new RegExp(`(?<=\\.\\s*)${escapedName}(?![\\w$])`).exec(binding);
|
|
10293
|
-
return match ? bindingOffset + match.index : bindingOffset;
|
|
10294
|
-
}
|
|
10295
10191
|
//#endregion
|
|
10296
10192
|
//#region src/lsp/markup.ts
|
|
10297
10193
|
const TAG_NAME = /[A-Za-z0-9_.:-]/;
|
|
10298
|
-
const
|
|
10194
|
+
const ATTRIBUTE_NAME = /[^\s=/>"']/;
|
|
10299
10195
|
const WHITESPACE = /\s/;
|
|
10300
10196
|
function markupContextAt(text, offset) {
|
|
10301
|
-
const
|
|
10197
|
+
const openElements = [];
|
|
10302
10198
|
let index = 0;
|
|
10303
10199
|
while (index < offset) if (text.startsWith("<!--", index)) {
|
|
10304
10200
|
const end = text.indexOf("-->", index + 4);
|
|
@@ -10311,34 +10207,34 @@ function markupContextAt(text, offset) {
|
|
|
10311
10207
|
if (offset <= nameEnd || close < 0 || close >= offset) return {
|
|
10312
10208
|
kind: "closeTag",
|
|
10313
10209
|
start: nameStart,
|
|
10314
|
-
open:
|
|
10210
|
+
open: openElements.at(-1)
|
|
10315
10211
|
};
|
|
10316
|
-
closeElement(
|
|
10212
|
+
closeElement(openElements, text.slice(nameStart, nameEnd));
|
|
10317
10213
|
index = close + 1;
|
|
10318
10214
|
} else if (text[index] === "<") {
|
|
10319
|
-
const tag = readTag(text, index, offset,
|
|
10215
|
+
const tag = readTag(text, index, offset, openElements);
|
|
10320
10216
|
if (tag.context) return tag.context;
|
|
10321
|
-
if (tag.element && !tag.selfClosing)
|
|
10217
|
+
if (tag.element && !tag.selfClosing) openElements.push(tag.element);
|
|
10322
10218
|
index = tag.end;
|
|
10323
10219
|
} else if (text.startsWith("{{", index)) {
|
|
10324
10220
|
const end = text.indexOf("}}", index + 2);
|
|
10325
10221
|
if (end < 0 || offset <= end) return {
|
|
10326
10222
|
kind: "binding",
|
|
10327
10223
|
start: index + 2,
|
|
10328
|
-
loopVariables: loopVariables(
|
|
10224
|
+
loopVariables: loopVariables(openElements)
|
|
10329
10225
|
};
|
|
10330
10226
|
index = end + 2;
|
|
10331
10227
|
} else index++;
|
|
10332
10228
|
return { kind: "text" };
|
|
10333
10229
|
}
|
|
10334
|
-
function readTag(text, start, offset,
|
|
10230
|
+
function readTag(text, start, offset, openElements) {
|
|
10335
10231
|
const nameStart = start + 1;
|
|
10336
10232
|
const nameEnd = readWhile(text, nameStart, TAG_NAME);
|
|
10337
10233
|
if (offset <= nameEnd) return {
|
|
10338
10234
|
context: {
|
|
10339
10235
|
kind: "tagName",
|
|
10340
10236
|
start: nameStart,
|
|
10341
|
-
parents: [...
|
|
10237
|
+
parents: [...openElements]
|
|
10342
10238
|
},
|
|
10343
10239
|
end: nameEnd
|
|
10344
10240
|
};
|
|
@@ -10373,7 +10269,7 @@ function readTag(text, start, offset, open) {
|
|
|
10373
10269
|
end: index + 1
|
|
10374
10270
|
};
|
|
10375
10271
|
const attributeStart = index;
|
|
10376
|
-
index =
|
|
10272
|
+
index = readWhile(text, index + 1, ATTRIBUTE_NAME);
|
|
10377
10273
|
const name = text.slice(attributeStart, index);
|
|
10378
10274
|
if (offset <= index) return {
|
|
10379
10275
|
context: {
|
|
@@ -10393,7 +10289,7 @@ function readTag(text, start, offset, open) {
|
|
|
10393
10289
|
if (quote === "\"" || quote === "'") {
|
|
10394
10290
|
const close = text.indexOf(quote, valueStart + 1);
|
|
10395
10291
|
if (close < 0 || offset <= close) return {
|
|
10396
|
-
context: valueContext(text.slice(valueStart + 1, offset), valueStart + 1, element, name,
|
|
10292
|
+
context: valueContext(text.slice(valueStart + 1, offset), valueStart + 1, element, name, openElements),
|
|
10397
10293
|
end: offset
|
|
10398
10294
|
};
|
|
10399
10295
|
attributes[name] = text.slice(valueStart + 1, close);
|
|
@@ -10406,7 +10302,7 @@ function readTag(text, start, offset, open) {
|
|
|
10406
10302
|
start: valueStart,
|
|
10407
10303
|
element,
|
|
10408
10304
|
attribute: name,
|
|
10409
|
-
loopVariables: loopVariables(
|
|
10305
|
+
loopVariables: loopVariables(openElements)
|
|
10410
10306
|
},
|
|
10411
10307
|
end: valueEnd
|
|
10412
10308
|
};
|
|
@@ -10415,8 +10311,8 @@ function readTag(text, start, offset, open) {
|
|
|
10415
10311
|
}
|
|
10416
10312
|
}
|
|
10417
10313
|
}
|
|
10418
|
-
function valueContext(valueBeforeCursor, valueStart, element, attribute,
|
|
10419
|
-
const variables = loopVariables(
|
|
10314
|
+
function valueContext(valueBeforeCursor, valueStart, element, attribute, openElements) {
|
|
10315
|
+
const variables = loopVariables(openElements);
|
|
10420
10316
|
const bindingStart = valueBeforeCursor.lastIndexOf("{{");
|
|
10421
10317
|
if (bindingStart >= 0 && !valueBeforeCursor.includes("}}", bindingStart)) return {
|
|
10422
10318
|
kind: "binding",
|
|
@@ -10431,18 +10327,18 @@ function valueContext(valueBeforeCursor, valueStart, element, attribute, open) {
|
|
|
10431
10327
|
loopVariables: variables
|
|
10432
10328
|
};
|
|
10433
10329
|
}
|
|
10434
|
-
function closeElement(
|
|
10435
|
-
for (let index =
|
|
10436
|
-
|
|
10330
|
+
function closeElement(openElements, tag) {
|
|
10331
|
+
for (let index = openElements.length - 1; index >= 0; index--) if (openElements[index].tag === tag) {
|
|
10332
|
+
openElements.length = index;
|
|
10437
10333
|
return;
|
|
10438
10334
|
}
|
|
10439
10335
|
}
|
|
10440
|
-
function loopVariables(
|
|
10336
|
+
function loopVariables(openElements) {
|
|
10441
10337
|
const variables = [];
|
|
10442
|
-
for (const element of
|
|
10338
|
+
for (const element of openElements) {
|
|
10443
10339
|
if (element.tag !== "For") continue;
|
|
10444
10340
|
const { item = "item", index = "index", each = "" } = element.attributes;
|
|
10445
|
-
const list = each
|
|
10341
|
+
const list = bindingSource(each);
|
|
10446
10342
|
variables.push({
|
|
10447
10343
|
name: item,
|
|
10448
10344
|
role: "item",
|
|
@@ -10455,6 +10351,9 @@ function loopVariables(open) {
|
|
|
10455
10351
|
}
|
|
10456
10352
|
return variables;
|
|
10457
10353
|
}
|
|
10354
|
+
function bindingSource(value) {
|
|
10355
|
+
return value.replace(/^\s*{{\s*|\s*}}\s*$/g, "");
|
|
10356
|
+
}
|
|
10458
10357
|
function readWhile(text, index, pattern) {
|
|
10459
10358
|
while (index < text.length && pattern.test(text[index])) index++;
|
|
10460
10359
|
return index;
|
|
@@ -10472,78 +10371,161 @@ function wordAt(text, offset, pattern) {
|
|
|
10472
10371
|
};
|
|
10473
10372
|
}
|
|
10474
10373
|
//#endregion
|
|
10475
|
-
//#region src/lsp/
|
|
10476
|
-
const
|
|
10477
|
-
|
|
10478
|
-
|
|
10374
|
+
//#region src/lsp/shapes.ts
|
|
10375
|
+
const NUMBER_SHAPE = {
|
|
10376
|
+
kind: "value",
|
|
10377
|
+
type: "number"
|
|
10378
|
+
};
|
|
10479
10379
|
const MEMBER_CHAIN = /([A-Za-z_$][\w$]*(?:\s*\.\s*[A-Za-z_$][\w$]*)*)\s*\.\s*$/;
|
|
10480
|
-
|
|
10380
|
+
function parseBinding(source) {
|
|
10381
|
+
try {
|
|
10382
|
+
return parseExpression(source);
|
|
10383
|
+
} catch (error) {
|
|
10384
|
+
if (error instanceof ScriptParseError) return;
|
|
10385
|
+
throw error;
|
|
10386
|
+
}
|
|
10387
|
+
}
|
|
10388
|
+
function shapeOf(expression, scope) {
|
|
10389
|
+
switch (expression.type) {
|
|
10390
|
+
case "Identifier":
|
|
10391
|
+
if (scope.loops.has(expression.name)) return scope.loops.get(expression.name);
|
|
10392
|
+
return scope.state[expression.name];
|
|
10393
|
+
case "Member": {
|
|
10394
|
+
const owner = shapeOf(expression.object, scope);
|
|
10395
|
+
if (!owner) return;
|
|
10396
|
+
const { property } = expression;
|
|
10397
|
+
if (property.type === "Literal" && typeof property.value === "string") return membersOf(owner)[property.value];
|
|
10398
|
+
if (owner.kind === "list") return owner.item;
|
|
10399
|
+
return;
|
|
10400
|
+
}
|
|
10401
|
+
default: return;
|
|
10402
|
+
}
|
|
10403
|
+
}
|
|
10404
|
+
function membersOf(shape) {
|
|
10405
|
+
switch (shape.kind) {
|
|
10406
|
+
case "object": return shape.properties;
|
|
10407
|
+
case "list": return { length: NUMBER_SHAPE };
|
|
10408
|
+
case "value": return {};
|
|
10409
|
+
}
|
|
10410
|
+
}
|
|
10411
|
+
function describeMembers(shape) {
|
|
10412
|
+
return Object.keys(membersOf(shape)).join(", ") || "none";
|
|
10413
|
+
}
|
|
10414
|
+
function loopScope(variables, state) {
|
|
10415
|
+
const loops = /* @__PURE__ */ new Map();
|
|
10416
|
+
for (const variable of variables) if (variable.role === "index") loops.set(variable.name, NUMBER_SHAPE);
|
|
10417
|
+
else loops.set(variable.name, itemShape(variable.list, {
|
|
10418
|
+
state,
|
|
10419
|
+
loops
|
|
10420
|
+
}));
|
|
10421
|
+
return {
|
|
10422
|
+
state,
|
|
10423
|
+
loops
|
|
10424
|
+
};
|
|
10425
|
+
}
|
|
10426
|
+
function bindingScope(loopVariables, page) {
|
|
10427
|
+
const shapes = page.state?.shapes;
|
|
10428
|
+
if (!shapes) return;
|
|
10429
|
+
return loopScope(loopVariables, shapes);
|
|
10430
|
+
}
|
|
10431
|
+
function itemShape(each, scope) {
|
|
10432
|
+
const list = parseBinding(bindingSource(each));
|
|
10433
|
+
if (!list) return;
|
|
10434
|
+
const shape = shapeOf(list, scope);
|
|
10435
|
+
if (shape?.kind === "list") return shape.item;
|
|
10436
|
+
}
|
|
10437
|
+
function withLoop(scope, item, itemOfList, index) {
|
|
10438
|
+
const loops = new Map(scope.loops);
|
|
10439
|
+
loops.set(item, itemOfList);
|
|
10440
|
+
loops.set(index, NUMBER_SHAPE);
|
|
10441
|
+
return {
|
|
10442
|
+
state: scope.state,
|
|
10443
|
+
loops
|
|
10444
|
+
};
|
|
10445
|
+
}
|
|
10446
|
+
function isMember(text, bindingStart, wordStart) {
|
|
10447
|
+
return text.slice(bindingStart, wordStart).trimEnd().endsWith(".");
|
|
10448
|
+
}
|
|
10449
|
+
function ownerOf(text, bindingStart, wordStart, scope) {
|
|
10450
|
+
const chain = MEMBER_CHAIN.exec(text.slice(bindingStart, wordStart))?.[1];
|
|
10451
|
+
if (chain === void 0) return;
|
|
10452
|
+
const expression = parseBinding(chain);
|
|
10453
|
+
if (!expression || !scope) return;
|
|
10454
|
+
return shapeOf(expression, scope);
|
|
10455
|
+
}
|
|
10456
|
+
//#endregion
|
|
10457
|
+
//#region src/lsp/completion.ts
|
|
10458
|
+
const IMAGE_ATTRIBUTES = ["src", "image"];
|
|
10459
|
+
const IDENTIFIER_BEFORE_CURSOR = /[A-Za-z_$][\w$]*$/;
|
|
10481
10460
|
const CONTROL_FLOW_SNIPPETS = {
|
|
10482
10461
|
If: "If condition=\"{{ $1 }}\">$0</If>",
|
|
10483
10462
|
ElseIf: "ElseIf condition=\"{{ $1 }}\">$0</ElseIf>",
|
|
10484
10463
|
Else: "Else>$0</Else>",
|
|
10485
10464
|
For: "For each=\"{{ $1 }}\" key=\"$2\">$0</For>"
|
|
10486
10465
|
};
|
|
10487
|
-
|
|
10488
|
-
If: "Shows its children when `condition` is true.",
|
|
10489
|
-
ElseIf: "Goes after an `<If>` or `<ElseIf>`. Shows its children when every condition before it is false and `condition` is true.",
|
|
10490
|
-
Else: "Goes after an `<If>` or `<ElseIf>`. Shows its children when every condition before it is false.",
|
|
10491
|
-
For: "Repeats its children for each item in `each`. Inside, the item is named `item` and its position `index`. `key` tells items apart: a property of each item, `*this` for the item itself, or `*index` to match items by position."
|
|
10492
|
-
};
|
|
10493
|
-
const CONTROL_FLOW_ATTRIBUTE_DOCS = {
|
|
10494
|
-
condition: "A single `{{ }}` binding",
|
|
10495
|
-
each: "The list to repeat, as a single `{{ }}` binding",
|
|
10496
|
-
item: "The name for each item (default `item`)",
|
|
10497
|
-
index: "The name for each position (default `index`)",
|
|
10498
|
-
key: "A property of each item, `*this` or `*index`"
|
|
10499
|
-
};
|
|
10500
|
-
function complete(document, offset, page, { components }) {
|
|
10466
|
+
function complete(document, offset, page, components) {
|
|
10501
10467
|
const text = document.getText();
|
|
10502
10468
|
const context = markupContextAt(text, offset);
|
|
10503
|
-
const range = (start) => ({
|
|
10504
|
-
start: document.positionAt(start),
|
|
10505
|
-
end: document.positionAt(offset)
|
|
10506
|
-
});
|
|
10507
|
-
const replacing = (start, items) => items.map((item) => ({
|
|
10508
|
-
...item,
|
|
10509
|
-
textEdit: {
|
|
10510
|
-
range: range(start),
|
|
10511
|
-
newText: item.insertText ?? item.label
|
|
10512
|
-
}
|
|
10513
|
-
}));
|
|
10514
10469
|
switch (context.kind) {
|
|
10515
|
-
case "tagName":
|
|
10470
|
+
case "tagName": {
|
|
10471
|
+
const items = tagCompletions(context.parents, components);
|
|
10472
|
+
return replacingFrom(document, context.start, offset, items);
|
|
10473
|
+
}
|
|
10516
10474
|
case "closeTag":
|
|
10517
10475
|
if (!context.open) return [];
|
|
10518
10476
|
return [{
|
|
10519
10477
|
label: `/${context.open.tag}`,
|
|
10520
10478
|
kind: import_main.CompletionItemKind.Property,
|
|
10521
10479
|
textEdit: {
|
|
10522
|
-
range:
|
|
10480
|
+
range: rangeFrom(document, context.start, offset),
|
|
10523
10481
|
newText: `${context.open.tag}>`
|
|
10524
10482
|
}
|
|
10525
10483
|
}];
|
|
10526
|
-
case "attributeName":
|
|
10484
|
+
case "attributeName": {
|
|
10485
|
+
const items = attributeCompletions(context.element, components);
|
|
10486
|
+
return replacingFrom(document, context.start, offset, items);
|
|
10487
|
+
}
|
|
10527
10488
|
case "attributeValue": {
|
|
10528
|
-
const scope =
|
|
10529
|
-
|
|
10489
|
+
const scope = bindingScope(context.loopVariables, page);
|
|
10490
|
+
const items = valueCompletions(context.element, context.attribute, page, scope, components);
|
|
10491
|
+
return replacingFrom(document, context.start, offset, items);
|
|
10530
10492
|
}
|
|
10531
10493
|
case "binding": {
|
|
10532
10494
|
const typed = text.slice(context.start, offset);
|
|
10533
|
-
const wordStart = offset - (
|
|
10534
|
-
const scope =
|
|
10535
|
-
|
|
10495
|
+
const wordStart = offset - (IDENTIFIER_BEFORE_CURSOR.exec(typed)?.[0] ?? "").length;
|
|
10496
|
+
const scope = bindingScope(context.loopVariables, page);
|
|
10497
|
+
let items;
|
|
10498
|
+
if (isMember(text, context.start, wordStart)) items = memberCompletions(ownerOf(text, context.start, wordStart, scope));
|
|
10499
|
+
else items = bindingCompletions(context.loopVariables, page, scope);
|
|
10500
|
+
return replacingFrom(document, wordStart, offset, items);
|
|
10536
10501
|
}
|
|
10537
10502
|
default: return [];
|
|
10538
10503
|
}
|
|
10539
10504
|
}
|
|
10505
|
+
function rangeFrom(document, start, offset) {
|
|
10506
|
+
return {
|
|
10507
|
+
start: document.positionAt(start),
|
|
10508
|
+
end: document.positionAt(offset)
|
|
10509
|
+
};
|
|
10510
|
+
}
|
|
10511
|
+
function replacingFrom(document, start, offset, items) {
|
|
10512
|
+
return items.map((item) => ({
|
|
10513
|
+
...item,
|
|
10514
|
+
textEdit: {
|
|
10515
|
+
range: rangeFrom(document, start, offset),
|
|
10516
|
+
newText: item.insertText ?? item.label
|
|
10517
|
+
}
|
|
10518
|
+
}));
|
|
10519
|
+
}
|
|
10540
10520
|
function tagCompletions(parents, components) {
|
|
10541
10521
|
if (parents.length === 0) {
|
|
10542
10522
|
const root = components[ROOT_TAG];
|
|
10543
|
-
|
|
10523
|
+
if (!root) return [];
|
|
10524
|
+
return [componentItem(root, "0")];
|
|
10544
10525
|
}
|
|
10545
10526
|
const parent = [...parents].reverse().find((element) => !(element.tag in CONTROL_FLOW_TAGS));
|
|
10546
|
-
|
|
10527
|
+
let parentManifest;
|
|
10528
|
+
if (parent) parentManifest = components[parent.tag];
|
|
10547
10529
|
const items = Object.values(components).filter((component) => !parentManifest || canHaveChildren(parentManifest, component)).map((component) => componentItem(component, "1"));
|
|
10548
10530
|
for (const [tag, snippet] of Object.entries(CONTROL_FLOW_SNIPPETS)) items.push({
|
|
10549
10531
|
label: tag,
|
|
@@ -10560,7 +10542,8 @@ function tagCompletions(parents, components) {
|
|
|
10560
10542
|
return items;
|
|
10561
10543
|
}
|
|
10562
10544
|
function componentItem(component, sortGroup) {
|
|
10563
|
-
|
|
10545
|
+
let snippet = `${component.name}$1>$0</${component.name}>`;
|
|
10546
|
+
if (component.children === "none") snippet = `${component.name}$1 />`;
|
|
10564
10547
|
return {
|
|
10565
10548
|
label: component.name,
|
|
10566
10549
|
kind: import_main.CompletionItemKind.Class,
|
|
@@ -10575,9 +10558,9 @@ function componentItem(component, sortGroup) {
|
|
|
10575
10558
|
};
|
|
10576
10559
|
}
|
|
10577
10560
|
function attributeCompletions(element, components) {
|
|
10578
|
-
const
|
|
10561
|
+
const isWritten = (name) => name in element.attributes;
|
|
10579
10562
|
const controlFlow = CONTROL_FLOW_TAGS[element.tag];
|
|
10580
|
-
if (controlFlow) return controlFlow.attributes.filter((name) => !
|
|
10563
|
+
if (controlFlow) return controlFlow.attributes.filter((name) => !isWritten(name)).map((name) => ({
|
|
10581
10564
|
label: name,
|
|
10582
10565
|
kind: import_main.CompletionItemKind.Property,
|
|
10583
10566
|
detail: CONTROL_FLOW_ATTRIBUTE_DOCS[name] ?? name,
|
|
@@ -10586,7 +10569,7 @@ function attributeCompletions(element, components) {
|
|
|
10586
10569
|
}));
|
|
10587
10570
|
const component = components[element.tag];
|
|
10588
10571
|
if (!component) return [];
|
|
10589
|
-
const props = Object.entries(component.props).filter(([name]) => !
|
|
10572
|
+
const props = Object.entries(component.props).filter(([name]) => !isWritten(name)).map(([name, type]) => ({
|
|
10590
10573
|
label: name,
|
|
10591
10574
|
kind: import_main.CompletionItemKind.Property,
|
|
10592
10575
|
detail: describeType(type),
|
|
@@ -10594,7 +10577,7 @@ function attributeCompletions(element, components) {
|
|
|
10594
10577
|
insertTextFormat: import_main.InsertTextFormat.Snippet,
|
|
10595
10578
|
sortText: `0${name}`
|
|
10596
10579
|
}));
|
|
10597
|
-
const events = component.events.map((event) => eventProp(event)).filter((name) => !
|
|
10580
|
+
const events = component.events.map((event) => eventProp(event)).filter((name) => !isWritten(name)).map((name) => ({
|
|
10598
10581
|
label: name,
|
|
10599
10582
|
kind: import_main.CompletionItemKind.Event,
|
|
10600
10583
|
detail: "a mutation or action in controller.ts",
|
|
@@ -10605,25 +10588,10 @@ function attributeCompletions(element, components) {
|
|
|
10605
10588
|
return [...props, ...events];
|
|
10606
10589
|
}
|
|
10607
10590
|
function valueCompletions(element, attribute, page, scope, components) {
|
|
10608
|
-
if (element.tag === "For" && attribute === "key")
|
|
10609
|
-
const item = scope && itemShape(element.attributes.each ?? "", scope);
|
|
10610
|
-
return [
|
|
10611
|
-
...item?.kind === "object" ? memberCompletions(item) : [],
|
|
10612
|
-
{
|
|
10613
|
-
label: "*this",
|
|
10614
|
-
kind: import_main.CompletionItemKind.Value,
|
|
10615
|
-
detail: "the item itself"
|
|
10616
|
-
},
|
|
10617
|
-
{
|
|
10618
|
-
label: "*index",
|
|
10619
|
-
kind: import_main.CompletionItemKind.Value,
|
|
10620
|
-
detail: "match items by position"
|
|
10621
|
-
}
|
|
10622
|
-
];
|
|
10623
|
-
}
|
|
10591
|
+
if (element.tag === "For" && attribute === "key") return forKeyCompletions(element, scope);
|
|
10624
10592
|
const component = components[element.tag];
|
|
10625
10593
|
if (!component) return [];
|
|
10626
|
-
if (isEventProp(attribute) && component.events.includes(eventName(attribute))) return
|
|
10594
|
+
if (isEventProp(attribute) && component.events.includes(eventName(attribute))) return handlerCompletions(page);
|
|
10627
10595
|
const type = component.props[attribute];
|
|
10628
10596
|
if (type && isWordsOr(type)) return type.words.map((value) => ({
|
|
10629
10597
|
label: value,
|
|
@@ -10643,27 +10611,52 @@ function valueCompletions(element, attribute, page, scope, components) {
|
|
|
10643
10611
|
}));
|
|
10644
10612
|
return [];
|
|
10645
10613
|
}
|
|
10646
|
-
function
|
|
10614
|
+
function forKeyCompletions(element, scope) {
|
|
10615
|
+
let properties = [];
|
|
10616
|
+
if (scope) {
|
|
10617
|
+
const item = itemShape(element.attributes.each ?? "", scope);
|
|
10618
|
+
if (item?.kind === "object") properties = memberCompletions(item);
|
|
10619
|
+
}
|
|
10620
|
+
return [
|
|
10621
|
+
...properties,
|
|
10622
|
+
{
|
|
10623
|
+
label: "*this",
|
|
10624
|
+
kind: import_main.CompletionItemKind.Value,
|
|
10625
|
+
detail: "the item itself"
|
|
10626
|
+
},
|
|
10627
|
+
{
|
|
10628
|
+
label: "*index",
|
|
10629
|
+
kind: import_main.CompletionItemKind.Value,
|
|
10630
|
+
detail: "match items by position"
|
|
10631
|
+
}
|
|
10632
|
+
];
|
|
10633
|
+
}
|
|
10634
|
+
function handlerCompletions(page) {
|
|
10647
10635
|
const { controller } = page;
|
|
10648
10636
|
if (!controller) return [];
|
|
10649
|
-
|
|
10637
|
+
const mutations = (controller.mutations ?? []).map((name) => ({
|
|
10650
10638
|
label: name,
|
|
10651
10639
|
kind: import_main.CompletionItemKind.Method,
|
|
10652
10640
|
detail: "mutation"
|
|
10653
|
-
}))
|
|
10641
|
+
}));
|
|
10642
|
+
const actions = (controller.actions ?? []).map((name) => ({
|
|
10654
10643
|
label: name,
|
|
10655
10644
|
kind: import_main.CompletionItemKind.Function,
|
|
10656
10645
|
detail: "action"
|
|
10657
|
-
}))
|
|
10646
|
+
}));
|
|
10647
|
+
return [...mutations, ...actions];
|
|
10658
10648
|
}
|
|
10659
10649
|
function bindingCompletions(loopVariables, page, scope) {
|
|
10660
10650
|
const items = /* @__PURE__ */ new Map();
|
|
10661
|
-
for (const key of page.state?.keys ?? [])
|
|
10662
|
-
|
|
10663
|
-
|
|
10664
|
-
|
|
10665
|
-
|
|
10666
|
-
|
|
10651
|
+
for (const key of page.state?.keys ?? []) {
|
|
10652
|
+
const shape = scope?.state[key];
|
|
10653
|
+
items.set(key, {
|
|
10654
|
+
label: key,
|
|
10655
|
+
kind: import_main.CompletionItemKind.Field,
|
|
10656
|
+
detail: shape ? `state: ${shape.type}` : "state",
|
|
10657
|
+
sortText: `1${key}`
|
|
10658
|
+
});
|
|
10659
|
+
}
|
|
10667
10660
|
for (const variable of loopVariables) items.set(variable.name, {
|
|
10668
10661
|
label: variable.name,
|
|
10669
10662
|
kind: import_main.CompletionItemKind.Variable,
|
|
@@ -10680,227 +10673,107 @@ function memberCompletions(owner) {
|
|
|
10680
10673
|
detail: shape.type
|
|
10681
10674
|
}));
|
|
10682
10675
|
}
|
|
10683
|
-
|
|
10684
|
-
|
|
10685
|
-
|
|
10686
|
-
|
|
10687
|
-
|
|
10688
|
-
|
|
10689
|
-
|
|
10690
|
-
|
|
10691
|
-
|
|
10692
|
-
|
|
10693
|
-
|
|
10694
|
-
|
|
10695
|
-
function hover(document, offset, page, { components }) {
|
|
10696
|
-
const found = symbolAt(document.getText(), offset, page);
|
|
10697
|
-
if (!found) return null;
|
|
10698
|
-
const markdown = describeSymbol(found.symbol, page, components);
|
|
10699
|
-
if (!markdown) return null;
|
|
10700
|
-
return {
|
|
10701
|
-
contents: {
|
|
10702
|
-
kind: import_main.MarkupKind.Markdown,
|
|
10703
|
-
value: markdown
|
|
10704
|
-
},
|
|
10705
|
-
range: {
|
|
10706
|
-
start: document.positionAt(found.start),
|
|
10707
|
-
end: document.positionAt(found.end)
|
|
10708
|
-
}
|
|
10709
|
-
};
|
|
10710
|
-
}
|
|
10711
|
-
function definition(document, offset, page) {
|
|
10712
|
-
const found = symbolAt(document.getText(), offset, page);
|
|
10713
|
-
if (!found) return null;
|
|
10714
|
-
const { symbol } = found;
|
|
10715
|
-
if (symbol.kind === "handler" && page.controller) return locationIn(page.controller.file, page.controller.text, page.controller.offsets[symbol.name]);
|
|
10716
|
-
if (symbol.kind === "state" && page.state) return locationIn(page.state.file, page.state.text, page.state.offsets[symbol.name]);
|
|
10717
|
-
if (symbol.kind === "property" && symbol.shape.declaration) {
|
|
10718
|
-
const { file, line, character } = symbol.shape.declaration;
|
|
10719
|
-
const position = {
|
|
10720
|
-
line,
|
|
10721
|
-
character
|
|
10722
|
-
};
|
|
10723
|
-
return {
|
|
10724
|
-
uri: pathToFileURL(file).href,
|
|
10725
|
-
range: {
|
|
10726
|
-
start: position,
|
|
10727
|
-
end: position
|
|
10728
|
-
}
|
|
10729
|
-
};
|
|
10676
|
+
//#endregion
|
|
10677
|
+
//#region src/lsp/check-members.ts
|
|
10678
|
+
const BINDING = /{{([\s\S]*?)}}/g;
|
|
10679
|
+
function checkMembers(text, state) {
|
|
10680
|
+
let nodes;
|
|
10681
|
+
try {
|
|
10682
|
+
nodes = parseDmlToIR(text, {
|
|
10683
|
+
locations: true,
|
|
10684
|
+
whitespace: "preserve"
|
|
10685
|
+
});
|
|
10686
|
+
} catch {
|
|
10687
|
+
return [];
|
|
10730
10688
|
}
|
|
10731
|
-
|
|
10732
|
-
|
|
10689
|
+
const problems = [];
|
|
10690
|
+
checkNodes(text, nodes, {
|
|
10691
|
+
state,
|
|
10692
|
+
loops: /* @__PURE__ */ new Map()
|
|
10693
|
+
}, problems);
|
|
10694
|
+
return problems;
|
|
10733
10695
|
}
|
|
10734
|
-
function
|
|
10735
|
-
const
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
return {
|
|
10741
|
-
symbol: {
|
|
10742
|
-
kind: "component",
|
|
10743
|
-
tag: text.slice(start, end)
|
|
10744
|
-
},
|
|
10745
|
-
start,
|
|
10746
|
-
end
|
|
10747
|
-
};
|
|
10748
|
-
}
|
|
10749
|
-
case "attributeName": {
|
|
10750
|
-
const { start, end } = wordAt(text, offset, ATTRIBUTE_NAME);
|
|
10751
|
-
const name = text.slice(start, end);
|
|
10752
|
-
return name ? {
|
|
10753
|
-
symbol: {
|
|
10754
|
-
kind: "attribute",
|
|
10755
|
-
tag: context.element.tag,
|
|
10756
|
-
name
|
|
10757
|
-
},
|
|
10758
|
-
start,
|
|
10759
|
-
end
|
|
10760
|
-
} : null;
|
|
10761
|
-
}
|
|
10762
|
-
case "attributeValue": {
|
|
10763
|
-
const { start, end } = wordAt(text, offset, VALUE);
|
|
10764
|
-
const value = text.slice(start, end);
|
|
10765
|
-
if (isEventProp(context.attribute)) return {
|
|
10766
|
-
symbol: {
|
|
10767
|
-
kind: "handler",
|
|
10768
|
-
name: value
|
|
10769
|
-
},
|
|
10770
|
-
start,
|
|
10771
|
-
end
|
|
10772
|
-
};
|
|
10773
|
-
if (page.statics?.includes(value)) return {
|
|
10774
|
-
symbol: {
|
|
10775
|
-
kind: "static",
|
|
10776
|
-
path: value
|
|
10777
|
-
},
|
|
10778
|
-
start,
|
|
10779
|
-
end
|
|
10780
|
-
};
|
|
10781
|
-
return null;
|
|
10696
|
+
function checkNodes(text, nodes, scope, problems) {
|
|
10697
|
+
for (const node of nodes) {
|
|
10698
|
+
const from = node.loc?.offset ?? 0;
|
|
10699
|
+
if (node.type === "Text") {
|
|
10700
|
+
checkBindings(text, node.content, from, scope, problems);
|
|
10701
|
+
continue;
|
|
10782
10702
|
}
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
|
|
10786
|
-
|
|
10787
|
-
const scope = shapeScope(context.loopVariables, page);
|
|
10788
|
-
if (isMember(text, context.start, start)) {
|
|
10789
|
-
const owner = ownerOf(text, context.start, start, scope);
|
|
10790
|
-
const shape = owner && membersOf(owner)[name];
|
|
10791
|
-
return owner && shape ? {
|
|
10792
|
-
symbol: {
|
|
10793
|
-
kind: "property",
|
|
10794
|
-
name,
|
|
10795
|
-
owner,
|
|
10796
|
-
shape
|
|
10797
|
-
},
|
|
10798
|
-
start,
|
|
10799
|
-
end
|
|
10800
|
-
} : null;
|
|
10801
|
-
}
|
|
10802
|
-
const variable = [...context.loopVariables].reverse().find((candidate) => candidate.name === name);
|
|
10803
|
-
if (variable) return {
|
|
10804
|
-
symbol: {
|
|
10805
|
-
kind: "loopVariable",
|
|
10806
|
-
variable,
|
|
10807
|
-
shape: scope?.loops.get(name)
|
|
10808
|
-
},
|
|
10809
|
-
start,
|
|
10810
|
-
end
|
|
10811
|
-
};
|
|
10812
|
-
return {
|
|
10813
|
-
symbol: {
|
|
10814
|
-
kind: "state",
|
|
10815
|
-
name
|
|
10816
|
-
},
|
|
10817
|
-
start,
|
|
10818
|
-
end
|
|
10819
|
-
};
|
|
10703
|
+
for (const value of Object.values(node.attributes)) checkBindings(text, value, from, scope, problems);
|
|
10704
|
+
if (node.tag !== "For") {
|
|
10705
|
+
checkNodes(text, node.children, scope, problems);
|
|
10706
|
+
continue;
|
|
10820
10707
|
}
|
|
10821
|
-
|
|
10708
|
+
const { each = "", item = "item", index = "index", key } = node.attributes;
|
|
10709
|
+
const itemOfList = itemShape(each, scope);
|
|
10710
|
+
if (itemOfList?.kind === "object" && key !== void 0 && !key.startsWith("*") && !(key in itemOfList.properties)) problems.push({
|
|
10711
|
+
message: `The <For> \`key\` "${key}" is not a property of \`${itemOfList.type}\`. It has: ${describeMembers(itemOfList)}`,
|
|
10712
|
+
offset: offsetOf(text, `key="${key}"`, from)
|
|
10713
|
+
});
|
|
10714
|
+
checkNodes(text, node.children, withLoop(scope, item, itemOfList, index), problems);
|
|
10822
10715
|
}
|
|
10823
10716
|
}
|
|
10824
|
-
function
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
if (CONTROL_FLOW_TAGS[symbol.tag]) {
|
|
10834
|
-
const doc = CONTROL_FLOW_ATTRIBUTE_DOCS[symbol.name];
|
|
10835
|
-
return doc ? `\`${symbol.name}\`: ${doc}` : null;
|
|
10836
|
-
}
|
|
10837
|
-
const component = components[symbol.tag];
|
|
10838
|
-
if (!component) return null;
|
|
10839
|
-
const type = component.props[symbol.name];
|
|
10840
|
-
if (type) return `\`${symbol.name}\`: ${describeType(type)}`;
|
|
10841
|
-
if (symbol.name.startsWith(DATA_PREFIX)) return `\`${symbol.name}\`: this element's handlers get it as \`event.data.${dataKey(symbol.name)}\``;
|
|
10842
|
-
if (isEventProp(symbol.name) && component.events.includes(eventName(symbol.name))) return `\`${symbol.name}\`: on \`${eventName(symbol.name)}\`, runs a mutation or action from controller.ts`;
|
|
10843
|
-
return null;
|
|
10844
|
-
}
|
|
10845
|
-
case "handler": {
|
|
10846
|
-
const { controller } = page;
|
|
10847
|
-
if (controller?.mutations?.includes(symbol.name)) return `mutation \`${symbol.name}\` in controller.ts`;
|
|
10848
|
-
if (controller?.actions?.includes(symbol.name)) return `action \`${symbol.name}\` in controller.ts`;
|
|
10849
|
-
return null;
|
|
10850
|
-
}
|
|
10851
|
-
case "state": return page.state?.keys?.includes(symbol.name) ? `state \`${symbol.name}\` in state.ts${typeSuffix(page.state.shapes?.[symbol.name])}` : null;
|
|
10852
|
-
case "loopVariable": return `\`${symbol.variable.name}\`: ${describeLoopVariable(symbol.variable, symbol.shape)}`;
|
|
10853
|
-
case "property": return `\`${symbol.name}\`: \`${symbol.shape.type}\`, a property of \`${symbol.owner.type}\``;
|
|
10854
|
-
case "static": return `image \`${symbol.path}\``;
|
|
10717
|
+
function checkBindings(text, value, from, scope, problems) {
|
|
10718
|
+
for (const [binding, source = ""] of value.matchAll(BINDING)) {
|
|
10719
|
+
const expression = parseBinding(source);
|
|
10720
|
+
if (!expression) continue;
|
|
10721
|
+
const bindingOffset = offsetOf(text, binding, from);
|
|
10722
|
+
for (const { name, owner } of unknownMembers(expression, scope)) problems.push({
|
|
10723
|
+
message: `\`${name}\` is not a property of \`${owner.type}\`. It has: ${describeMembers(owner)}`,
|
|
10724
|
+
offset: memberOffset(bindingOffset, binding, name)
|
|
10725
|
+
});
|
|
10855
10726
|
}
|
|
10856
10727
|
}
|
|
10857
|
-
function
|
|
10858
|
-
const
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
10863
|
-
|
|
10864
|
-
|
|
10865
|
-
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10728
|
+
function unknownMembers(expression, scope) {
|
|
10729
|
+
const unknownIn = (node) => unknownMembers(node, scope);
|
|
10730
|
+
switch (expression.type) {
|
|
10731
|
+
case "Literal":
|
|
10732
|
+
case "Identifier": return [];
|
|
10733
|
+
case "Member": {
|
|
10734
|
+
const inner = [...unknownIn(expression.object), ...unknownIn(expression.property)];
|
|
10735
|
+
const { property } = expression;
|
|
10736
|
+
if (property.type !== "Literal" || typeof property.value !== "string") return inner;
|
|
10737
|
+
const owner = shapeOf(expression.object, scope);
|
|
10738
|
+
if (owner && owner.kind !== "value" && !(property.value in membersOf(owner))) return [...inner, {
|
|
10739
|
+
name: property.value,
|
|
10740
|
+
owner
|
|
10741
|
+
}];
|
|
10742
|
+
return inner;
|
|
10743
|
+
}
|
|
10744
|
+
case "Unary": return unknownIn(expression.argument);
|
|
10745
|
+
case "Binary": return [...unknownIn(expression.left), ...unknownIn(expression.right)];
|
|
10746
|
+
case "Conditional": return [
|
|
10747
|
+
...unknownIn(expression.test),
|
|
10748
|
+
...unknownIn(expression.consequent),
|
|
10749
|
+
...unknownIn(expression.alternate)
|
|
10750
|
+
];
|
|
10869
10751
|
}
|
|
10870
10752
|
}
|
|
10871
|
-
function
|
|
10872
|
-
|
|
10873
|
-
|
|
10874
|
-
return type.map((value) => `"${value}"`).join(" | ");
|
|
10875
|
-
}
|
|
10876
|
-
function describeLoopVariable(variable, shape) {
|
|
10877
|
-
const list = variable.list ? ` \`${variable.list}\`` : "";
|
|
10878
|
-
return variable.role === "item" ? `each item of${list}${typeSuffix(shape)}` : `the position in${list}`;
|
|
10879
|
-
}
|
|
10880
|
-
function typeSuffix(shape) {
|
|
10881
|
-
return shape ? `: \`${shape.type}\`` : "";
|
|
10753
|
+
function offsetOf(text, written, from) {
|
|
10754
|
+
const at = text.indexOf(written, from);
|
|
10755
|
+
return at < 0 ? from : at;
|
|
10882
10756
|
}
|
|
10883
|
-
function
|
|
10884
|
-
|
|
10885
|
-
const
|
|
10886
|
-
return
|
|
10887
|
-
uri: pathToFileURL(file).href,
|
|
10888
|
-
range: {
|
|
10889
|
-
start: position,
|
|
10890
|
-
end: position
|
|
10891
|
-
}
|
|
10892
|
-
};
|
|
10757
|
+
function memberOffset(bindingOffset, binding, name) {
|
|
10758
|
+
const escapedName = name.replaceAll("$", "\\$");
|
|
10759
|
+
const match = new RegExp(`(?<=\\.\\s*)${escapedName}(?![\\w$])`).exec(binding);
|
|
10760
|
+
return match ? bindingOffset + match.index : bindingOffset;
|
|
10893
10761
|
}
|
|
10894
|
-
|
|
10762
|
+
//#endregion
|
|
10763
|
+
//#region src/lsp/diagnostics.ts
|
|
10764
|
+
function diagnose(document, file, page, components) {
|
|
10895
10765
|
const diagnostics = [];
|
|
10896
10766
|
const context = {
|
|
10897
10767
|
file,
|
|
10898
10768
|
components,
|
|
10899
|
-
onWarning: (warning) =>
|
|
10769
|
+
onWarning: (warning) => {
|
|
10770
|
+
diagnostics.push(sourceDiagnostic(document, warning, import_main.DiagnosticSeverity.Warning));
|
|
10771
|
+
}
|
|
10900
10772
|
};
|
|
10901
10773
|
if (page.statics) context.statics = page.statics;
|
|
10902
10774
|
if (page.state?.keys) context.stateKeys = page.state.keys;
|
|
10903
|
-
const
|
|
10775
|
+
const mutations = page.controller?.mutations;
|
|
10776
|
+
const actions = page.controller?.actions;
|
|
10904
10777
|
if (mutations && actions) context.handlers = [...mutations, ...actions];
|
|
10905
10778
|
try {
|
|
10906
10779
|
compileView(document.getText(), context);
|
|
@@ -10918,13 +10791,14 @@ function diagnosticAt(document, start, message, severity) {
|
|
|
10918
10791
|
const text = document.getText();
|
|
10919
10792
|
let end = start;
|
|
10920
10793
|
while (end < text.length && !/[\s>]/.test(text[end])) end++;
|
|
10794
|
+
const atLeastOneCharacter = Math.min(start + 1, text.length);
|
|
10921
10795
|
return {
|
|
10922
10796
|
severity,
|
|
10923
10797
|
source: "dot",
|
|
10924
10798
|
message,
|
|
10925
10799
|
range: {
|
|
10926
10800
|
start: document.positionAt(start),
|
|
10927
|
-
end: document.positionAt(Math.max(end,
|
|
10801
|
+
end: document.positionAt(Math.max(end, atLeastOneCharacter))
|
|
10928
10802
|
}
|
|
10929
10803
|
};
|
|
10930
10804
|
}
|
|
@@ -11001,13 +10875,13 @@ var StateTypes = class {
|
|
|
11001
10875
|
const key = root ?? file;
|
|
11002
10876
|
let project = this.#projects.get(key);
|
|
11003
10877
|
if (!project) {
|
|
11004
|
-
project = new
|
|
10878
|
+
project = new TypeScriptProject(root);
|
|
11005
10879
|
this.#projects.set(key, project);
|
|
11006
10880
|
}
|
|
11007
10881
|
return project.read(file, text);
|
|
11008
10882
|
}
|
|
11009
10883
|
};
|
|
11010
|
-
var
|
|
10884
|
+
var TypeScriptProject = class {
|
|
11011
10885
|
#options;
|
|
11012
10886
|
#fileNames;
|
|
11013
10887
|
#open = /* @__PURE__ */ new Map();
|
|
@@ -11062,9 +10936,9 @@ var Project = class {
|
|
|
11062
10936
|
function readConfig(root) {
|
|
11063
10937
|
const file = join(root, "tsconfig.json");
|
|
11064
10938
|
if (!existsSync(file)) return;
|
|
11065
|
-
const
|
|
11066
|
-
if (
|
|
11067
|
-
return ts.parseJsonConfigFileContent(
|
|
10939
|
+
const configFile = ts.readConfigFile(file, (path) => ts.sys.readFile(path));
|
|
10940
|
+
if (configFile.error) return;
|
|
10941
|
+
return ts.parseJsonConfigFileContent(configFile.config, ts.sys, root, void 0, file);
|
|
11068
10942
|
}
|
|
11069
10943
|
function propertyShape(checker, property, depth, seen) {
|
|
11070
10944
|
const shape = typeShape(checker, checker.getTypeOfSymbol(property), depth, seen);
|
|
@@ -11116,40 +10990,234 @@ function isRecord(checker, type) {
|
|
|
11116
10990
|
return (type.isUnion() ? type.types : [type]).every((member) => (member.flags & ts.TypeFlags.Object) !== 0 && member.getCallSignatures().length === 0 && checker.getIndexInfosOfType(member).length === 0);
|
|
11117
10991
|
}
|
|
11118
10992
|
//#endregion
|
|
10993
|
+
//#region src/lsp/symbols.ts
|
|
10994
|
+
const IDENTIFIER = /[\w$]/;
|
|
10995
|
+
const VALUE = /[^\s"']/;
|
|
10996
|
+
function hover(document, offset, page, components) {
|
|
10997
|
+
const found = symbolAt(document.getText(), offset, page);
|
|
10998
|
+
if (!found) return null;
|
|
10999
|
+
const markdown = describeSymbol(found.symbol, page, components);
|
|
11000
|
+
if (!markdown) return null;
|
|
11001
|
+
return {
|
|
11002
|
+
contents: {
|
|
11003
|
+
kind: import_main.MarkupKind.Markdown,
|
|
11004
|
+
value: markdown
|
|
11005
|
+
},
|
|
11006
|
+
range: {
|
|
11007
|
+
start: document.positionAt(found.start),
|
|
11008
|
+
end: document.positionAt(found.end)
|
|
11009
|
+
}
|
|
11010
|
+
};
|
|
11011
|
+
}
|
|
11012
|
+
function definition(document, offset, page) {
|
|
11013
|
+
const found = symbolAt(document.getText(), offset, page);
|
|
11014
|
+
if (!found) return null;
|
|
11015
|
+
const { symbol } = found;
|
|
11016
|
+
if (symbol.kind === "handler" && page.controller) {
|
|
11017
|
+
const { file, text, offsets } = page.controller;
|
|
11018
|
+
return locationIn(file, text, offsets[symbol.name]);
|
|
11019
|
+
}
|
|
11020
|
+
if (symbol.kind === "state" && page.state) {
|
|
11021
|
+
const { file, text, offsets } = page.state;
|
|
11022
|
+
return locationIn(file, text, offsets[symbol.name]);
|
|
11023
|
+
}
|
|
11024
|
+
if (symbol.kind === "property" && symbol.shape.declaration) {
|
|
11025
|
+
const { file, line, character } = symbol.shape.declaration;
|
|
11026
|
+
const position = {
|
|
11027
|
+
line,
|
|
11028
|
+
character
|
|
11029
|
+
};
|
|
11030
|
+
return {
|
|
11031
|
+
uri: pathToFileURL(file).href,
|
|
11032
|
+
range: {
|
|
11033
|
+
start: position,
|
|
11034
|
+
end: position
|
|
11035
|
+
}
|
|
11036
|
+
};
|
|
11037
|
+
}
|
|
11038
|
+
if (symbol.kind === "static" && page.root) return locationIn(join(page.root, symbol.path), "", 0);
|
|
11039
|
+
return null;
|
|
11040
|
+
}
|
|
11041
|
+
function symbolAt(text, offset, page) {
|
|
11042
|
+
const context = markupContextAt(text, offset);
|
|
11043
|
+
switch (context.kind) {
|
|
11044
|
+
case "tagName":
|
|
11045
|
+
case "closeTag": {
|
|
11046
|
+
const { start, end } = wordAt(text, offset, TAG_NAME);
|
|
11047
|
+
return {
|
|
11048
|
+
symbol: {
|
|
11049
|
+
kind: "component",
|
|
11050
|
+
tag: text.slice(start, end)
|
|
11051
|
+
},
|
|
11052
|
+
start,
|
|
11053
|
+
end
|
|
11054
|
+
};
|
|
11055
|
+
}
|
|
11056
|
+
case "attributeName": {
|
|
11057
|
+
const { start, end } = wordAt(text, offset, ATTRIBUTE_NAME);
|
|
11058
|
+
const name = text.slice(start, end);
|
|
11059
|
+
if (!name) return null;
|
|
11060
|
+
return {
|
|
11061
|
+
symbol: {
|
|
11062
|
+
kind: "attribute",
|
|
11063
|
+
tag: context.element.tag,
|
|
11064
|
+
name
|
|
11065
|
+
},
|
|
11066
|
+
start,
|
|
11067
|
+
end
|
|
11068
|
+
};
|
|
11069
|
+
}
|
|
11070
|
+
case "attributeValue": {
|
|
11071
|
+
const { start, end } = wordAt(text, offset, VALUE);
|
|
11072
|
+
const value = text.slice(start, end);
|
|
11073
|
+
if (isEventProp(context.attribute)) return {
|
|
11074
|
+
symbol: {
|
|
11075
|
+
kind: "handler",
|
|
11076
|
+
name: value
|
|
11077
|
+
},
|
|
11078
|
+
start,
|
|
11079
|
+
end
|
|
11080
|
+
};
|
|
11081
|
+
if (page.statics?.includes(value)) return {
|
|
11082
|
+
symbol: {
|
|
11083
|
+
kind: "static",
|
|
11084
|
+
path: value
|
|
11085
|
+
},
|
|
11086
|
+
start,
|
|
11087
|
+
end
|
|
11088
|
+
};
|
|
11089
|
+
return null;
|
|
11090
|
+
}
|
|
11091
|
+
case "binding": {
|
|
11092
|
+
const { start, end } = wordAt(text, offset, IDENTIFIER);
|
|
11093
|
+
const name = text.slice(start, end);
|
|
11094
|
+
if (!name) return null;
|
|
11095
|
+
const scope = bindingScope(context.loopVariables, page);
|
|
11096
|
+
if (isMember(text, context.start, start)) {
|
|
11097
|
+
const owner = ownerOf(text, context.start, start, scope);
|
|
11098
|
+
if (!owner) return null;
|
|
11099
|
+
const shape = membersOf(owner)[name];
|
|
11100
|
+
if (!shape) return null;
|
|
11101
|
+
return {
|
|
11102
|
+
symbol: {
|
|
11103
|
+
kind: "property",
|
|
11104
|
+
name,
|
|
11105
|
+
owner,
|
|
11106
|
+
shape
|
|
11107
|
+
},
|
|
11108
|
+
start,
|
|
11109
|
+
end
|
|
11110
|
+
};
|
|
11111
|
+
}
|
|
11112
|
+
const variable = [...context.loopVariables].reverse().find((candidate) => candidate.name === name);
|
|
11113
|
+
if (variable) return {
|
|
11114
|
+
symbol: {
|
|
11115
|
+
kind: "loopVariable",
|
|
11116
|
+
variable,
|
|
11117
|
+
shape: scope?.loops.get(name)
|
|
11118
|
+
},
|
|
11119
|
+
start,
|
|
11120
|
+
end
|
|
11121
|
+
};
|
|
11122
|
+
return {
|
|
11123
|
+
symbol: {
|
|
11124
|
+
kind: "state",
|
|
11125
|
+
name
|
|
11126
|
+
},
|
|
11127
|
+
start,
|
|
11128
|
+
end
|
|
11129
|
+
};
|
|
11130
|
+
}
|
|
11131
|
+
default: return null;
|
|
11132
|
+
}
|
|
11133
|
+
}
|
|
11134
|
+
function describeSymbol(symbol, page, components) {
|
|
11135
|
+
switch (symbol.kind) {
|
|
11136
|
+
case "component": {
|
|
11137
|
+
const controlFlowDoc = CONTROL_FLOW_DOCS[symbol.tag];
|
|
11138
|
+
if (controlFlowDoc) return `**<${symbol.tag}>** · control flow\n\n${controlFlowDoc}`;
|
|
11139
|
+
const component = components[symbol.tag];
|
|
11140
|
+
if (!component) return null;
|
|
11141
|
+
return describeComponent(component);
|
|
11142
|
+
}
|
|
11143
|
+
case "attribute": return describeAttribute(symbol.tag, symbol.name, components);
|
|
11144
|
+
case "handler": {
|
|
11145
|
+
const { controller } = page;
|
|
11146
|
+
if (controller?.mutations?.includes(symbol.name)) return `mutation \`${symbol.name}\` in controller.ts`;
|
|
11147
|
+
if (controller?.actions?.includes(symbol.name)) return `action \`${symbol.name}\` in controller.ts`;
|
|
11148
|
+
return null;
|
|
11149
|
+
}
|
|
11150
|
+
case "state": {
|
|
11151
|
+
if (!page.state?.keys?.includes(symbol.name)) return null;
|
|
11152
|
+
const shape = page.state.shapes?.[symbol.name];
|
|
11153
|
+
return `state \`${symbol.name}\` in state.ts${typeSuffix(shape)}`;
|
|
11154
|
+
}
|
|
11155
|
+
case "loopVariable": return `\`${symbol.variable.name}\`: ${describeLoopVariable(symbol.variable, symbol.shape)}`;
|
|
11156
|
+
case "property": return `\`${symbol.name}\`: \`${symbol.shape.type}\`, a property of \`${symbol.owner.type}\``;
|
|
11157
|
+
case "static": return `image \`${symbol.path}\``;
|
|
11158
|
+
}
|
|
11159
|
+
}
|
|
11160
|
+
function describeAttribute(tag, name, components) {
|
|
11161
|
+
if (CONTROL_FLOW_TAGS[tag]) {
|
|
11162
|
+
const doc = CONTROL_FLOW_ATTRIBUTE_DOCS[name];
|
|
11163
|
+
if (!doc) return null;
|
|
11164
|
+
return `\`${name}\`: ${doc}`;
|
|
11165
|
+
}
|
|
11166
|
+
const component = components[tag];
|
|
11167
|
+
if (!component) return null;
|
|
11168
|
+
const type = component.props[name];
|
|
11169
|
+
if (type) return `\`${name}\`: ${describeType(type)}`;
|
|
11170
|
+
if (name.startsWith(DATA_PREFIX)) return `\`${name}\`: this element's handlers get it as \`event.data.${dataKey(name)}\``;
|
|
11171
|
+
if (isEventProp(name) && component.events.includes(eventName(name))) return `\`${name}\`: on \`${eventName(name)}\`, runs a mutation or action from controller.ts`;
|
|
11172
|
+
return null;
|
|
11173
|
+
}
|
|
11174
|
+
function locationIn(file, text, offset) {
|
|
11175
|
+
if (offset === void 0) return null;
|
|
11176
|
+
const position = TextDocument.create("", "typescript", 0, text).positionAt(offset);
|
|
11177
|
+
return {
|
|
11178
|
+
uri: pathToFileURL(file).href,
|
|
11179
|
+
range: {
|
|
11180
|
+
start: position,
|
|
11181
|
+
end: position
|
|
11182
|
+
}
|
|
11183
|
+
};
|
|
11184
|
+
}
|
|
11185
|
+
//#endregion
|
|
11119
11186
|
//#region src/lsp/server.ts
|
|
11120
|
-
const
|
|
11187
|
+
const WATCHED_FILES = [
|
|
11121
11188
|
"**/*.ts",
|
|
11122
11189
|
"**/dot.json",
|
|
11123
11190
|
"**/statics/**"
|
|
11124
11191
|
];
|
|
11125
11192
|
function startLanguageServer(connection) {
|
|
11126
11193
|
const documents = new import_main.TextDocuments(TextDocument);
|
|
11127
|
-
const
|
|
11194
|
+
const components = builtinRegistry;
|
|
11128
11195
|
const stateTypes = new StateTypes();
|
|
11129
11196
|
let canWatchFiles = false;
|
|
11130
11197
|
const readText = async (file) => {
|
|
11131
|
-
const
|
|
11132
|
-
if (
|
|
11133
|
-
|
|
11198
|
+
const openDocument = documents.get(pathToFileURL(file).href);
|
|
11199
|
+
if (openDocument) return openDocument.getText();
|
|
11200
|
+
if (!existsSync(file)) return;
|
|
11201
|
+
return readFile(file, "utf8");
|
|
11134
11202
|
};
|
|
11135
|
-
const
|
|
11203
|
+
const openView = async (uri) => {
|
|
11136
11204
|
const document = documents.get(uri);
|
|
11137
11205
|
if (!document || !uri.endsWith(".dml")) return;
|
|
11138
11206
|
const file = fileURLToPath(uri);
|
|
11139
11207
|
return {
|
|
11140
11208
|
document,
|
|
11141
|
-
|
|
11142
|
-
file
|
|
11209
|
+
file,
|
|
11210
|
+
page: await readPage(file, readText, stateTypes)
|
|
11143
11211
|
};
|
|
11144
11212
|
};
|
|
11145
11213
|
const validate = async (uri) => {
|
|
11146
11214
|
try {
|
|
11147
|
-
const
|
|
11148
|
-
if (!
|
|
11149
|
-
const diagnostics = diagnose(
|
|
11215
|
+
const view = await openView(uri);
|
|
11216
|
+
if (!view) return;
|
|
11217
|
+
const diagnostics = diagnose(view.document, view.file, view.page, components);
|
|
11150
11218
|
await connection.sendDiagnostics({
|
|
11151
11219
|
uri,
|
|
11152
|
-
version:
|
|
11220
|
+
version: view.document.version,
|
|
11153
11221
|
diagnostics
|
|
11154
11222
|
});
|
|
11155
11223
|
} catch (error) {
|
|
@@ -11178,10 +11246,14 @@ function startLanguageServer(connection) {
|
|
|
11178
11246
|
};
|
|
11179
11247
|
});
|
|
11180
11248
|
connection.onInitialized(() => {
|
|
11181
|
-
if (canWatchFiles)
|
|
11249
|
+
if (canWatchFiles) {
|
|
11250
|
+
const watchers = WATCHED_FILES.map((globPattern) => ({ globPattern }));
|
|
11251
|
+
connection.client.register(import_main.DidChangeWatchedFilesNotification.type, { watchers });
|
|
11252
|
+
}
|
|
11182
11253
|
});
|
|
11183
11254
|
documents.onDidChangeContent(({ document }) => {
|
|
11184
|
-
document.uri.endsWith(".dml")
|
|
11255
|
+
if (document.uri.endsWith(".dml")) validate(document.uri);
|
|
11256
|
+
else validateAll();
|
|
11185
11257
|
});
|
|
11186
11258
|
documents.onDidClose(({ document }) => {
|
|
11187
11259
|
connection.sendDiagnostics({
|
|
@@ -11193,19 +11265,22 @@ function startLanguageServer(connection) {
|
|
|
11193
11265
|
validateAll();
|
|
11194
11266
|
});
|
|
11195
11267
|
connection.onCompletion(async ({ textDocument, position }) => {
|
|
11196
|
-
const
|
|
11197
|
-
if (!
|
|
11198
|
-
|
|
11268
|
+
const view = await openView(textDocument.uri);
|
|
11269
|
+
if (!view) return [];
|
|
11270
|
+
const offset = view.document.offsetAt(position);
|
|
11271
|
+
return complete(view.document, offset, view.page, components);
|
|
11199
11272
|
});
|
|
11200
11273
|
connection.onHover(async ({ textDocument, position }) => {
|
|
11201
|
-
const
|
|
11202
|
-
if (!
|
|
11203
|
-
|
|
11274
|
+
const view = await openView(textDocument.uri);
|
|
11275
|
+
if (!view) return null;
|
|
11276
|
+
const offset = view.document.offsetAt(position);
|
|
11277
|
+
return hover(view.document, offset, view.page, components);
|
|
11204
11278
|
});
|
|
11205
11279
|
connection.onDefinition(async ({ textDocument, position }) => {
|
|
11206
|
-
const
|
|
11207
|
-
if (!
|
|
11208
|
-
|
|
11280
|
+
const view = await openView(textDocument.uri);
|
|
11281
|
+
if (!view) return null;
|
|
11282
|
+
const offset = view.document.offsetAt(position);
|
|
11283
|
+
return definition(view.document, offset, view.page);
|
|
11209
11284
|
});
|
|
11210
11285
|
documents.listen(connection);
|
|
11211
11286
|
connection.listen();
|
|
@@ -11217,4 +11292,4 @@ function serveStdio() {
|
|
|
11217
11292
|
//#endregion
|
|
11218
11293
|
export { serveStdio };
|
|
11219
11294
|
|
|
11220
|
-
//# sourceMappingURL=server-
|
|
11295
|
+
//# sourceMappingURL=server-B9-F93mS.js.map
|