mkdocs-easylinks-plugin 0.1.3__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/.github/workflows/test.yml +18 -1
  2. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/CHANGELOG.md +21 -0
  3. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/PKG-INFO +10 -10
  4. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/README.md +6 -5
  5. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/mkdocs_easylinks/__init__.py +1 -1
  6. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/mkdocs_easylinks/plugin.py +14 -32
  7. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/mkdocs_easylinks_plugin.egg-info/PKG-INFO +10 -10
  8. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/mkdocs_easylinks_plugin.egg-info/requires.txt +1 -0
  9. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/pyproject.toml +4 -5
  10. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/tests/test_plugin.py +3 -2
  11. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/.github/workflows/publish.yml +0 -0
  12. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/.gitignore +0 -0
  13. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/LICENSE +0 -0
  14. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/mkdocs_easylinks_plugin.egg-info/SOURCES.txt +0 -0
  15. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/mkdocs_easylinks_plugin.egg-info/dependency_links.txt +0 -0
  16. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/mkdocs_easylinks_plugin.egg-info/entry_points.txt +0 -0
  17. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/mkdocs_easylinks_plugin.egg-info/top_level.txt +0 -0
  18. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/setup.cfg +0 -0
  19. {mkdocs_easylinks_plugin-0.1.3 → mkdocs_easylinks_plugin-0.2.0}/tests/__init__.py +0 -0
@@ -14,7 +14,7 @@ jobs:
14
14
  runs-on: ubuntu-latest
15
15
  strategy:
16
16
  matrix:
17
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
17
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
18
18
 
19
19
  steps:
20
20
  - uses: actions/checkout@v4
@@ -29,3 +29,20 @@ jobs:
29
29
 
30
30
  - name: Run tests
31
31
  run: pytest
32
+
33
+ typecheck:
34
+ runs-on: ubuntu-latest
35
+
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+
39
+ - name: Set up Python 3.13
40
+ uses: actions/setup-python@v5
41
+ with:
42
+ python-version: "3.13"
43
+
44
+ - name: Install dependencies
45
+ run: pip install -e ".[dev]"
46
+
47
+ - name: Run mypy
48
+ run: mypy mkdocs_easylinks --ignore-missing-imports
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.2.0] - 2026-04-02
6
+
7
+ ### Removed
8
+ - Dropped support for Python 3.8 and 3.9 (both EOL)
9
+
10
+ ## [0.1.4] - 2026-03-30
11
+
12
+ ### Changed
13
+ - Pre-compiled code fence and HTML comment regex patterns as class-level constants for consistency and minor performance improvement
14
+ - Excluded directories are now normalized once per build rather than on every file check
15
+ - `_should_ignore_file` simplified to a single `any()` expression
16
+ - `files_ambiguous` stat now tracked separately from `files_indexed`, making both counts accurate and meaningful
17
+ - Development status classifier updated from Alpha to Beta
18
+ - README stats section updated to reflect `files_ambiguous` and `files_ignored` counters
19
+
20
+ ### Fixed
21
+ - `files_indexed` no longer counts ambiguous duplicate filenames — those are now counted in the new `files_ambiguous` stat
22
+
23
+ ### Dev
24
+ - Added mypy type checking to CI (`typecheck` job in GitHub Actions)
25
+
5
26
  ## [0.1.3] - 2026-03-30
6
27
 
7
28
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-easylinks-plugin
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: An MkDocs plugin that allows linking to files by filename only
5
5
  Author: Daniel Ferguson
6
6
  License: MIT
@@ -8,23 +8,22 @@ Project-URL: Homepage, https://github.com/dsferg/mkdocs-easylinks-plugin
8
8
  Project-URL: Documentation, https://github.com/dsferg/mkdocs-easylinks-plugin
9
9
  Project-URL: Repository, https://github.com/dsferg/mkdocs-easylinks-plugin
10
10
  Keywords: mkdocs,plugin,links,cross-references
11
- Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
15
  Classifier: Programming Language :: Python :: 3.10
18
16
  Classifier: Programming Language :: Python :: 3.11
19
17
  Classifier: Programming Language :: Python :: 3.12
20
18
  Classifier: Programming Language :: Python :: 3.13
21
- Requires-Python: >=3.8
19
+ Requires-Python: >=3.10
22
20
  Description-Content-Type: text/markdown
23
21
  License-File: LICENSE
24
22
  Requires-Dist: mkdocs>=1.4.0
25
23
  Provides-Extra: dev
