@hawkeyexl/manni 2.8.5 → 2.9.0-cite-marker-ids.1

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.
@@ -75,7 +75,7 @@ import {
75
75
  withValueAt,
76
76
  writeEncryptionKey,
77
77
  writeFileAtomic
78
- } from "./chunk-4UI4MDAG.js";
78
+ } from "./chunk-DI3NC57Z.js";
79
79
 
80
80
  // src/meta/core/reencrypt.ts
81
81
  import { extname } from "node:path";
@@ -832,11 +832,32 @@ function nextLineStart(content, offset) {
832
832
  if (offset === 0 || content.charCodeAt(offset - 1) === 10) return offset;
833
833
  return lineEnd(content, offset) + 1;
834
834
  }
835
+ var MAX_IDS_PER_MARKER = 25;
835
836
  function payloadOf(payload) {
836
837
  if (payload === "") return { kind: "bad", reason: "empty payload" };
837
- if (payload.startsWith("{")) return { kind: "bad", reason: "a JSON payload", json: true };
838
- if (ID.test(payload)) return { kind: "ref", id: payload };
839
- return { kind: "bad", reason: "payload is not an id" };
838
+ if (/[\r\n]/.test(payload)) {
839
+ return { kind: "bad", reason: "a marker is one line; write two markers" };
840
+ }
841
+ const words = payload.split(" ").filter((word) => word !== "");
842
+ if (words.some((word) => word.startsWith("{"))) {
843
+ return { kind: "bad", reason: "a JSON payload", json: true };
844
+ }
845
+ if (words.length > MAX_IDS_PER_MARKER) {
846
+ return {
847
+ kind: "bad",
848
+ reason: `more than ${String(MAX_IDS_PER_MARKER)} ids in one marker (${String(words.length)}); write a second marker`
849
+ };
850
+ }
851
+ const bad = words.find((word) => !ID.test(word));
852
+ if (bad !== void 0) return { kind: "bad", reason: `"${bad}" is not an id` };
853
+ const seen = /* @__PURE__ */ new Set();
854
+ for (const word of words) {
855
+ if (seen.has(word)) {
856
+ return { kind: "bad", reason: `"${word}" is named twice in one marker` };
857
+ }
858
+ seen.add(word);
859
+ }
860
+ return { kind: "ref", ids: words };
840
861
  }
