@hawkeyexl/manni 2.2.0 → 2.3.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.
package/README.md CHANGED
@@ -98,7 +98,7 @@ actually reads rather than hand-written:
98
98
  ```yaml
99
99
  repos:
100
100
  - repo: https://github.com/hawkeyexl/manni
101
- rev: v2.2.0
101
+ rev: v2.3.1
102
102
  hooks:
103
103
  - id: manni-meta
104
104
  ```
@@ -69,7 +69,7 @@ import {
69
69
  withValueAt,
70
70
  writeEncryptionKey,
71
71
  writeFileAtomic
72
- } from "./chunk-5O4FIGDJ.js";
72
+ } from "./chunk-HUHERW7E.js";
73
73
 
74
74
  // src/meta/core/reencrypt.ts
75
75
  import { extname } from "node:path";
@@ -491,51 +491,12 @@ async function readSource(root, index, range, key) {
491
491
  }
492
492
  }
493
493
 
494
- // src/cite/core/statements.ts
495
- var HTML_COMMENT = { open: "<!--", close: "-->" };
496
- var JSX_COMMENT = { open: "{/*", close: "*/}" };
497
- var LINK_REFERENCE = { open: "[comment]: # (", close: ")" };
498
- var MARKDOWN_FORMS = [HTML_COMMENT, JSX_COMMENT, LINK_REFERENCE];
499
- var MDX_FORMS = [JSX_COMMENT, HTML_COMMENT, LINK_REFERENCE];
500
- var COMMENT_FORMS = [{ open: "<!--", close: "-->" }];
501
- var ASCIIDOC_FORMS = [{ open: "// (", close: ")" }];
502
- var RST_FORMS = [{ open: ".. (", close: ")" }];
503
- var ID = /^[a-z0-9][a-z0-9-]*$/;
504
- var ANY_FENCE = /^(?:[ \t]*(?:`{3,}|~{3,})|-{4,})/;
505
- var MARKDOWN_FENCE = /^(`{3,}|~{3,})/;
506
- var ASCIIDOC_FENCE = /^(-{4,})/;
494
+ // src/shared/code-regions.ts
507
495
  var SKIP_MARKDOWN_FENCE = /^[ \t]*(`{3,}|~{3,})/;
508
- var SKIP_ASCIIDOC_FENCE = ASCIIDOC_FENCE;
496
+ var SKIP_ASCIIDOC_FENCE = /^(-{4,})/;
509
497
  var SPAN_FORMATS = /* @__PURE__ */ new Set(["markdown", "mdx", "asciidoc", "rst"]);
510
- function statementForms(format) {
511
- switch (format) {
512
- case "markdown":
513
- return MARKDOWN_FORMS;
514
- case "mdx":
515
- return MDX_FORMS;
516
- case "html":
517
- case "xml":
518
- return COMMENT_FORMS;
519
- case "asciidoc":
520
- return ASCIIDOC_FORMS;
521
- case "rst":
522
- return RST_FORMS;
523
- default:
524
- return [];
525
- }
526
- }
498
+ var BACKTICK = 96;
527
499
  function fenceFor(format) {
528
- switch (format) {
529
- case "markdown":
530
- case "mdx":
531
- return MARKDOWN_FENCE;
532
- case "asciidoc":
533
- return ASCIIDOC_FENCE;
534
- default:
535
- return void 0;
536
- }
537
- }
538
- function skipFenceFor(format) {
539
500
  switch (format) {
540
501
  case "markdown":
541
502
  case "mdx":
@@ -546,24 +507,23 @@ function skipFenceFor(format) {
546
507
  return void 0;
547
508
  }
548
509
  }
549
- var BACKTICK = 96;
550
510
  function backtickRun(text, i) {
551
511
  let n = 0;
552
512
  while (text.charCodeAt(i + n) === BACKTICK) n++;
553
513
  return n;
554
514
  }
555
- function spanRegions(text, lineStart, out) {
515
+ function spanRegions(line, lineStart, out) {
556
516
  let i = 0;
557
- while (i < text.length) {
558
- const n = backtickRun(text, i);
517
+ while (i < line.length) {
518
+ const n = backtickRun(line, i);
559
519
  if (n === 0) {
560
520
  i++;
561
521
  continue;
562
522
  }
563
523
  let j = i + n;
564
524
  let closer = -1;
565
- while (j < text.length) {
566
- const m = backtickRun(text, j);
525
+ while (j < line.length) {
526
+ const m = backtickRun(line, j);
567
527
  if (m === 0) {
568
528
  j++;
569
529
  continue;
@@ -582,40 +542,87 @@ function spanRegions(text, lineStart, out) {
582
542
  i = closer + n;
583
543
  }
584
544
  }
585
- function closesFence(text, open) {
586
- const trimmed = text.trim();
545
+ function closesFence(line, open) {
546
+ const trimmed = line.trim();
587
547
  let n = 0;
588
548
  while (trimmed.charAt(n) === open.char) n++;
589
549
  return n >= open.length && n === trimmed.length;
590
550
  }
591
- function codeRegions(body, format) {
592
- const fence = skipFenceFor(format);
593
- const spans = SPAN_FORMATS.has(format);
551
+ function codeRegions(text, format, options = {}) {
552
+ const fence = fenceFor(format);
553
+ const spans = options.spans !== false && SPAN_FORMATS.has(format);
594
554
  const out = [];
595
555
  if (fence === void 0 && !spans) return out;
596
556
  let open;
597
557
  let pos = 0;
598
- while (pos < body.length) {
599
- const end = lineEnd(body, pos);
600
- const text = lineText(body, pos, end);
558
+ while (pos < text.length) {
559
+ const nl = text.indexOf("\n", pos);
560
+ const end = nl === -1 ? text.length : nl;
561
+ const raw = text.slice(pos, end);
562
+ const line = raw.endsWith("\r") ? raw.slice(0, -1) : raw;
601
563
  if (open !== void 0) {
602
- if (closesFence(text, open)) {
564
+ if (closesFence(line, open)) {
603
565
  out.push({ start: open.start, end });
604
566
  open = void 0;
605
567
  }
606
568
  } else {
607
- const opener = fence?.exec(text)?.[1];
569
+ const opener = fence?.exec(line)?.[1];
608
570
  if (opener !== void 0) {
609
571
  open = { char: opener.charAt(0), length: opener.length, start: pos };
610
572
  } else if (spans) {
611
- spanRegions(text, pos, out);
573
+ spanRegions(line, pos, out);
612
574
  }
613
575
  }
614
576
  pos = end + 1;
615
577
  }
616
- if (open !== void 0) out.push({ start: open.start, end: body.length });
578
+ if (open !== void 0) out.push({ start: open.start, end: text.length });
617
579
  return out;
618
580
  }
581
+ function codeEndAt(regions, offset) {
582
+ return regions.find((r) => offset >= r.start && offset < r.end)?.end;
583
+ }
584
+
585
+ // src/cite/core/statements.ts
586
+ var HTML_COMMENT = { open: "<!--", close: "-->" };
587
+ var JSX_COMMENT = { open: "{/*", close: "*/}" };
588
+ var LINK_REFERENCE = { open: "[comment]: # (", close: ")" };
589
+ var MARKDOWN_FORMS = [HTML_COMMENT, JSX_COMMENT, LINK_REFERENCE];
590
+ var MDX_FORMS = [JSX_COMMENT, HTML_COMMENT, LINK_REFERENCE];
591
+ var COMMENT_FORMS = [{ open: "<!--", close: "-->" }];
592
+ var ASCIIDOC_FORMS = [{ open: "// (", close: ")" }];
593
+ var RST_FORMS = [{ open: ".. (", close: ")" }];
594
+ var ID = /^[a-z0-9][a-z0-9-]*$/;
595
+ var ANY_FENCE = /^(?:[ \t]*(?:`{3,}|~{3,})|-{4,})/;
596
+ var MARKDOWN_FENCE = /^(`{3,}|~{3,})/;
597
+ var ASCIIDOC_FENCE = /^(-{4,})/;
598
+ function statementForms(format) {
599
+ switch (format) {
600
+ case "markdown":
601
+ return MARKDOWN_FORMS;
602
+ case "mdx":
603
+ return MDX_FORMS;
604
+ case "html":
605
+ case "xml":
606
+ return COMMENT_FORMS;
607
+ case "asciidoc":
608
+ return ASCIIDOC_FORMS;
609
+ case "rst":
610
+ return RST_FORMS;
611
+ default:
612
+ return [];
613
+ }
614
+ }
615
+ function fenceFor2(format) {
616
+ switch (format) {
617
+ case "markdown":
618
+ case "mdx":
619
+ return MARKDOWN_FENCE;
620
+ case "asciidoc":
621
+ return ASCIIDOC_FENCE;
622
+ default:
623
+ return void 0;
624
+ }
625
+ }
619
626
  function lineEnd(content, pos) {
620
627
  const nl = content.indexOf("\n", pos);
621
628
  return nl === -1 ? content.length : nl;
@@ -637,17 +644,29 @@ function payloadOf(payload) {
637
644
  if (ID.test(payload)) return { kind: "ref", id: payload };
638
645
  return { kind: "bad", reason: "payload is not an id" };
639
646
  }
647
+ function isCite(inner) {
648
+ return inner === "cite" || inner.startsWith("cite") && /\s/.test(inner.charAt(4));
649
+ }
650
+ function isMarkerLine(line, format) {
651
+ const text = line.trim();
652
+ return statementForms(format).some((form) => {
653
+ if (text.length < form.open.length + form.close.length) return false;
654
+ if (!text.startsWith(form.open)) return false;
655
+ const closeAt = text.indexOf(form.close, form.open.length);
656
+ if (closeAt !== text.length - form.close.length) return false;
657
+ return isCite(text.slice(form.open.length, closeAt).trim());
658
+ });
659
+ }
640
660
  function parseStatements(body, format, from = { offset: 0, line: 1 }) {
641
661
  const out = [];
642
662
  const fileLine = (bodyOffset) => from.line + lineAt(body, bodyOffset) - 1;
643
663
  const code = codeRegions(body, format);
644
- const codeEndAt = (at2) => code.find((r) => at2 >= r.start && at2 < r.end)?.end;
645
664
  for (const form of statementForms(format)) {
646
665
  let cursor = 0;
647
666
  for (; ; ) {
648
667
  const at2 = body.indexOf(form.open, cursor);
649
668
  if (at2 === -1) break;
650
- const codeEnd = codeEndAt(at2);
669
+ const codeEnd = codeEndAt(code, at2);
651
670
  if (codeEnd !== void 0) {
652
671
  cursor = codeEnd;
653
672
  continue;
@@ -657,9 +676,7 @@ function parseStatements(body, format, from = { offset: 0, line: 1 }) {
657
676
  const end = closeAt + form.close.length;
658
677
  cursor = end;
659
678
  const inner = body.slice(at2 + form.open.length, closeAt).trim();
660
- if (!(inner === "cite" || inner.startsWith("cite") && /\s/.test(inner.charAt(4)))) {
661
- continue;
662
- }
679
+ if (!isCite(inner)) continue;
663
680
  const payload = inner.slice(4).trim();
664
681
  const unit = anchoredLines(body, end, format);
665
682
  const anchorLine = unit === void 0 ? void 0 : from.line + unit.start - 1;
@@ -680,14 +697,15 @@ function detectEol(text) {
680
697
  const nl = text.indexOf("\n");
681
698
  return nl > 0 && text.charCodeAt(nl - 1) === 13 ? "\r\n" : "\n";
682
699
  }
683
- function paragraphAfter(content, offset) {
700
+ function paragraphAfter(content, offset, format) {
701
+ const marker = (text) => format !== void 0 && isMarkerLine(text, format);
684
702
  let pos = offset;
685
703
  const firstEnd = lineEnd(content, pos);
686
704
  if (lineText(content, pos, firstEnd).trim() === "") pos = firstEnd + 1;
687
705
  while (pos < content.length) {
688
706
  const end = lineEnd(content, pos);
689
707
  const text = lineText(content, pos, end);
690
- if (text.trim() === "") {
708
+ if (text.trim() === "" || marker(text)) {
691
709
  pos = end + 1;
692
710
  continue;
693
711
  }
@@ -698,7 +716,7 @@ function paragraphAfter(content, offset) {
698
716
  while (cursor < content.length) {
699
717
  const e = lineEnd(content, cursor);
700
718
  const t = lineText(content, cursor, e);
701
- if (t.trim() === "" || ANY_FENCE.test(t)) break;
719
+ if (t.trim() === "" || ANY_FENCE.test(t) || marker(t)) break;
702
720
  last = e;
703
721
  cursor = e + 1;
704
722
  }
@@ -737,7 +755,7 @@ function scanFence(content, offset, fence) {
737
755
  return void 0;
738
756
  }
739
757
  function fencedBlockAfter(content, offset, format) {
740
- const fence = fenceFor(format);
758
+ const fence = fenceFor2(format);
741
759
  if (!fence) return void 0;
742
760
  const block = scanFence(content, offset, fence);
743
761
  if (!block) return void 0;
@@ -755,7 +773,7 @@ function fencedBlockAt(content, offset) {
755
773
  return { start, end, line, text };
756
774
  }
757
775
  function fencedBlocks(content, bodyOffset, format) {
758
- const fence = fenceFor(format);
776
+ const fence = fenceFor2(format);
759
777
  if (!fence) return [];
760
778
  const out = [];
761
779
  let pos = bodyOffset;
@@ -779,17 +797,18 @@ function formatStatement(format, payload) {
779
797
  const pad = form.open.endsWith("(") ? "" : " ";
780
798
  return `${form.open}${pad}cite ${payload.id}${pad}${form.close}`;
781
799
  }
782
- function nextNonBlankLine(text, offset) {
800
+ function nextTextLine(text, offset, format) {
783
801
  let pos = nextLineStart(text, offset);
784
802
  while (pos < text.length) {
785
803
  const end = lineEnd(text, pos);
786
- if (lineText(text, pos, end).trim() !== "") return lineAt(text, pos);
804
+ const line = lineText(text, pos, end);
805
+ if (line.trim() !== "" && !isMarkerLine(line, format)) return lineAt(text, pos);
787
806
  pos = end + 1;
788
807
  }
789
808
  return void 0;
790
809
  }
791
810
  function fenceSpanAt(text, line, format) {
792
- const fence = fenceFor(format);
811
+ const fence = fenceFor2(format);
793
812
  if (!fence) return void 0;
794
813
  const block = scanFence(text, offsetOfLine(text, line), fence);
795
814
  if (!block || block.line !== line) return void 0;
@@ -800,6 +819,27 @@ function insideFence(text, bodyOffset, line, format) {
800
819
  (block) => line > block.line && line <= lineAt(text, block.end)
801
820
  );
802
821
  }
822
+ function unitHolding(text, line, format, body) {
823
+ const textOf = (n) => lineText(text, offsetOfLine(text, n), lineEnd(text, offsetOfLine(text, n)));
824
+ const own = textOf(line);
825
+ if (own.trim() === "" || isMarkerLine(own, format)) return void 0;
826
+ const block = fencedBlocks(text, body.offset, format).find(
827
+ (b) => line >= b.line && line <= lineAt(text, b.end)
828
+ );
829
+ if (block !== void 0) {
830
+ return { start: block.line, end: lineAt(text, block.end), kind: "block" };
831
+ }
832
+ if (ANY_FENCE.test(own)) return void 0;
833
+ let start = line;
834
+ while (start - 1 >= body.line) {
835
+ const above = textOf(start - 1);
836
+ if (above.trim() === "" || ANY_FENCE.test(above) || isMarkerLine(above, format)) break;
837
+ start--;
838
+ }
839
+ const paragraph = paragraphAfter(text, offsetOfLine(text, start), format);
840
+ if (paragraph?.line !== start) return void 0;
841
+ return { start, end: lineAt(text, paragraph.end), kind: "paragraph" };
842
+ }
803
843
  function anchoredLines(text, after, format, quote = false) {
804
844
  if (quote) {
805
845
  const block = fencedBlockAfter(text, after, format);
@@ -810,11 +850,11 @@ function anchoredLines(text, after, format, quote = false) {
810
850
  const line2 = lineAt(text, after);
811
851
  return { start: line2, end: line2 };
812
852
  }
813
- const paragraph = paragraphAfter(text, after);
853
+ const paragraph = paragraphAfter(text, after, format);
814
854
  if (paragraph !== void 0) {
815
855
  return { start: paragraph.line, end: lineAt(text, paragraph.end) };
816
856
  }
817
- const line = nextNonBlankLine(text, after);
857
+ const line = nextTextLine(text, after, format);
818
858
  return line === void 0 ? void 0 : fenceSpanAt(text, line, format);
819
859
  }
820
860
  function lineAt(content, offset) {
@@ -1320,7 +1360,7 @@ function subjectOf(entry) {
1320
1360
  function pickExtractor(file, format) {
1321
1361
  if (format !== void 0) {
1322
1362
  const forced = extractorByName(format);
1323
- if (!forced?.implemented) {
1363
+ if (forced === void 0) {
1324
1364
  throw new CiteError(
1325
1365
  `Unknown format "${format}". Supported extensions: ${supportedExtensions().join(", ")}.`
1326
1366
  );
@@ -2582,7 +2622,7 @@ async function prepareRun(opts, action, verb, requireSources = false) {
2582
2622
  }
2583
2623
  const usingStdin = inputs.includes(STDIN_TOKEN);
2584
2624
  const forced = opts.as === void 0 ? void 0 : extractorByName(opts.as);
2585
- if (opts.as !== void 0 && forced?.implemented !== true) {
2625
+ if (opts.as !== void 0 && forced === void 0) {
2586
2626
  throw new CiteError(
2587
2627
  `Unknown format "${opts.as}". Supported extensions: ${supportedExtensions().join(", ")}.`
2588
2628
  );
@@ -2748,6 +2788,35 @@ async function citedText(root, index, src, key) {
2748
2788
  }
2749
2789
  return sliceLines2(splitLines(source.text), range, range.path);
2750
2790
  }
2791
+ function spellAt(lines) {
2792
+ return lines.start === lines.end ? `line ${String(lines.start)}` : `lines ${String(lines.start)}-${String(lines.end)}`;
2793
+ }
2794
+ function shiftedEntries(citations, insertLine, bodyLine, label) {
2795
+ const out = { frontmatter: [], manifest: /* @__PURE__ */ new Map() };
2796
+ for (const { citation, origin } of citations) {
2797
+ const spec = citation.claim?.lines;
2798
+ const recorded = spec === void 0 ? void 0 : parseLines(spec);
2799
+ if (recorded === void 0) continue;
2800
+ const file = toFileLines(recorded, bodyLine);
2801
+ if (file.start < insertLine) {
2802
+ if (file.end < insertLine) continue;
2803
+ const whose = citation.id === void 0 ? `the claim at ${spellAt(file)}` : `the claim of ${citation.id} (${spellAt(file)})`;
2804
+ throw new CiteError(
2805
+ `${label}:${String(insertLine)} is inside ${whose}. A marker there would change its pin.`
2806
+ );
2807
+ }
2808
+ const moved = lineSpec({ start: recorded.start + 1, end: recorded.end + 1 });
2809
+ if (origin.kind === "manifest") out.manifest.set(origin.index, moved);
2810
+ else out.frontmatter.push({ index: origin.index, lines: moved });
2811
+ }
2812
+ return out;
2813
+ }
2814
+ function withClaimLines(entry, lines) {
2815
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return entry;
2816
+ const claim = entry.claim;
2817
+ if (typeof claim !== "object" || claim === null || Array.isArray(claim)) return entry;
2818
+ return { ...entry, claim: { ...claim, lines } };
2819
+ }
2751
2820
  async function runAdd(opts) {
2752
2821
  const cwd = resolve4(opts.cwd ?? process.cwd());
2753
2822
  const run3 = await resolveCiteRun({
@@ -2839,16 +2908,33 @@ async function runAdd(opts) {
2839
2908
  let markerAt;
2840
2909
  let claimSpan;
2841
2910
  let claim;
2911
+ let shifted = { frontmatter: [], manifest: /* @__PURE__ */ new Map() };
2842
2912
  if (marker && pageLines !== void 0) {
2913
+ const holding = unitHolding(content, pageLines.start, format, {
2914
+ offset: page.bodyOffset,
2915
+ line: page.bodyLine
2916
+ });
2917
+ if (holding === void 0) {
2918
+ throw new CiteError(`${at2} has no paragraph or block for a marker to anchor.`);
2919
+ }
2920
+ if (pageLines.end > holding.end) {
2921
+ throw new CiteError(
2922
+ `${at2} runs past the ${holding.kind} at ${spellAt(holding)}. A marker anchors one paragraph.`
2923
+ );
2924
+ }
2925
+ shifted = shiftedEntries(page.citations, holding.start, page.bodyLine, label);
2843
2926
  const statement = formatStatement(format, { kind: "ref", id: opts.id ?? "" });
2844
- const insertAt = offsetOfLine(content, pageLines.start);
2927
+ const insertAt = offsetOfLine(content, holding.start);
2845
2928
  body = insertStatementBefore(content, insertAt, statement);
2846
- markerAt = pageLines.start;
2929
+ markerAt = holding.start;
2847
2930
  const unit = anchoredLines(body, insertAt + statement.length, format, quote);
2848
2931
  const pin = unit === void 0 ? void 0 : pinOfLines(splitLines(body), unit);
2849
2932
  if (unit === void 0 || pin === void 0) {
2850
2933
  throw new CiteError(`${at2} has no paragraph or block for a marker to anchor.`);
2851
2934
  }
2935
+ for (const { index, lines: moved } of shifted.frontmatter) {
2936
+ body = spliceEntryField(body, format, index, ["claim", "lines"], moved);
2937
+ }
2852
2938
  claimSpan = unit;
2853
2939
  claim = { integrity: pin };
2854
2940
  } else if (pageLines !== void 0) {
@@ -2902,7 +2988,10 @@ async function runAdd(opts) {
2902
2988
  `${label} carries no ${owner.join}: value, so its citations cannot be keyed in ${owner.file}.`
2903
2989
  );
2904
2990
  }
2905
- const existing = (sidecar.citations ?? []).map((input) => input.entry);
2991
+ const existing = (sidecar.citations ?? []).map((input, index) => {
2992
+ const moved = shifted.manifest.get(index);
2993
+ return moved === void 0 ? input.entry : withClaimLines(input.entry, moved);
2994
+ });
2906
2995
  const list = [...existing, entryObject(citation)];
2907
2996
  const at3 = await manifests.write(owner, sidecar.entry, list, list.length - 1);
2908
2997
  const [changed] = manifests.changed();
@@ -3870,7 +3959,7 @@ async function runKeyRotate(opts) {
3870
3959
  if (inputs.length === 0) throw new KeyError(NO_FILES);
3871
3960
  const base = fromCollections && file !== null ? file.dir : cwd;
3872
3961
  const forced = opts.as === void 0 ? void 0 : extractorByName(opts.as);
3873
- if (opts.as !== void 0 && forced?.implemented !== true) {
3962
+ if (opts.as !== void 0 && forced === void 0) {
3874
3963
  throw new KeyError(
3875
3964
  `Unknown format "${opts.as}". Supported extensions: ${supportedExtensions().join(", ")}.`
3876
3965
  );
@@ -4080,6 +4169,8 @@ export {
4080
4169
  isSeverity,
4081
4170
  meetsSeverity,
4082
4171
  reencryptMetadata,
4172
+ codeRegions,
4173
+ codeEndAt,
4083
4174
  CiteError,
4084
4175
  asCiteError,
4085
4176
  spellSource,
@@ -4099,4 +4190,4 @@ export {
4099
4190
  runKeyRotate,
4100
4191
  runKeySet
4101
4192
  };
4102
- //# sourceMappingURL=chunk-6JWX6JM2.js.map
4193
+ //# sourceMappingURL=chunk-CD3LHG26.js.map