diff --git a/obsidianpilot/tools/link_management.py b/obsidianpilot/tools/link_management.py index b638710..e14c818 100644 --- a/obsidianpilot/tools/link_management.py +++ b/obsidianpilot/tools/link_management.py @@ -3,6 +3,7 @@ import re import asyncio from typing import List, Optional, Dict, Set, Tuple +from urllib.parse import unquote from ..utils.filesystem import get_vault from ..utils import is_markdown_file from ..utils.validation import validate_note_path @@ -185,6 +186,9 @@ def extract_links_from_content(content: str, source_path: Optional[str] = None) if link_path.startswith('http://') or link_path.startswith('https://'): continue + # URL decode the path + link_path = unquote(link_path) + # Ensure .md extension if not link_path.endswith('.md'): link_path += '.md' @@ -351,6 +355,10 @@ async def check_note_for_backlinks(note_path: str) -> List[dict]: # Check for markdown-style links for match in MARKDOWN_LINK_PATTERN.finditer(content): link_path = match.group(2).strip() + + # URL decode the path + link_path = unquote(link_path) + if link_path in target_names: backlink_info = { 'source_path': note_path, diff --git a/tests/test_link_management.py b/tests/test_link_management.py index 409a21e..adb5fba 100644 --- a/tests/test_link_management.py +++ b/tests/test_link_management.py @@ -27,17 +27,17 @@ def test_extract_wiki_links(self): assert len(links) == 5 assert links[0] == { - 'path': 'Daily Note.md', + 'target': 'Daily Note.md', 'display_text': 'Daily Note', 'type': 'wiki' } assert links[1] == { - 'path': 'Projects/AI Research.md', + 'target': 'Projects/AI Research.md', 'display_text': 'Projects/AI Research', 'type': 'wiki' } assert links[2] == { - 'path': 'Long Note Name.md', + 'target': 'Long Note Name.md', 'display_text': 'Short Name', 'type': 'wiki' } @@ -60,12 +60,12 @@ def test_extract_markdown_links(self): assert len(links) == 2 assert links[0] == { - 'path': 'note.md', + 'target': 'note.md', 'display_text': 'markdown link', 'type': 'markdown' } assert links[1] == { - 'path': 'folder/note.md', + 'target': 'folder/note.md', 'display_text': 'another one', 'type': 'markdown' } @@ -144,17 +144,26 @@ def test_normalize_paths(self): """Test that paths are normalized correctly.""" content = "[[Note]] and [[Note.md]] should be treated the same" links = extract_links_from_content(content) - + assert len(links) == 2 - assert links[0]['path'] == 'Note.md' - assert links[1]['path'] == 'Note.md' + assert links[0]['target'] == 'Note.md' + assert links[1]['target'] == 'Note.md' def test_preserve_folder_structure(self): """Test that folder paths are preserved.""" content = "[[Projects/Subfolder/Deep Note]]" links = extract_links_from_content(content) - - assert links[0]['path'] == 'Projects/Subfolder/Deep Note.md' + + assert links[0]['target'] == 'Projects/Subfolder/Deep Note.md' + + def test_handle_url_encoded_paths(self): + """Test that URL-encoded paths are decoded correctly.""" + content = "[Markdown Note](Markdown%20Note.md)" + links = extract_links_from_content(content) + + assert len(links) == 1 + assert links[0]['target'] == 'Markdown Note.md' + assert links[0]['display_text'] == 'Markdown Note' if __name__ == "__main__":