26
24
  Requires-Dist: pytest>=7.0; extra == "dev"
27
25
  Requires-Dist: pytest-cov>=4.0; extra == "dev"
26
+ Requires-Dist: mypy>=1.0; extra == "dev"
28
27
  Dynamic: license-file
29
28
 
30
29
  # MkDocs EasyLinks Plugin
@@ -41,7 +40,7 @@ An MkDocs plugin that allows you to create cross-references and embed images by
41
40
  - **Link statistics**: See which files are most linked and find orphaned content
42
41
  - **Ambiguity warnings**: Get notified if multiple files share the same name
43
42
  - **Material for MkDocs compatible**: Works seamlessly with Material theme
44
- - **Smart protection**: Code fences and HTML comments are preserved unchanged
43
+ - **Smart protection**: Code fences and HTML comments are preserved unchanged by default (configurable)
45
44
 
46
45
  ## Installation
47
46
 
@@ -126,7 +125,7 @@ plugins:
126
125
  ```
127
126
 
128
127
  This will display:
129
- - Files scanned and indexed
128
+ - Files scanned, indexed, ambiguous, and ignored
130
129
  - Links processed, resolved, and unresolved
131
130
  - Images processed, resolved, and unresolved
132
131
  - Most frequently linked files
@@ -185,8 +184,8 @@ Anchors work for document links:
185
184
  - `[text](/absolute/path.md)` - Absolute paths
186
185
  - `[text](../relative/path.md)` - Explicit relative paths with directories
187
186
  - `[text](#anchor)` - Fragment-only links
188
- - Links/images inside code fences (` ``` ` or `~~~`)
189
- - Links/images inside HTML comments (`<!-- -->`)
187
+ - Links/images inside code fences (` ``` ` or `~~~`) — unless `protect_code_fences: false`
188
+ - Links/images inside HTML comments (`<!-- -->`) — unless `protect_html_comments: false`
190
189
 
191
190
  ### Protected Content
192
191
 
@@ -204,7 +203,7 @@ The plugin intelligently ignores links in:
204
203
  <!-- This [link](example.md) won't be processed -->
205
204
  ```
206
205
 
207
- This ensures that example code and commented-out content remain unchanged.
206
+ This ensures that example code and commented-out content remain unchanged. Both behaviours are configurable via `protect_code_fences` and `protect_html_comments`.
208
207
 
209
208
  **Important: Indented Content**
210
209
 
@@ -228,6 +227,7 @@ This design choice ensures the plugin works seamlessly with MkDocs features like
228
227
  **Files that are excluded from mapping:**
229
228
  - Files starting with `.` (dotfiles) - always ignored
230
229
  - Files listed in `ignore_files` configuration - useful for drafts and templates
230
+ - Files in directories listed in `exclude_dirs`
231
231
 
232
232
  ## Using with mkdocs-macros-plugin (Snippets)
233
233
 
@@ -12,7 +12,7 @@ An MkDocs plugin that allows you to create cross-references and embed images by
12
12
  - **Link statistics**: See which files are most linked and find orphaned content
13
13
  - **Ambiguity warnings**: Get notified if multiple files share the same name
14
14
  - **Material for MkDocs compatible**: Works seamlessly with Material theme
15
- - **Smart protection**: Code fences and HTML comments are preserved unchanged
15
+ - **Smart protection**: Code fences and HTML comments are preserved unchanged by default (configurable)
16
16
 
17
17
  ## Installation
18
18
 
@@ -97,7 +97,7 @@ plugins:
97
97
  ```
98
98
 
99
99
  This will display:
100
- - Files scanned and indexed
100
+ - Files scanned, indexed, ambiguous, and ignored
101
101
  - Links processed, resolved, and unresolved
102
102
  - Images processed, resolved, and unresolved
103
103
  - Most frequently linked files
@@ -156,8 +156,8 @@ Anchors work for document links:
156
156
  - `[text](/absolute/path.md)` - Absolute paths
157
157
  - `[text](../relative/path.md)` - Explicit relative paths with directories
158
158
  - `[text](#anchor)` - Fragment-only links
159
- - Links/images inside code fences (` ``` ` or `~~~`)
160
- - Links/images inside HTML comments (`<!-- -->`)
159
+ - Links/images inside code fences (` ``` ` or `~~~`) — unless `protect_code_fences: false`
160
+ - Links/images inside HTML comments (`<!-- -->`) — unless `protect_html_comments: false`
161
161
 
162
162
  ### Protected Content
163
163
 
@@ -175,7 +175,7 @@ The plugin intelligently ignores links in:
175
175
  <!-- This [link](example.md) won't be processed -->
176
176
  ```
177
177
 
178
- This ensures that example code and commented-out content remain unchanged.
178
+ This ensures that example code and commented-out content remain unchanged. Both behaviours are configurable via `protect_code_fences` and `protect_html_comments`.
179
179
 
180
180
  **Important: Indented Content**
181
181
 
@@ -199,6 +199,7 @@ This design choice ensures the plugin works seamlessly with MkDocs features like
199
199
  **Files that are excluded from mapping:**
200
200
  - Files starting with `.` (dotfiles) - always ignored
201
201
  - Files listed in `ignore_files` configuration - useful for drafts and templates
202
+ - Files in directories listed in `exclude_dirs`
202
203
 
203
204
  ## Using with mkdocs-macros-plugin (Snippets)
204
205
 
@@ -1,3 +1,3 @@
1
1
  """MkDocs EasyLinks Plugin - Simplified cross-referencing by filename."""
2
2
 
3
- __version__ = "0.1.3"
3
+ __version__ = "0.1.4"
@@ -31,15 +31,19 @@ class EasyLinksPlugin(BasePlugin[EasyLinksConfig]):
31
31
  """Plugin to resolve markdown links by filename only."""
32
32
 
33
33
  _link_pattern = re.compile(r'(!)?\[([^\]]+)\]\(([^)]+)\)')
34
+ _code_fence_pattern = re.compile(r'^```[\s\S]*?^```|^~~~[\s\S]*?^~~~', re.MULTILINE)
35
+ _html_comment_pattern = re.compile(r'<!--[\s\S]*?-->')
34
36
 
35
37
  def __init__(self):
36
38
  super().__init__()
37
39
  self.file_map: Dict[str, str] = {}
38
40
  self.ambiguous_files: Dict[str, list] = {}
41
+ self._normalized_exclude_dirs: list = []
39
42
  # Statistics tracking
40
43
  self.stats = {
41
44
  "total_files_scanned": 0,
42
45
  "files_indexed": 0,
46
+ "files_ambiguous": 0,
43
47
  "files_ignored": 0,
44
48
  "links_processed": 0,
45
49
  "links_resolved": 0,
@@ -56,6 +60,9 @@ class EasyLinksPlugin(BasePlugin[EasyLinksConfig]):
56
60
  self.ambiguous_files = {}
57
61
  self.stats = {key: 0 for key in self.stats}
58
62
  self.link_counts = defaultdict(int)
63
+ self._normalized_exclude_dirs = [
64
+ d.replace("\\", "/").rstrip("/") + "/" for d in self.config["exclude_dirs"]
65
+ ]
59
66
 
60
67
  # Process all files (documentation pages, images, etc.)
61
68
  for file in files:
@@ -81,7 +88,7 @@ class EasyLinksPlugin(BasePlugin[EasyLinksConfig]):
81
88
  if filename not in self.ambiguous_files:
82
89
  self.ambiguous_files[filename] = [self.file_map[filename]]
83
90
  self.ambiguous_files[filename].append(file.src_path)
84
- self.stats["files_indexed"] += 1
91
+ self.stats["files_ambiguous"] += 1
85
92
  else:
86
93
  self.file_map[filename] = file.src_path
87
94
  self.stats["files_indexed"] += 1
@@ -100,32 +107,15 @@ class EasyLinksPlugin(BasePlugin[EasyLinksConfig]):
100
107
 
101
108
  def _is_excluded_dir(self, file_path: str) -> bool:
102
109
  """Check if a file is in an excluded directory."""
103
- if not self.config["exclude_dirs"]:
110
+ if not self._normalized_exclude_dirs:
104
111
  return False
105
112
 
106
- # Normalize the path
107
113
  normalized_path = file_path.replace("\\", "/")
108
-
109
- for excluded_dir in self.config["exclude_dirs"]:
110
- # Normalize excluded dir
111
- excluded = excluded_dir.rstrip("/") + "/"
112
- # Check if file path starts with excluded directory
113
- if normalized_path.startswith(excluded):
114
- return True
115
-
116
- return False
114
+ return any(normalized_path.startswith(d) for d in self._normalized_exclude_dirs)
117
115
 
118
116
  def _should_ignore_file(self, filename: str) -> bool:
119
117
  """Check if a filename matches any ignore pattern (supports glob patterns)."""
120
- if not self.config["ignore_files"]:
121
- return False
122
-
123
- for pattern in self.config["ignore_files"]:
124
- # Support both exact match and glob patterns
125
- if fnmatch.fnmatch(filename, pattern):
126
- return True
127
-
128
- return False
118
+ return any(fnmatch.fnmatch(filename, p) for p in self.config["ignore_files"])
129
119
 
130
120
  def on_page_markdown(
131
121
  self, markdown: str, *, page: Page, config: MkDocsConfig, files: Files
@@ -229,20 +219,11 @@ class EasyLinksPlugin(BasePlugin[EasyLinksConfig]):
229
219
  # Match fenced code blocks (both ``` and ~~~)