841
862
  function isCite(inner) {
842
863
  return inner === "cite" || inner.startsWith("cite") && /\s/.test(inner.charAt(4));
@@ -1017,7 +1038,15 @@ function writtenForm(format) {
1017
1038
  function formatStatement(format, payload) {
1018
1039
  const form = writtenForm(format);
1019
1040
  const pad = form.open.endsWith("(") ? "" : " ";
1020
- return `${form.open}${pad}cite ${payload.id}${pad}${form.close}`;
1041
+ return `${form.open}${pad}cite ${payload.ids.join(" ")}${pad}${form.close}`;
1042
+ }
1043
+ function respellStatement(content, statement, ids) {
1044
+ const text = content.slice(statement.start, statement.end);
1045
+ const at2 = text.indexOf(statement.raw);
1046
+ if (at2 === -1) return content;
1047
+ const respelled = `cite ${ids.join(" ")}`;
1048
+ const marker = text.slice(0, at2) + respelled + text.slice(at2 + statement.raw.length);
1049
+ return content.slice(0, statement.start) + marker + content.slice(statement.end);
1021
1050
  }
1022
1051
  function nextTextLine(text, offset, format) {
1023
1052
  let pos = nextLineStart(text, offset);
@@ -1408,34 +1437,58 @@ function readPage(file, content, opts) {
1408
1437
  );
1409
1438
  continue;
1410
1439
  }
1411
- const target = byId.get(payload.id);
1412
- if (target === void 0) {
1413
- findings.push(
1414
- finding("marker-orphan", `no entry has id "${payload.id}"`, {
1415
- line: statement.line,
1416
- id: payload.id
1417
- })
1418
- );
1419
- continue;
1420
- }
1421
- const first = markedAt.get(target);
1422
- if (first !== void 0) {
1440
+ const marked = [];
1441
+ for (const id of payload.ids) {
1442
+ const target = byId.get(id);
1443
+ if (target === void 0) {
1444
+ findings.push(
1445
+ finding("marker-orphan", `no entry has id "${id}"`, {
1446
+ line: statement.line,
1447
+ id
1448
+ })
1449
+ );
1450
+ continue;
1451
+ }
1452
+ marked.push(target);
1453
+ const first = markedAt.get(target);
1454
+ if (first !== void 0) {
1455
+ findings.push(
1456
+ finding(
1457
+ "marker-repeated",
1458
+ `${id} is named by markers at lines ${String(first)} and ${String(statement.line)}; the first anchors it.`,
1459
+ {
1460
+ line: statement.line,
1461
+ id,
1462
+ index: target.origin.index,
1463
+ src: spellSource(target.citation.source)
1464
+ }
1465
+ )
1466
+ );
1467
+ continue;
1468
+ }
1469
+ markedAt.set(target, statement.line);
1470
+ target.marker = statement;
1471
+ }
1472
+ const quoted2 = marked.find((entry) => entry.citation.quote === true);
1473
+ const plain = marked.find((entry) => entry.citation.quote !== true);
1474
+ if (quoted2 !== void 0 && plain !== void 0) {
1475
+ const mixed = {
1476
+ line: statement.line,
1477
+ index: quoted2.origin.index,
1478
+ src: spellSource(quoted2.citation.source)
1479
+ };
1480
+ if (quoted2.citation.id !== void 0) mixed.id = quoted2.citation.id;
1423
1481
  findings.push(
1424
1482
  finding(
1425
- "marker-repeated",
1426
- `${payload.id} is named by markers at lines ${String(first)} and ${String(statement.line)}; the first anchors it.`,
1427
- {
1428
- line: statement.line,
1429
- id: payload.id,
1430
- index: target.origin.index,
1431
- src: spellSource(target.citation.source)
1432
- }
1483
+ "anchor-invalid",
1484
+ named(
1485
+ quoted2.citation.id,
1486
+ "a quote entry shares a marker with a non-quote entry. Give the quote its own marker."
1487
+ ),
1488
+ mixed
1433
1489
  )
1434
1490
  );
1435
- continue;
1436
1491
  }
1437
- markedAt.set(target, statement.line);
1438
- target.marker = statement;
1439
1492
  }
1440
1493
  for (const entry of citations) {
1441
1494
  const { citation, origin, marker } = entry;
@@ -1985,7 +2038,7 @@ function readThen(input, text) {
1985
2038
  const id = input.entry.citation.id;
1986
2039
  if (id === void 0) return void 0;
1987
2040
  const marker = then.statements.find(
1988
- (statement) => statement.payload.kind === "ref" && statement.payload.id === id
2041
+ (statement) => statement.payload.kind === "ref" && statement.payload.ids.includes(id)
1989
2042
  );
1990
2043
  if (marker === void 0) return void 0;
1991
2044
  return { page: then, lines, marker };
@@ -3834,7 +3887,7 @@ async function runAdd(opts) {
3834
3887
  }
3835
3888
  if (marker && opts.id !== void 0) {
3836
3889
  const already = page.statements.find(
3837
- (s) => s.payload.kind === "ref" && s.payload.id === opts.id
3890
+ (s) => s.payload.kind === "ref" && s.payload.ids.includes(opts.id ?? "")
3838
3891
  );
3839
3892
  if (already !== void 0) {
3840
3893
  throw new CiteError(
@@ -3887,11 +3940,17 @@ async function runAdd(opts) {
3887
3940
  const sourceIndex = await sourceIndexFor(root, client);
3888
3941
  let body = content;
3889
3942
  let markerAt2;
3943
+ let markerJoined = false;
3890
3944
  let claimSpan;
3891
3945
  let claim;
3892
3946
  let elsewhere = [];
3893
3947
  let shifted = { frontmatter: [], manifest: /* @__PURE__ */ new Map() };
3894
3948
  if (marker && pageLines !== void 0) {
3949
+ if (isMarkerLine(lines[pageLines.start - 1] ?? "", format)) {
3950
+ throw new CiteError(
3951
+ `${label}:${String(pageLines.start)} is a marker line. A marker there would change its pin.`
3952
+ );
3953
+ }
3895
3954
  const holding = unitHolding(content, pageLines.start, format, {
3896
3955
  offset: page.bodyOffset,
3897
3956
  line: page.bodyLine
@@ -3910,18 +3969,30 @@ async function runAdd(opts) {
3910
3969
  `${at2} is in a ${holding.kind} at ${spellAt(holding)} that ${unitWide}. A marker anchors the whole ${holding.kind}.`
3911
3970
  );
3912
3971
  }
3913
- shifted = shiftedEntries({
3914
- citations: page.citations,
3915
- at: [holding.start],
3916
- delta: 1,
3917
- bodyLine: page.bodyLine,
3918
- label
3919
- });
3920
- const statement = markerIndent(content, holding.start, format, page.bodyLine) + formatStatement(format, { kind: "ref", id: opts.id ?? "" });
3921
- const insertAt = offsetOfLine(content, holding.start);
3922
- body = insertStatementBefore(content, insertAt, statement);
3923
- markerAt2 = holding.start;
3924
- const unit = anchoredLines(body, insertAt + statement.length, format, quote);
3972
+ const above = page.statements.filter((s) => s.anchorLine === holding.start);
3973
+ const nearest = above[above.length - 1];
3974
+ const held = nearest !== void 0 && nearest.payload.kind === "ref" && nearest.payload.ids.length < MAX_IDS_PER_MARKER ? nearest.payload.ids : void 0;
3975
+ let after2;
3976
+ if (nearest !== void 0 && held !== void 0) {
3977
+ body = respellStatement(content, nearest, [...held, opts.id ?? ""]);
3978
+ after2 = nearest.end + (body.length - content.length);
3979
+ markerAt2 = nearest.line;
3980
+ markerJoined = true;
3981
+ } else {
3982
+ shifted = shiftedEntries({
3983
+ citations: page.citations,
3984
+ at: [holding.start],
3985
+ delta: 1,
3986
+ bodyLine: page.bodyLine,
3987
+ label
3988
+ });
3989
+ const statement = markerIndent(content, holding.start, format, page.bodyLine) + formatStatement(format, { kind: "ref", ids: [opts.id ?? ""] });
3990
+ const insertAt = offsetOfLine(content, holding.start);
3991
+ body = insertStatementBefore(content, insertAt, statement);
3992
+ after2 = insertAt + statement.length;
3993
+ markerAt2 = holding.start;
3994
+ }
3995
+ const unit = anchoredLines(body, after2, format, quote);
3925
3996
  const pin = unit === void 0 ? void 0 : pinOfLines(splitLines(body), unit);
3926
3997
  if (unit === void 0 || pin === void 0) {
3927
3998
  throw new CiteError(`${at2} has no paragraph or block for a marker to anchor.`);
@@ -4019,6 +4090,7 @@ async function runAdd(opts) {
4019
4090
  ...placed === void 0 ? {} : { manifest: placed }
4020
4091
  };
4021
4092
  if (markerAt2 !== void 0) result.markerLine = markerAt2 + shift;
4093
+ if (markerJoined) result.markerJoined = true;
4022
4094
  if (claimSpan !== void 0) {
4023
4095
  result.claimLines = { start: claimSpan.start + shift, end: claimSpan.end + shift };
4024
4096
  }
@@ -4105,7 +4177,15 @@ async function runRemove(opts) {
4105
4177
  }
4106
4178
  return [];
4107
4179
  };
4108
- const markersFor = (id) => id === void 0 ? [] : page.statements.filter((s) => s.payload.kind === "ref" && s.payload.id === id).map((s) => s.line);
4180
+ const markersFor = (id) => id === void 0 ? [] : page.statements.filter(
4181
+ (s) => s.payload.kind === "ref" && s.payload.ids.includes(id)
4182
+ );
4183
+ const dropped = /* @__PURE__ */ new Map();
4184
+ const drop = (statement, id) => {
4185
+ const held = dropped.get(statement.line) ?? { statement, ids: /* @__PURE__ */ new Set() };
4186
+ held.ids.add(id);
4187
+ dropped.set(statement.line, held);
4188
+ };
4109
4189
  const removed = [];
4110
4190
  const indices = /* @__PURE__ */ new Set();
4111
4191
  const markerLines = [];
@@ -4116,20 +4196,22 @@ async function runRemove(opts) {
4116
4196
  const id = idOf(input?.entry);
4117
4197
  const markers2 = markersFor(id);
4118
4198
  indices.add(index);
4119
- markerLines.push(...markers2);
4199
+ markerLines.push(...markers2.map((s) => s.line));
4200
+ if (id !== void 0) for (const statement of markers2) drop(statement, id);
4120
4201
  removed.push({
4121
4202
  ...id === void 0 ? {} : { id },
4122
4203
  index,
4123
4204
  ...input === void 0 ? {} : { origin: input.origin },
4124
- markerLines: markers2
4205
+ markerLines: markers2.map((s) => s.line)
4125
4206
  });
4126
4207
  matched.add(value);
4127
4208
  }
4128
4209
  if (found.length > 0) continue;
4129
4210
  const markers = markersFor(value);
4130
4211
  if (markers.length === 0) continue;
4131
- markerLines.push(...markers);
4132
- removed.push({ id: value, markerLines: markers });
4212
+ markerLines.push(...markers.map((s) => s.line));
4213
+ for (const statement of markers) drop(statement, value);
4214
+ removed.push({ id: value, markerLines: markers.map((s) => s.line) });
4133
4215
  matched.add(value);
4134
4216
  }
4135
4217
  const nothing = { file: label, removed: [], diff: "", written: false };
@@ -4138,16 +4220,24 @@ async function runRemove(opts) {
4138
4220
  planned.push({ page: nothing, ...path === void 0 ? {} : { path } });
4139
4221
  return;
4140
4222
  }
4141
- const ordered = [...new Set(markerLines)].sort((a, b) => a - b);
4142
- for (const line of ordered) {
4223
+ const touched = [...new Set(markerLines)].sort((a, b) => a - b);
4224
+ for (const line of touched) {
4143
4225
  if (!isMarkerLine(lines[line - 1] ?? "", format)) {
4144
4226
  const id = page.statements.find((s) => s.line === line)?.payload;
4145
- const named4 = id?.kind === "ref" ? ` ${id.id}` : "";
4227
+ const named4 = id?.kind === "ref" ? ` ${id.ids.join(" ")}` : "";
4146
4228
  throw new CiteError(
4147
4229
  `the marker${named4} at ${label}:${String(line)} shares its line with text; remove it by hand.`
4148
4230
  );
4149
4231
  }
4150
4232
  }
4233
+ const kept = /* @__PURE__ */ new Map();
4234
+ const ordered = [];
4235
+ for (const line of touched) {
4236
+ const held = dropped.get(line);
4237
+ const ids = held === void 0 || held.statement.payload.kind !== "ref" ? [] : held.statement.payload.ids.filter((id) => !held.ids.has(id));
4238
+ if (ids.length === 0) ordered.push(line);
4239
+ else kept.set(line, ids);
4240
+ }
4151
4241
  const survivors = page.citations.filter((c) => !indices.has(c.origin.index));
4152
4242
  const shifted = shiftedEntries({
4153
4243
  citations: survivors,
@@ -4160,6 +4250,10 @@ async function runRemove(opts) {
4160
4250
  for (const { index, lines: moved } of shifted.frontmatter) {
4161
4251
  after = spliceEntryField(after, format, index, ["claim", "lines"], moved);
4162
4252
  }
4253
+ for (const [line, ids] of kept) {
4254
+ const held = dropped.get(line);
4255
+ if (held !== void 0) after = respellStatement(after, held.statement, ids);
4256
+ }
4163
4257
  for (const line of [...ordered].sort((a, b) => b - a)) after = removeLine(after, line);
4164
4258
  if (owner === void 0) {
4165
4259
  after = removeFrontmatterCitations(after, format, indices, label);
@@ -4169,12 +4263,12 @@ async function runRemove(opts) {
4169
4263
  `${label} carries no ${owner.join}: value, so its citations cannot be keyed in ${owner.file}.`
4170
4264
  );
4171
4265
  }
4172
- const kept = inputs.map((input, index) => {
4266
+ const kept2 = inputs.map((input, index) => {
4173
4267
  const moved = shifted.manifest.get(index);
4174
4268
  return moved === void 0 ? input.entry : withClaimLines(input.entry, moved);
4175
4269
  }).filter((_entry, index) => !indices.has(index));
4176
- if (kept.length === 0) await manifests.remove(owner, setup.sidecar.entry);
4177
- else void await manifests.write(owner, setup.sidecar.entry, kept, 0);
4270
+ if (kept2.length === 0) await manifests.remove(owner, setup.sidecar.entry);
4271
+ else void await manifests.write(owner, setup.sidecar.entry, kept2, 0);
4178
4272
  }
4179
4273
  const out = {
4180
4274
  file: label,
@@ -6117,4 +6211,4 @@ export {
6117
6211
  runKeyRotate,
6118
6212
  runKeySet
6119
6213
  };
6120
- //# sourceMappingURL=chunk-GRPDC5BY.js.map
6214
+ //# sourceMappingURL=chunk-33FPHH3J.js.map