fix(format): resolve current-file/note tokens in one pass to stop rescan loops (#1358)#1369
fix(format): resolve current-file/note tokens in one pass to stop rescan loops (#1358)#1369chhoumann wants to merge 2 commits into
Conversation
…can loops (#1358) Note-derived tokens ({{linkcurrent}}, {{linksection}}, {{filenamecurrent}}, {{folder}}/{{folder|name}}, {{title}}) resolved as separate sequential regex passes after format(), so a later pass re-scanned an earlier pass's generated output. When the active file/folder/title was literally named like a token this corrupted a generated link (a note "{{folder}}.md" with a {{linkcurrent}} body produced [[]] instead of [[{{folder}}]]) or, via the while-loop replacers, hung the formatter forever (a note "{{filenamecurrent}}.md", or a title literally "{{title}}"). Resolve all note-derived tokens in a single function-replacer pass (replaceCurrentFileTokensInString) so a resolved value is never re-scanned, generalizing the link-only pass added in #1356. Each of the six entry points (formatFileContent / formatFileName / formatFolderPath / formatLocationString plus the two display formatters) calls it with its token subset; inactive tokens stay literal. The individual replacers delegate to it, and replaceLinkToCurrentFileInString is now single-pass — fixing their standalone self-loops too. Required/optional throw semantics + messages + precedence, multi-occurrence replacement, $-literal handling, and case-insensitive {{FOLDER|name}} are all preserved. Adds regression tests for every token-named-file direction (combined resolver + production entry points).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughIntroduces a protected ChangesSingle-pass current-file token resolver
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying quickadd with
|
| Latest commit: |
47f08f9
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://69e9c22b.quickadd.pages.dev |
| Branch Preview URL: | https://chhoumann-1358-current-file.quickadd.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/formatters/formatter-token-named-file.test.ts`:
- Line 113: The constant ALL does not follow TypeScript naming conventions as it
should be in camelCase. Rename the ALL constant declaration to allTokens, and
then update all 10 usages of ALL throughout the file to use the new allTokens
identifier instead. This ensures consistency with TypeScript naming standards
for constants and variables.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1be3f29b-e6dd-46b7-92d8-ecba29120869
📒 Files selected for processing (6)
src/formatters/completeFormatter.test.tssrc/formatters/completeFormatter.tssrc/formatters/fileNameDisplayFormatter.tssrc/formatters/formatDisplayFormatter.tssrc/formatters/formatter-token-named-file.test.tssrc/formatters/formatter.ts
Summary
Closes #1358.
Note-derived format tokens —
{{linkcurrent}},{{linksection}},{{filenamecurrent}},{{folder}}/{{folder|name}},{{title}}— resolved as separate sequential regex passes afterformat(). Each replacement embeds user-controlled text (the active file's basename, the target folder's name, the note title), so a later pass re-scanned an earlier pass's generated output. Two defects when a note/folder/title was literally named like a token:{{folder}}.mdwith a{{linkcurrent}}body: the link pass makes[[{{folder}}]], then the folder pass rewrites{{folder}}→[[]](link destroyed). Same for{{title}}.md.replaceCurrentFileNameInStringandreplaceTitleInStringusedwhile (REGEX.test(output)) output = replacer(output, REGEX, value). When the value itself matches the regex (a note{{filenamecurrent}}.md, or a title literally{{title}}), the loop never terminates and freezes the formatter (and Obsidian).{{linkcurrent}}/{{linksection}}were already hardened against re-scanning each other in #1356; this PR extends that single-pass guarantee to all note-derived tokens so none can re-scan another's output.Fix
Resolve every note-derived token in one function-replacer pass —
Formatter.replaceCurrentFileTokensInString(input, opts). A single left-to-rightString.replaceinserts each resolved value literally and never re-scans it, fixing both the corruption and the hang.CompleteFormatter.formatFileContentCompleteFormatter.formatFileName{{title}}throws earlier)CompleteFormatter.formatFolderPathCompleteFormatter.formatLocationStringFileNameDisplayFormatter.formatFormatDisplayFormatter.formatreplaceLinkToCurrentFileInStringis converted from awhileloop to a single pass.CaptureChoiceFormatterinherits the fix viasuper.formatFileContent/formatLocationString. Preflight (RequirementCollector) and the syntax suggester never resolve these tokens, so they need no change.Behaviour preserved
Required/optional throw semantics + byte-identical messages + link-over-filename precedence;
{{folder}}/{{title}}never throw; multi-occurrence replacement;$treated literally; case-insensitive{{FOLDER|NAME}}; the inactive-token-literal contract per entry point. The only intended change is that a token appearing inside a generated value is no longer re-resolved — exactly the bug class being fixed.Verification
api.format:{{folder}}.md+{{linkcurrent}}→[[]];{{title}}.md+{{linkcurrent}}→[[]]; the{{filenamecurrent}}cases hung.[[{{folder}}]],[[{{title}}]],{{filenamecurrent}},[[{{filenamecurrent}}]]— the previously-hanging cases now return. Happy path unchanged (Normal Note→[[Normal Note]]).tsc,eslint, andbuildclean.formatter-token-named-file.test.ts(combined-resolver unit semantics: no-loop, no-corruption, inactive-literal, throw precedence,$/case/multi-occurrence) and production-level guards incompleteFormatter.test.tsdriving the realformatFileContent/formatFileNameso a future revert to sequential passes is caught.Review
Design reviewed by an ultracode completeness pass + 2 adversarial reviewers (all "sound"); implementation diff reviewed by 3 adversarial reviewers (Codex).
Known, pre-existing limitations (out of scope, not regressions)
{{TEMPLATE:}}-includes a child whose output contains a resolved value literally equal to a token (only possible with a degenerately-named note) is still re-scanned by the parent's pass ([[]]). The old code produced the same result via the child's own pass; fixing it cleanly needs sentinel-protection across the template-composition boundary.{{macro:…}}/{{random:n}}is mutated in the preview only (no disk writes; pre-existing position, unchanged here).Summary by CodeRabbit
#1358: prevented re-scanning/re-writing of generated file links when active file basenames include formatting tokens.{{filenamecurrent}}.