230
220
  # Indented code blocks are NOT protected to support MkDocs admonitions
231
221
  if self.config["protect_code_fences"]:
232
- markdown = re.sub(
233
- r'^```[\s\S]*?^```|^~~~[\s\S]*?^~~~',
234
- make_placeholder,
235
- markdown,
236
- flags=re.MULTILINE
237
- )
222
+ markdown = self._code_fence_pattern.sub(make_placeholder, markdown)
238
223
 
239
224
  # Extract HTML comments
240
225
  if self.config["protect_html_comments"]:
241
- markdown = re.sub(
242
- r'<!--[\s\S]*?-->',
243
- make_placeholder,
244
- markdown
245
- )
226
+ markdown = self._html_comment_pattern.sub(make_placeholder, markdown)
246
227
 
247
228
  return markdown, protected_blocks
248
229
 
@@ -279,6 +260,7 @@ class EasyLinksPlugin(BasePlugin[EasyLinksConfig]):
279
260
  # File statistics
280
261
  logger.info(f"Files scanned: {self.stats['total_files_scanned']}")
281
262
  logger.info(f"Files indexed: {self.stats['files_indexed']}")
263
+ logger.info(f"Files ambiguous: {self.stats['files_ambiguous']}")
282
264
  logger.info(f"Files ignored: {self.stats['files_ignored']}")
