@abco20/btxml-checker 0.1.3 → 0.1.5

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/server.cjs CHANGED
@@ -34993,6 +34993,7 @@ var RuleCodes = {
34993
34993
  ConflictingModelKind: "BT120_CONFLICTING_MODEL_KIND",
34994
34994
  UnusedModelDefinition: "BT121_UNUSED_MODEL_DEFINITION",
34995
34995
  DuplicateModelDefinition: "BT122_DUPLICATE_MODEL_DEFINITION",
34996
+ MissingLocalModelDefinition: "BT123_MISSING_LOCAL_MODEL_DEFINITION",
34996
34997
  ExternalModelFileNotFound: "BT321_EXTERNAL_MODEL_FILE_NOT_FOUND",
34997
34998
  AugmentationFileNotFound: "BT324_AUGMENTATION_FILE_NOT_FOUND",
34998
34999
  MissingTreeNodesModel: "BT322_MISSING_TREENODESMODEL",
@@ -35213,8 +35214,8 @@ var RULES = {
35213
35214
  },
35214
35215
  "model/require-output-port-remap": {
35215
35216
  code: RuleCodes.OutputPortRequiresRemap,
35216
- defaultSeverity: "warn",
35217
- description: "Resolved output ports must write to a blackboard remap."
35217
+ defaultSeverity: "error",
35218
+ description: "Resolved output ports must be explicitly or default-remapped to a blackboard entry."
35218
35219
  },
35219
35220
  "model/no-childless-control-shape-mismatch": {
35220
35221
  code: RuleCodes.ChildCapableNodeSelfClosing,
@@ -35246,6 +35247,11 @@ var RULES = {
35246
35247
  defaultSeverity: "error",
35247
35248
  description: "Inline model definitions should be used in the same BT XML file."
35248
35249
  },
35250
+ "model/require-local-definition": {
35251
+ code: RuleCodes.MissingLocalModelDefinition,
35252
+ defaultSeverity: "error",
35253
+ description: "When models.convention is used-only, normal node usages should have a local TreeNodesModel definition in the same file."
35254
+ },
35249
35255
  "model/no-duplicate-definition": {
35250
35256
  code: RuleCodes.DuplicateModelDefinition,
35251
35257
  defaultSeverity: "error",
@@ -39197,6 +39203,8 @@ async function resolveIncludeGraph(input) {
39197
39203
  var RULE_CONFLICTING_KIND = "model/no-conflicting-kind-for-id";
39198
39204
  var RULE_UNUSED_DEFINITION = "model/no-unused-definition";
39199
39205
  var RULE_DUPLICATE_DEFINITION = "model/no-duplicate-definition";
39206
+ var RULE_REQUIRE_LOCAL_DEFINITION = "model/require-local-definition";
39207
+ var NORMAL_MODEL_KINDS = /* @__PURE__ */ new Set(["Action", "Condition", "Decorator", "Control"]);
39200
39208
  function createConventionDiagnostic(input) {
39201
39209
  const diagnostic = createDiagnostic(
39202
39210
  input.code,
@@ -39223,6 +39231,9 @@ function resolveConventionSeverity(config2, rule) {
39223
39231
  function definitionRange(definition) {
39224
39232
  return definition.model.idRange ?? definition.range;
39225
39233
  }
39234
+ function definitionDeleteRange(definition) {
39235
+ return definition.range;
39236
+ }
39226
39237
  function definitionInfo(definition) {
39227
39238
  return {
39228
39239
  uri: definition.uri,
@@ -39231,6 +39242,30 @@ function definitionInfo(definition) {
39231
39242
  range: definition.range
39232
39243
  };
39233
39244
  }
39245
+ function isNormalModelKind(kind) {
39246
+ return NORMAL_MODEL_KINDS.has(kind);
39247
+ }
39248
+ function toFixableModel(index, id) {
39249
+ const resolved = getNodeModel(index, id);
39250
+ if (!resolved) return void 0;
39251
+ if (!isNormalModelKind(resolved.kind)) return void 0;
39252
+ const hasConflict = getModelConflicts(index).some(
39253
+ (conflict) => conflict.id === id && (conflict.code === "BT012_CONFLICTING_NODE_MODEL" || conflict.code === "BT107_CONFLICTING_PORT_DEFAULT")
39254
+ );
39255
+ if (hasConflict) return void 0;
39256
+ return {
39257
+ id: resolved.id,
39258
+ kind: resolved.kind,
39259
+ ports: resolved.ports.map((port) => ({
39260
+ direction: port.direction,
39261
+ name: port.name,
39262
+ type: port.type,
39263
+ defaultValue: port.defaultValue,
39264
+ description: port.description,
39265
+ enum: port.enum
39266
+ }))
39267
+ };
39268
+ }
39234
39269
  function validateConflictingKinds(input) {
39235
39270
  const severity = resolveConventionSeverity(input.config, RULE_CONFLICTING_KIND);
39236
39271
  if (!severity) return [];
@@ -39274,17 +39309,75 @@ function validateConflictingKinds(input) {
39274
39309
  function validateUsedOnly(input) {
39275
39310
  if (input.config.models.convention !== "used-only") return [];
39276
39311
  const severity = resolveConventionSeverity(input.config, RULE_UNUSED_DEFINITION);
39277
- if (!severity) return [];
39312
+ const missingSeverity = resolveConventionSeverity(input.config, RULE_REQUIRE_LOCAL_DEFINITION);
39313
+ if (!severity && !missingSeverity) return [];
39278
39314
  const diagnostics = [];
39279
39315
  const usagesByUri = getNodeUsagesByUri(input.index);
39280
- for (const definition of input.facts) {
39281
- if (definition.isBuiltin) continue;
39316
+ const allInlineByUri = buildAllInlineDefinitionsByUri(input.facts);
39317
+ const localNormalByUri = buildLocalNormalDefinitionsByUri(input.facts);
39318
+ const relevantUris = /* @__PURE__ */ new Set([
39319
+ ...usagesByUri.keys(),
39320
+ ...localNormalByUri.keys(),
39321
+ ...allInlineByUri.keys()
39322
+ ]);
39323
+ for (const uri of relevantUris) {
39324
+ const sameFileNodeUsages = usagesByUri.get(uri) ?? [];
39325
+ const usedNodeIds = getUsedNodeIds(sameFileNodeUsages);
39326
+ const localNormalDefinitions = localNormalByUri.get(uri) ?? [];
39327
+ const localNormalIds = new Set(localNormalDefinitions.map((definition) => definition.id));
39328
+ const localAnyIds = new Set((allInlineByUri.get(uri) ?? []).map((definition) => definition.id));
39329
+ if (severity) {
39330
+ diagnostics.push(
39331
+ ...createUnusedDefinitionDiagnostics(localNormalDefinitions, usedNodeIds, severity)
39332
+ );
39333
+ }
39334
+ if (missingSeverity) {
39335
+ diagnostics.push(
39336
+ ...createMissingLocalDefinitionDiagnostics({
39337
+ index: input.index,
39338
+ uri,
39339
+ usedNodeIds,
39340
+ localNormalIds,
39341
+ localAnyIds,
39342
+ sameFileNodeUsages,
39343
+ severity: missingSeverity
39344
+ })
39345
+ );
39346
+ }
39347
+ }
39348
+ return diagnostics;
39349
+ }
39350
+ function buildAllInlineDefinitionsByUri(facts) {
39351
+ const allInlineByUri = /* @__PURE__ */ new Map();
39352
+ for (const definition of facts) {
39282
39353
  if (definition.sourceKind !== "inline-tree-nodes-model") continue;
39283
- if (definition.kind === "SubTree") continue;
39284
- const sameFileNodeUsages = usagesByUri.get(definition.uri ?? "") ?? [];
39285
- const usedNodeIds = new Set(
39286
- sameFileNodeUsages.filter((usage) => usage.kind === "node").map((usage) => usage.id)
39287
- );
39354
+ if (!definition.uri) continue;
39355
+ const allInline = allInlineByUri.get(definition.uri) ?? [];
39356
+ allInline.push(definition);
39357
+ allInlineByUri.set(definition.uri, allInline);
39358
+ }
39359
+ return allInlineByUri;
39360
+ }
39361
+ function buildLocalNormalDefinitionsByUri(facts) {
39362
+ const localNormalByUri = /* @__PURE__ */ new Map();
39363
+ for (const definition of facts) {
39364
+ if (definition.sourceKind !== "inline-tree-nodes-model") continue;
39365
+ if (!definition.uri) continue;
39366
+ if (!isNormalModelKind(definition.kind)) continue;
39367
+ const localNormal = localNormalByUri.get(definition.uri) ?? [];
39368
+ localNormal.push(definition);
39369
+ localNormalByUri.set(definition.uri, localNormal);
39370
+ }
39371
+ return localNormalByUri;
39372
+ }
39373
+ function getUsedNodeIds(sameFileNodeUsages) {
39374
+ return new Set(
39375
+ sameFileNodeUsages.filter((usage) => usage.kind === "node").map((usage) => usage.id)
39376
+ );
39377
+ }
39378
+ function createUnusedDefinitionDiagnostics(localNormalDefinitions, usedNodeIds, severity) {
39379
+ const diagnostics = [];
39380
+ for (const definition of localNormalDefinitions) {
39288
39381
  if (usedNodeIds.has(definition.id)) continue;
39289
39382
  diagnostics.push(
39290
39383
  createConventionDiagnostic({
@@ -39299,10 +39392,47 @@ function validateUsedOnly(input) {
39299
39392
  nodeId: definition.id,
39300
39393
  modelKind: definition.kind,
39301
39394
  sourceKind: "inline-tree-nodes-model",
39302
- fix: definition.uri && definitionRange(definition) && definition.editable ? {
39395
+ fix: definition.uri && definitionDeleteRange(definition) && definition.editable ? {
39303
39396
  kind: "delete-definition",
39304
39397
  uri: definition.uri,
39305
- range: definitionRange(definition)
39398
+ range: definitionDeleteRange(definition)
39399
+ } : void 0
39400
+ }
39401
+ })
39402
+ );
39403
+ }
39404
+ return diagnostics;
39405
+ }
39406
+ function createMissingLocalDefinitionDiagnostics(input) {
39407
+ const diagnostics = [];
39408
+ for (const nodeId of input.usedNodeIds) {
39409
+ if (input.localNormalIds.has(nodeId)) continue;
39410
+ const hasNormalModel = getNodeModelDefinitions(input.index, nodeId).some(
39411
+ (definition) => isNormalModelKind(definition.kind)
39412
+ );
39413
+ if (!hasNormalModel) continue;
39414
+ const hasNonNormalLocalDefinition = input.localAnyIds.has(nodeId);
39415
+ const firstUsage = input.sameFileNodeUsages.find(
39416
+ (usage) => usage.kind === "node" && usage.id === nodeId
39417
+ );
39418
+ const fixableModel = hasNonNormalLocalDefinition ? void 0 : toFixableModel(input.index, nodeId);
39419
+ diagnostics.push(
39420
+ createConventionDiagnostic({
39421
+ code: "BT123_MISSING_LOCAL_MODEL_DEFINITION",
39422
+ message: `missing local model definition \`${nodeId}\` in this file`,
39423
+ uri: input.uri,
39424
+ range: firstUsage?.elementRange ?? firstUsage?.range,
39425
+ rule: RULE_REQUIRE_LOCAL_DEFINITION,
39426
+ severity: input.severity,
39427
+ data: {
39428
+ kind: "missing-local-model-definition",
39429
+ nodeId,
39430
+ sourceKind: "inline-tree-nodes-model",
39431
+ fix: fixableModel && input.uri ? {
39432
+ kind: "add-local-definition",
39433
+ uri: input.uri,
39434
+ nodeId,
39435
+ model: fixableModel
39306
39436
  } : void 0
39307
39437
  }
39308
39438
  })
@@ -39317,7 +39447,7 @@ function duplicateFix(definitions) {
39317
39447
  if (!keep?.uri) return void 0;
39318
39448
  const deleteTargets = definitions.filter((definition) => definition !== keep);
39319
39449
  if (deleteTargets.some(
39320
- (definition) => !definition.uri || !definitionRange(definition) || !definition.editable
39450
+ (definition) => !definition.uri || !definitionDeleteRange(definition) || !definition.editable
39321
39451
  )) {
39322
39452
  return void 0;
39323
39453
  }
@@ -39329,7 +39459,7 @@ function duplicateFix(definitions) {
39329
39459
  },
39330
39460
  delete: deleteTargets.map((definition) => ({
39331
39461
  uri: definition.uri,
39332
- range: definitionRange(definition)
39462
+ range: definitionDeleteRange(definition)
39333
39463
  }))
39334
39464
  };
39335
39465
  }
@@ -40256,7 +40386,9 @@ var modelRules = [
40256
40386
  }),
40257
40387
  makeRuleModule({
40258
40388
  name: "model/valid-port-name",
40259
- meta: { description: "Port names must be valid XML attribute names for BT nodes." },
40389
+ meta: {
40390
+ description: "Port names must be valid XML attribute names for BT nodes."
40391
+ },
40260
40392
  create(context) {
40261
40393
  return {
40262
40394
  TreeNodeModel(node) {
@@ -40301,7 +40433,9 @@ var modelRules = [
40301
40433
  }),
40302
40434
  makeRuleModule({
40303
40435
  name: "model/valid-port-default-value",
40304
- meta: { description: "TreeNodesModel port defaults must match the declared type." },
40436
+ meta: {
40437
+ description: "TreeNodesModel port defaults must match the declared type."
40438
+ },
40305
40439
  create(context) {
40306
40440
  return {
40307
40441
  Element(element) {
@@ -40411,7 +40545,7 @@ var modelRules = [
40411
40545
  makeRuleModule({
40412
40546
  name: "model/require-output-port-remap",
40413
40547
  meta: {
40414
- description: "Resolved output ports must write to a blackboard remap."
40548
+ description: "Resolved output ports must be explicitly or default-remapped to a blackboard entry."
40415
40549
  },
40416
40550
  create(context) {
40417
40551
  return {
@@ -40419,19 +40553,34 @@ var modelRules = [
40419
40553
  if (isStructuralElement2(element)) return;
40420
40554
  const usage = context.getNodeUsage(element);
40421
40555
  if (usage.model.status !== "resolved" && usage.tagForm !== "subtree") return;
40422
- for (const attr of element.attributes) {
40423
- const portUsage = context.getPortUsage(element, attr.name);
40424
- if (portUsage?.status !== "resolved") continue;
40425
- if (portUsage.port.direction !== "output") continue;
40426
- if (getExactBlackboardReference(portUsage.port.name, attr.value) !== void 0)
40556
+ for (const port of usage.ports) {
40557
+ if (port.direction !== "output") continue;
40558
+ const binding = usage.portUsages.find(
40559
+ (candidate) => candidate.name === port.name && candidate.status === "resolved"
40560
+ );
40561
+ if (binding) {
40562
+ if (getExactBlackboardReference(port.name, binding.value) !== void 0) continue;
40563
+ context.report({
40564
+ code: RuleCodes.OutputPortRequiresRemap,
40565
+ message: `output port \`${port.name}\` must be remapped to a blackboard entry`,
40566
+ range: binding.attribute.range,
40567
+ details: {
40568
+ primaryLabel: `output port \`${port.name}\` requires a blackboard remap`,
40569
+ help: `use \`${port.name}="{${port.name}}"\` or \`${port.name}="{some_key}"\``
40570
+ }
40571
+ });
40572
+ continue;
40573
+ }
40574
+ if (port.defaultValue !== void 0 && getExactBlackboardReference(port.name, port.defaultValue) !== void 0) {
40427
40575
  continue;
40576
+ }
40428
40577
  context.report({
40429
40578
  code: RuleCodes.OutputPortRequiresRemap,
40430
- message: `output port \`${portUsage.port.name}\` must be remapped to a blackboard entry`,
40431
- range: attr.range,
40579
+ message: `output port \`${port.name}\` must be remapped to a blackboard entry`,
40580
+ range: element.range,
40432
40581
  details: {
40433
- primaryLabel: `output port \`${portUsage.port.name}\` requires a blackboard remap`,
40434
- help: `use \`${portUsage.port.name}="{${portUsage.port.name}}"\` or \`${portUsage.port.name}="{some_key}"\``
40582
+ primaryLabel: `output port \`${port.name}\` requires a blackboard remap`,
40583
+ help: `use \`${port.name}="{${port.name}}"\` or \`${port.name}="{some_key}"\``
40435
40584
  }
40436
40585
  });
40437
40586
  }
@@ -44002,12 +44151,15 @@ function removeAttributeEdit(document, attribute) {
44002
44151
  return { range, newText: "" };
44003
44152
  }
44004
44153
  function addAttributeEdit(document, element, name) {
44154
+ return addAttributeWithValueEdit(document, element, name, "");
44155
+ }
44156
+ function addAttributeWithValueEdit(document, element, name, value) {
44005
44157
  const closeOffset = Math.max(
44006
44158
  element.openTagRange.end.offset - (element.selfClosing ? 2 : 1),
44007
44159
  element.openTagRange.start.offset
44008
44160
  );
44009
44161
  const pos = document.positionAt(closeOffset);
44010
- return { range: sourceRange(pos, pos), newText: ` ${name}=""` };
44162
+ return { range: sourceRange(pos, pos), newText: ` ${name}="${value}"` };
44011
44163
  }
44012
44164
 
44013
44165
  // ../language-service/src/ranges.ts
@@ -44028,84 +44180,160 @@ function getCodeActions(context, input) {
44028
44180
  });
44029
44181
  }
44030
44182
  for (const diag of diagnostics) {
44031
- if (!diag.range) continue;
44183
+ if (!hasRange(diag)) continue;
44032
44184
  const inspect = inspectXmlCursor({
44033
44185
  document: input.document,
44034
44186
  parsed: context.parsed,
44035
44187
  position: diag.range.start
44036
44188
  });
44037
- if (diag.code === "BT101_MISSING_REQUIRED_PORT") {
44038
- const targetOffset = Math.min(diag.range.start.offset + 1, diag.range.end.offset);
44039
- const target = context.parsed?.root ? findElementAt(context.parsed.root, targetOffset) || ("element" in inspect ? inspect.element : void 0) : "element" in inspect ? inspect.element : void 0;
44040
- if (!target) continue;
44041
- const usage = resolveNodeUsage(context.semantic, {
44042
- element: target,
44043
- documentRoot: context.parsed?.root,
44044
- uri: input.document.uri,
44045
- config: context.config,
44046
- policy: context.nodeUsagePolicy
44047
- });
44048
- const missing = usage.ports.find(
44049
- (port) => port.required && !usage.portUsages.some(
44050
- (binding) => binding.status === "resolved" && binding.name === port.name
44051
- )
44052
- );
44053
- if (missing) {
44054
- actions.push({
44055
- title: `Add missing port ${missing.name}`,
44056
- kind: "quickfix",
44057
- diagnostics: [diag],
44058
- edits: [addAttributeEdit(input.document, target, missing.name)]
44059
- });
44060
- }
44061
- }
44062
- if (diag.code === "BT102_UNKNOWN_PORT" && "attribute" in inspect && "element" in inspect && inspect.attribute) {
44063
- const usage = inspect.element ? resolveNodeUsage(context.semantic, {
44064
- element: inspect.element,
44065
- documentRoot: context.parsed?.root,
44066
- uri: input.document.uri,
44067
- config: context.config,
44068
- policy: context.nodeUsagePolicy
44069
- }) : void 0;
44070
- const unknown2 = usage?.portUsages.find(
44071
- (candidate) => candidate.status === "undeclared" && candidate.attribute === inspect.attribute
44072
- );
44073
- const edit = unknown2 ? removeAttributeEdit(input.document, inspect.attribute) : void 0;
44074
- if (edit && unknown2) {
44075
- actions.push({
44076
- title: `Remove unknown port ${inspect.attribute.name}`,
44077
- kind: "quickfix",
44078
- diagnostics: [diag],
44079
- edits: [edit]
44080
- });
44081
- }
44082
- }
44083
- if (diag.code === "BT002_MISSING_BTCPP_FORMAT" && context.parsed?.root) {
44084
- const insertPos = input.document.positionAt(
44085
- context.parsed.root.nameRange?.end.offset || context.parsed.root.openTagRange.end.offset - 1
44086
- );
44189
+ const target = resolveTargetElement(context, input, diag, inspect);
44190
+ addMissingRequiredPortAction(actions, context, input, diag.code, diag, target);
44191
+ addOutputRemapAction(actions, context, input, diag.code, diag, inspect, target);
44192
+ addUnknownPortAction(actions, context, input, diag.code, diag, inspect);
44193
+ addMissingBtcppFormatAction(actions, context, input, diag.code, diag);
44194
+ addSuppressionAction(actions, input, diag);
44195
+ }
44196
+ return { actions };
44197
+ }
44198
+ function hasRange(diag) {
44199
+ return !!diag.range;
44200
+ }
44201
+ function isResolvedPortUsage(binding) {
44202
+ return binding.status === "resolved";
44203
+ }
44204
+ function resolveUsage(context, input, element) {
44205
+ return resolveNodeUsage(context.semantic, {
44206
+ element,
44207
+ documentRoot: context.parsed?.root,
44208
+ uri: input.document.uri,
44209
+ config: context.config,
44210
+ policy: context.nodeUsagePolicy
44211
+ });
44212
+ }
44213
+ function inspectElement(inspect) {
44214
+ return "element" in inspect ? inspect.element : void 0;
44215
+ }
44216
+ function resolveTargetElement(context, input, diag, inspect) {
44217
+ const fallback = inspectElement(inspect);
44218
+ const root = context.parsed?.root;
44219
+ if (!root || !diag.range) return fallback;
44220
+ const targetOffset = Math.min(diag.range.start.offset + 1, diag.range.end.offset);
44221
+ return findElementAt(root, targetOffset) ?? fallback;
44222
+ }
44223
+ function addMissingRequiredPortAction(actions, context, input, code, diag, target) {
44224
+ if (code !== "BT101_MISSING_REQUIRED_PORT") return;
44225
+ if (!target) return;
44226
+ const usage = resolveUsage(context, input, target);
44227
+ const missing = usage.ports.find(
44228
+ (port) => port.required && !usage.portUsages.some(
44229
+ (binding) => binding.status === "resolved" && binding.name === port.name
44230
+ )
44231
+ );
44232
+ if (!missing) return;
44233
+ actions.push({
44234
+ title: `Add missing port ${missing.name}`,
44235
+ kind: "quickfix",
44236
+ diagnostics: [diag],
44237
+ edits: [addAttributeEdit(input.document, target, missing.name)]
44238
+ });
44239
+ }
44240
+ function addOutputRemapAction(actions, context, input, code, diag, inspect, target) {
44241
+ if (code !== "BT115_OUTPUT_PORT_REQUIRES_REMAP") return;
44242
+ if (!target) return;
44243
+ const outputPortName = /`([^`]+)`/.exec(diag.message)?.[1];
44244
+ const usage = resolveUsage(context, input, target);
44245
+ const attribute = "attribute" in inspect ? inspect.attribute : void 0;
44246
+ if (attribute) {
44247
+ const binding = usage.portUsages.find(
44248
+ (candidate) => isResolvedPortUsage(candidate) && candidate.port.direction === "output" && candidate.attribute === attribute && (!outputPortName || candidate.port.name === outputPortName)
44249
+ );
44250
+ if (binding) {
44087
44251
  actions.push({
44088
- title: 'Add BTCPP_format="4" to <root>',
44252
+ title: `Remap output port ${binding.port.name}`,
44089
44253
  kind: "quickfix",
44090
44254
  diagnostics: [diag],
44091
- edits: [{ range: sourceRange(insertPos, insertPos), newText: ' BTCPP_format="4"' }]
44255
+ edits: [
44256
+ {
44257
+ range: attribute.valueContentRange ?? attribute.valueRange,
44258
+ newText: `{${binding.port.name}}`
44259
+ }
44260
+ ]
44092
44261
  });
44262
+ return;
44093
44263
  }
44094
- actions.push({
44095
- title: `Suppress ${diag.code} for next line`,
44096
- kind: "quickfix",
44097
- diagnostics: [diag],
44098
- edits: [
44099
- insertAtLineStart(
44100
- input.document,
44101
- diag.range.start.line,
44102
- `<!-- btxml-disable-next-line ${diag.code} reason: TODO -->
44103
- `
44104
- )
44105
- ]
44106
- });
44107
44264
  }
44108
- return { actions };
44265
+ const missingOutputPort = usage.ports.find(
44266
+ (port) => port.direction === "output" && (!outputPortName || port.name === outputPortName) && !usage.portUsages.some(
44267
+ (binding) => binding.status === "resolved" && binding.name === port.name
44268
+ )
44269
+ );
44270
+ if (!missingOutputPort) return;
44271
+ actions.push({
44272
+ title: `Remap output port ${missingOutputPort.name}`,
44273
+ kind: "quickfix",
44274
+ diagnostics: [diag],
44275
+ edits: [
44276
+ addAttributeWithValueEdit(
44277
+ input.document,
44278
+ target,
44279
+ missingOutputPort.name,
44280
+ `{${missingOutputPort.name}}`
44281
+ )
44282
+ ]
44283
+ });
44284
+ }
44285
+ function addUnknownPortAction(actions, context, input, code, diag, inspect) {
44286
+ if (code !== "BT102_UNKNOWN_PORT") return;
44287
+ if (!("attribute" in inspect && inspect.attribute)) return;
44288
+ const element = inspectElement(inspect);
44289
+ if (!element) return;
44290
+ const usage = resolveUsage(context, input, element);
44291
+ const unknown2 = usage.portUsages.find(
44292
+ (candidate) => candidate.status === "undeclared" && candidate.attribute === inspect.attribute
44293
+ );
44294
+ if (!unknown2) return;
44295
+ const edit = removeAttributeEdit(input.document, inspect.attribute);
44296
+ if (!edit) return;
44297
+ actions.push({
44298
+ title: `Remove unknown port ${inspect.attribute.name}`,
44299
+ kind: "quickfix",
44300
+ diagnostics: [diag],
44301
+ edits: [edit]
44302
+ });
44303
+ }
44304
+ function addMissingBtcppFormatAction(actions, context, input, code, diag) {
44305
+ if (code !== "BT002_MISSING_BTCPP_FORMAT") return;
44306
+ if (!context.parsed?.root) return;
44307
+ const insertPos = input.document.positionAt(
44308
+ context.parsed.root.nameRange?.end.offset || context.parsed.root.openTagRange.end.offset - 1
44309
+ );
44310
+ actions.push({
44311
+ title: 'Add BTCPP_format="4" to <root>',
44312
+ kind: "quickfix",
44313
+ diagnostics: [diag],
44314
+ edits: [
44315
+ {
44316
+ range: sourceRange(insertPos, insertPos),
44317
+ newText: ' BTCPP_format="4"'
44318
+ }
44319
+ ]
44320
+ });
44321
+ }
44322
+ function addSuppressionAction(actions, input, diag) {
44323
+ if (!diag.range) return;
44324
+ actions.push({
44325
+ title: `Suppress ${diag.code} for next line`,
44326
+ kind: "quickfix",
44327
+ diagnostics: [diag],
44328
+ edits: [
44329
+ insertAtLineStart(
44330
+ input.document,
44331
+ diag.range.start.line,
44332
+ `<!-- btxml-disable-next-line ${diag.code} reason: TODO -->
44333
+ `
44334
+ )
44335
+ ]
44336
+ });
44109
44337
  }
44110
44338
 
44111
44339
  // ../language-service/src/completions.ts
@@ -46852,7 +47080,7 @@ function handleGetChildCapability(workspace, params) {
46852
47080
  }
46853
47081
 
46854
47082
  // src/server.ts
46855
- var SERVER_VERSION = true ? "0.1.3" : "unknown";
47083
+ var SERVER_VERSION = true ? "0.1.5" : "unknown";
46856
47084
  var connection = (0, import_node4.createConnection)(import_node4.ProposedFeatures.all);
46857
47085
  var documents = new import_node4.TextDocuments(TextDocument);
46858
47086
  var openUris = /* @__PURE__ */ new Set();