From 211a302e94f25f6fc034fee089890dbbe0253f08 Mon Sep 17 00:00:00 2001 From: vycdev2 Date: Sun, 9 Aug 2026 22:28:49 +0000 Subject: [PATCH] fix: support classic Mac line endings --- CHANGELOG.md | 1 + src/utils/ackmate.ts | 5 +++-- src/utils/embedded/providers/abstract.ts | 3 ++- src/utils/embedded/providers/ag.ts | 3 ++- src/utils/embedded/regex.ts | 3 ++- src/utils/line-splitting.ts | 1 + test/ackmate.spec.ts | 15 +++++++++++++++ test/embedded-regex.spec.ts | 6 ++++++ 8 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 src/utils/line-splitting.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 72caf8d..777fa0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### Version 5.3.0 (Unreleased) +- Fixed embedded todo scanning and external-search result parsing for files and tool output that use classic Mac carriage-return line endings. - Fixed dependency indexing so one unreadable or removed Todo file no longer hides dependencies from other workspace files. - Fixed embedded todo parsing for file paths that start with a number. - Fixed date-sorted archiving so consecutive finished todos are ordered by their own completion dates. diff --git a/src/utils/ackmate.ts b/src/utils/ackmate.ts index e0a7186..fba5665 100644 --- a/src/utils/ackmate.ts +++ b/src/utils/ackmate.ts @@ -1,7 +1,8 @@ +import { splitLines } from './line-splitting'; + /* ACKMATE */ const Ackmate = { - newLineRe: /\r?\n/g, filePathRe: /^:?([^]+)$/, matchLineRe: /^(\d+)(?:;\d+ \d+)?:([^]*)$/, @@ -10,7 +11,7 @@ const Ackmate = { }, parse(str) { - const lines = str.split(Ackmate.newLineRe); + const lines = splitLines(str); let filePath, match; diff --git a/src/utils/embedded/providers/abstract.ts b/src/utils/embedded/providers/abstract.ts index b794f54..435ba5a 100644 --- a/src/utils/embedded/providers/abstract.ts +++ b/src/utils/embedded/providers/abstract.ts @@ -12,6 +12,7 @@ import Consts from '../../../consts'; import { hasConditionalExcludeGlobs, isFileIncluded } from '../../file-globs'; import { getWorkspaceExcludeRules } from '../../workspace-excludes'; import { getFollowingContext } from '../context'; +import { splitLines } from '../../line-splitting'; /* ABSTRACT */ @@ -199,7 +200,7 @@ class Abstract { parseContent(filePath: string, content: string) { const data = [], - lines = content.split(/\r?\n/); + lines = splitLines(content); if (!content) return data; diff --git a/src/utils/embedded/providers/ag.ts b/src/utils/embedded/providers/ag.ts index 22b68d6..f1819df 100644 --- a/src/utils/embedded/providers/ag.ts +++ b/src/utils/embedded/providers/ag.ts @@ -10,6 +10,7 @@ import { getGlobMatchOptions } from '../../file-globs'; import File from '../../file'; import Folder from '../../folder'; import { getWorkspaceExcludeGlobs } from '../../workspace-excludes'; +import { splitLines } from '../../line-splitting'; import Abstract from './abstract'; /* AG */ // The Silver Searcher //URL: https://github.com/ggreer/the_silver_searcher @@ -106,7 +107,7 @@ class AG extends Abstract { const content = await File.read(filePath); if (content !== undefined) { - contextLines[filePath] = content.split(/\r?\n/); + contextLines[filePath] = splitLines(content); } } ) diff --git a/src/utils/embedded/regex.ts b/src/utils/embedded/regex.ts index 243993f..e386652 100644 --- a/src/utils/embedded/regex.ts +++ b/src/utils/embedded/regex.ts @@ -1,9 +1,10 @@ import * as _ from 'lodash'; +import { splitLines } from '../line-splitting'; export const hasEmbeddedMatch = (content: string, regex: RegExp): boolean => { const matcher = new RegExp(regex.source, regex.flags); - return content.split(/\r?\n/).some((line) => { + return splitLines(content).some((line) => { matcher.lastIndex = 0; return matcher.test(_.trimStart(line)); diff --git a/src/utils/line-splitting.ts b/src/utils/line-splitting.ts new file mode 100644 index 0000000..7b701a4 --- /dev/null +++ b/src/utils/line-splitting.ts @@ -0,0 +1 @@ +export const splitLines = (text: string): string[] => text.split(/\r\n?|\n/); diff --git a/test/ackmate.spec.ts b/test/ackmate.spec.ts index fbb3b5a..9b2901f 100644 --- a/test/ackmate.spec.ts +++ b/test/ackmate.spec.ts @@ -29,4 +29,19 @@ describe('Ackmate parser', () => { }, ]); }); + + it('parses records separated by classic Mac line endings', () => { + expect(Ackmate.parse('TODO.md\r2:TODO first\r3:FIXME second')).to.deep.equal([ + { + filePath: 'TODO.md', + lineNr: 1, + line: 'TODO first', + }, + { + filePath: 'TODO.md', + lineNr: 2, + line: 'FIXME second', + }, + ]); + }); }); diff --git a/test/embedded-regex.spec.ts b/test/embedded-regex.spec.ts index 57feedb..76759e1 100644 --- a/test/embedded-regex.spec.ts +++ b/test/embedded-regex.spec.ts @@ -74,4 +74,10 @@ describe('Embedded todo scan pre-check', () => { expect(hasEmbeddedMatch(content, regex)).to.equal(true); expect(hasEmbeddedMatch('// TODO ignored', regex)).to.equal(false); }); + + it('recognizes embedded todos in files with classic Mac line endings', () => { + const regex = /^\/\/\s*(TASK):?\s*(.*)$/gi; + + expect(hasEmbeddedMatch('const ready = true;\r // TASK ship it', regex)).to.equal(true); + }); });