283
265
 
284
266
  # Link statistics
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs-easylinks-plugin
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: An MkDocs plugin that allows linking to files by filename only
5
5
  Author: Daniel Ferguson
6
6
  License: MIT
@@ -8,23 +8,22 @@ Project-URL: Homepage, https://github.com/dsferg/mkdocs-easylinks-plugin
8
8
  Project-URL: Documentation, https://github.com/dsferg/mkdocs-easylinks-plugin
9
9
  Project-URL: Repository, https://github.com/dsferg/mkdocs-easylinks-plugin
10
10
  Keywords: mkdocs,plugin,links,cross-references
11
- Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
15
  Classifier: Programming Language :: Python :: 3.10
18
16
  Classifier: Programming Language :: Python :: 3.11
19
17
  Classifier: Programming Language :: Python :: 3.12
20
18
  Classifier: Programming Language :: Python :: 3.13
21
- Requires-Python: >=3.8
19
+ Requires-Python: >=3.10
22
20
  Description-Content-Type: text/markdown
23
21
  License-File: LICENSE
24
22
  Requires-Dist: mkdocs>=1.4.0
25
23
  Provides-Extra: dev
26
24
  Requires-Dist: pytest>=7.0; extra == "dev"
27
25
  Requires-Dist: pytest-cov>=4.0; extra == "dev"
26
+ Requires-Dist: mypy>=1.0; extra == "dev"
28
27
  Dynamic: license-file
29
28
 
30
29
  # MkDocs EasyLinks Plugin
@@ -41,7 +40,7 @@ An MkDocs plugin that allows you to create cross-references and embed images by
41
40
  - **Link statistics**: See which files are most linked and find orphaned content
42
41
  - **Ambiguity warnings**: Get notified if multiple files share the same name
43
42
  - **Material for MkDocs compatible**: Works seamlessly with Material theme
44
- - **Smart protection**: Code fences and HTML comments are preserved unchanged
43
+ - **Smart protection**: Code fences and HTML comments are preserved unchanged by default (configurable)
45
44
 
46
45
  ## Installation
47
46
 
