Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions obsidianpilot/tools/link_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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,
Expand Down
29 changes: 19 additions & 10 deletions tests/test_link_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Expand All @@ -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'
}
Expand Down Expand Up @@ -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__":
Expand Down