Skip to content

Commit c896718

Browse files
fix(memory-wiki): strip fenced code blocks before wikilink extraction (fixes #97945) (AI-assisted) (#97954)
* fix(memory-wiki): strip fenced code blocks before wikilink extraction extractWikiLinks runs OBSIDIAN_LINK_PATTERN against full markdown including fenced code blocks and inline code spans, causing false positive 'Broken wikilink target' warnings for bash [[...]] test syntax and Scala generics inside code blocks. Strip fenced code blocks and inline code before running the wikilink regex to eliminate code-block false positives while preserving real wikilinks in prose. Fixes #97945 * fix(memory-wiki): strip fenced code blocks before wikilink extraction --------- Co-authored-by: Vincent Koc <[email protected]>
1 parent b2787a1 commit c896718

2 files changed

Lines changed: 69 additions & 1 deletion

File tree

extensions/memory-wiki/src/lint.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,69 @@ describe("lintMemoryWikiVault", () => {
6767
expect(result.issues.map((issue) => issue.code)).not.toContain("broken-wikilink");
6868
});
6969

70+
it("does not report broken-wikilink for [[...]] inside fenced code blocks or inline code", async () => {
71+
const { rootDir, config } = await createVault({
72+
prefix: "memory-wiki-lint-fenced-code-",
73+
config: {
74+
vault: { renderMode: "obsidian" },
75+
},
76+
});
77+
await Promise.all(
78+
["entities", "sources"].map((dir) => fs.mkdir(path.join(rootDir, dir), { recursive: true })),
79+
);
80+
81+
await fs.writeFile(
82+
path.join(rootDir, "sources", "code-snippets.md"),
83+
renderWikiMarkdown({
84+
frontmatter: {
85+
pageType: "source",
86+
id: "source.code-snippets",
87+
title: "Code Snippets",
88+
},
89+
body: [
90+
"# Code Snippets",
91+
"",
92+
"Normal text with no wikilinks here.",
93+
"",
94+
"```bash",
95+
'if [[ "$name" == "Alice" ]]; then',
96+
" echo found",
97+
"fi",
98+
"```",
99+
"",
100+
"```scala",
101+
"val result = Future[Option[User]] {",
102+
' collectionName = "users"',
103+
"}",
104+
"```",
105+
"",
106+
'Inline code: `val userId: String` and `[[ "$str" == "test" ]]`.',
107+
].join("\n"),
108+
}),
109+
"utf8",
110+
);
111+
await fs.writeFile(
112+
path.join(rootDir, "entities", "alpha.md"),
113+
renderWikiMarkdown({
114+
frontmatter: {
115+
pageType: "entity",
116+
id: "entity.alpha",
117+
title: "Alpha",
118+
sourceIds: ["source.code-snippets"],
119+
},
120+
body: "# Alpha\n",
121+
}),
122+
"utf8",
123+
);
124+
125+
const result = await lintMemoryWikiVault(config);
126+
127+
const linkIssues = result.issues.filter(
128+
(issue) => issue.path === "sources/code-snippets.md" && issue.code === "broken-wikilink",
129+
);
130+
expect(linkIssues).toHaveLength(0);
131+
});
132+
70133
it("accepts unmanaged raw markdown source pages without page frontmatter", async () => {
71134
const { rootDir, config } = await createVault({
72135
prefix: "memory-wiki-lint-raw-sources-",

extensions/memory-wiki/src/markdown.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,12 @@ function normalizeMarkdownLinkTarget(sourceRelativePath: string, target: string)
387387
}
388388

389389
function extractWikiLinks(markdown: string, sourceRelativePath: string): string[] {
390-
const searchable = markdown.replace(RELATED_BLOCK_PATTERN, "");
390+
// Strip fenced code blocks and inline code before link extraction to avoid
391+
// false positives from [[...]] patterns in code (bash tests, Scala generics).
392+
const searchable = markdown
393+
.replace(/(^|\n)(`{3,})[^\n]*\n[\s\S]*?\n\2(?=\n|$)/g, "\n")
394+
.replace(/`[^`]+`/g, "``")
395+
.replace(RELATED_BLOCK_PATTERN, "");
391396
const links: string[] = [];
392397
for (const match of searchable.matchAll(OBSIDIAN_LINK_PATTERN)) {
393398
const target = match[1]?.trim();

0 commit comments

Comments
 (0)