@@ -126,7 +125,7 @@ plugins:
126
125
  ```
127
126
 
128
127
  This will display:
129
- - Files scanned and indexed
128
+ - Files scanned, indexed, ambiguous, and ignored
130
129
  - Links processed, resolved, and unresolved
131
130
  - Images processed, resolved, and unresolved
132
131
  - Most frequently linked files
@@ -185,8 +184,8 @@ Anchors work for document links:
185
184
  - `[text](/absolute/path.md)` - Absolute paths
186
185
  - `[text](../relative/path.md)` - Explicit relative paths with directories
187
186
  - `[text](#anchor)` - Fragment-only links
188
- - Links/images inside code fences (` ``` ` or `~~~`)
189
- - Links/images inside HTML comments (`<!-- -->`)
187
+ - Links/images inside code fences (` ``` ` or `~~~`) — unless `protect_code_fences: false`
188
+ - Links/images inside HTML comments (`<!-- -->`) — unless `protect_html_comments: false`
190
189
 
191
190
  ### Protected Content
192
191
 
@@ -204,7 +203,7 @@ The plugin intelligently ignores links in:
204
203
  <!-- This [link](example.md) won't be processed -->
205
204
  ```
206
205
 
207
- This ensures that example code and commented-out content remain unchanged.
206
+ This ensures that example code and commented-out content remain unchanged. Both behaviours are configurable via `protect_code_fences` and `protect_html_comments`.
208
207
 
209
208
  **Important: Indented Content**
210
209
 
@@ -228,6 +227,7 @@ This design choice ensures the plugin works seamlessly with MkDocs features like
228
227
  **Files that are excluded from mapping:**
229
228
  - Files starting with `.` (dotfiles) - always ignored
230
229
  - Files listed in `ignore_files` configuration - useful for drafts and templates
230
+ - Files in directories listed in `exclude_dirs`
231
231
 
232
232
  ## Using with mkdocs-macros-plugin (Snippets)
233
233
 
@@ -3,3 +3,4 @@ mkdocs>=1.4.0
3
3
  [dev]
4
4
  pytest>=7.0
5
5
  pytest-cov>=4.0
6
+ mypy>=1.0
@@ -4,22 +4,20 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mkdocs-easylinks-plugin"
7
- version = "0.1.3"
7
+ version = "0.2.0"
8
8
  description = "An MkDocs plugin that allows linking to files by filename only"
9
9
  readme = "README.md"
10
- requires-python = ">=3.8"
10
+ requires-python = ">=3.10"
11
11
  license = {text = "MIT"}
12
12
  authors = [
13
13
  {name = "Daniel Ferguson"}
14
14
  ]
15
15
  keywords = ["mkdocs", "plugin", "links", "cross-references"]
16
16
  classifiers = [
17
- "Development Status :: 3 - Alpha",
17
+ "Development Status :: 4 - Beta",
18
18
  "Intended Audience :: Developers",
19
19
  "License :: OSI Approved :: MIT License",
20
20
  "Programming Language :: Python :: 3",
21
- "Programming Language :: Python :: 3.8",
22
- "Programming Language :: Python :: 3.9",
23
21
  "Programming Language :: Python :: 3.10",
24
22
  "Programming Language :: Python :: 3.11",
25
23
  "Programming Language :: Python :: 3.12",
@@ -33,6 +31,7 @@ dependencies = [
33
31
  dev = [
34
32
  "pytest>=7.0",
35
33
  "pytest-cov>=4.0",
34
+ "mypy>=1.0",
36
35
  ]
37
36
 
38
37
  [project.urls]
@@ -176,7 +176,7 @@ class TestEasyLinksPlugin:
176
176
  assert any("docs/about.md" in msg and "index.md" in msg for msg in caplog.messages)
177
177
 
178
178
  def test_ambiguous_files_counted_in_indexed_stats(self):
179
- """Test that duplicate filenames are counted in files_indexed."""
179
+ """Test that duplicate filenames are counted in files_ambiguous, not files_indexed."""
180
180
  mock_config = MagicMock()
181
181
  mock_files = MagicMock(spec=Files)
182
182
 
@@ -189,7 +189,8 @@ class TestEasyLinksPlugin:
189
189
  self.plugin.on_files(mock_files, config=mock_config)
190
190
 
191
191
  assert self.plugin.stats["total_files_scanned"] == 3
192
- assert self.plugin.stats["files_indexed"] == 3 # all three, including the duplicate
192
+ assert self.plugin.stats["files_indexed"] == 2 # unique filenames only
193
+ assert self.plugin.stats["files_ambiguous"] == 1 # duplicates tracked separately
193
194
  assert self.plugin.stats["files_ignored"] == 0
194
195
  assert "index.md" in self.plugin.ambiguous_files
195
196