( input: string, entryId: string, )
| 552 | } |
| 553 | |
| 554 | export function findLocalMemoryEntryDraftRange( |
| 555 | input: string, |
| 556 | entryId: string, |
| 557 | ): LocalMemoryEntryDraftRange | null { |
| 558 | const id = entryId.trim(); |
| 559 | if (!id) return null; |
| 560 | |
| 561 | const lines = input.split(/\r?\n/); |
| 562 | const lineStarts: number[] = []; |
| 563 | let offset = 0; |
| 564 | for (let index = 0; index < lines.length; index += 1) { |
| 565 | lineStarts[index] = offset; |
| 566 | offset += (lines[index] ?? '').length; |
| 567 | if (index < lines.length - 1) { |
| 568 | offset += input[offset] === '\r' && input[offset + 1] === '\n' ? 2 : 1; |
| 569 | } |
| 570 | } |
| 571 | lineStarts[lines.length] = input.length; |
| 572 | |
| 573 | let current: { title: string; headingLineIndex: number; meta?: Record<string, string> } | null = |
| 574 | null; |
| 575 | |
| 576 | const matchCurrent = (endLineIndex: number): LocalMemoryEntryDraftRange | null => { |
| 577 | if (!current) return null; |
| 578 | const currentId = current.meta?.id ?? slugId(current.title); |
| 579 | if (currentId !== id) return null; |
| 580 | return { |
| 581 | start: lineStarts[current.headingLineIndex] ?? 0, |
| 582 | end: lineStarts[endLineIndex] ?? input.length, |
| 583 | }; |
| 584 | }; |
| 585 | |
| 586 | for (let index = 0; index < lines.length; index += 1) { |
| 587 | const line = lines[index] ?? ''; |
| 588 | const heading = /^##\s+(.+?)\s*$/.exec(line); |
| 589 | if (heading) { |
| 590 | const matched = matchCurrent(index); |
| 591 | if (matched) return matched; |
| 592 | current = { title: heading[1] ?? '未命名记忆', headingLineIndex: index }; |
| 593 | continue; |
| 594 | } |
| 595 | if (!current || current.meta) continue; |
| 596 | const meta = parseMetaComment(line); |
| 597 | if (meta) current.meta = meta; |
| 598 | } |
| 599 | return matchCurrent(lines.length); |
| 600 | } |
| 601 | |
| 602 | export function findLocalMemoryEntryDraft( |
| 603 | input: string, |
no test coverage detected