Skip to content

Commit 8db9a98

Browse files
committed
Fix pickup of tags in code-block directive
1 parent b98d120 commit 8db9a98

File tree

2 files changed

+93
-28
lines changed

2 files changed

+93
-28
lines changed

docs/quickstart.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Quickstart
33

44
The ``sphinx-tags`` package enables the use of blog-style tags with Sphinx.
55

6-
.. tags:: tag documentation, tag installation
6+
.. tags:: tag documentation, tag installation
77

88
Tags are created using the custom directive ``.. tags::`` with the tag titles
99
as arguments.

src/sphinx_tags/__init__.py

Lines changed: 92 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
from typing import List
1010

1111
from docutils import nodes
12+
from docutils.parsers.rst import Parser
13+
from docutils import frontend, utils
1214
from sphinx.errors import ExtensionError
1315
from sphinx.util.docutils import SphinxDirective
1416
from sphinx.util.logging import getLogger
17+
from sphinx.util.matching import get_matching_files
1518
from sphinx.util.rst import textwidth
1619

1720
__version__ = "0.3.1"
@@ -30,35 +33,42 @@ class TagLinks(SphinxDirective):
3033

3134
# Sphinx directive class attributes
3235
required_arguments = 0
33-
optional_arguments = 1 # Arbitrary, split on seperator
36+
optional_arguments = 1 # Arbitrary, split on separator
3437
final_argument_whitespace = True
3538
has_content = True
3639
final_argument_whitespace = True
37-
# Custom attributes
3840
separator = ","
3941

4042
def run(self):
4143
if not (self.arguments or self.content):
4244
raise ExtensionError("No tags passed to 'tags' directive.")
4345

4446
tagline = []
45-
# normalize white space and remove "\n"
47+
48+
# normalize white space and remove "\n"
4649
if self.arguments:
4750
tagline.extend(self.arguments[0].split())
4851
if self.content:
4952
tagline.extend((" ".join(self.content)).strip().split())
53+
#tags = read_tags(tagline, ".. tags::", "")
54+
#tags = [tag.strip() for tag in (" ".join(tagline)).split(self.separator)]
5055

51-
tags = [tag.strip() for tag in (" ".join(tagline)).split(self.separator)]
56+
settings = frontend.get_default_settings(Parser)
57+
document = utils.new_document("tags", settings)
58+
tags = Parser().parse(tagline, document)
59+
print(f"Tags: {tags}")
5260

5361
tag_dir = Path(self.env.app.srcdir) / self.env.app.config.tags_output_dir
5462
result = nodes.paragraph()
5563
result["classes"] = ["tags"]
5664
result += nodes.inline(text=f"{self.env.app.config.tags_intro_text} ")
5765
count = 0
5866

67+
# Parse the directive contents.
68+
self.state.nested_parse(self.content, self.content_offset, result)
69+
5970
current_doc_dir = Path(self.env.doc2path(self.env.docname)).parent
6071
relative_tag_dir = Path(os.path.relpath(tag_dir, current_doc_dir))
61-
6272
for tag in tags:
6373
count += 1
6474
# We want the link to be the path to the _tags folder, relative to
@@ -81,9 +91,6 @@ def run(self):
8191
if not count == len(tags):
8292
result += nodes.inline(text=tag_separator)
8393

84-
# register tags to global metadata for document
85-
self.env.metadata[self.env.docname]["tags"] = tags
86-
8794
return [result]
8895

8996
def _get_plaintext_node(
@@ -134,6 +141,9 @@ def __init__(self, name):
134141
self.name = name
135142
self.file_basename = _normalize_tag(name)
136143

144+
def __repr__(self):
145+
return f"Tag({self.name}), {len(self.items)} items: {self.items}"
146+
137147
def create_file(
138148
self,
139149
items,
@@ -214,21 +224,60 @@ def create_file(
214224
class Entry:
215225
"""Tags to pages map"""
216226

217-
def __init__(self, entrypath: Path, tags: list):
227+
# def __init__(self, entrypath: Path, tags: list):
228+
def __init__(self, entrypath: Path):
218229
self.filepath = entrypath
219-
self.tags = tags
230+
self.lines = self.filepath.read_text(encoding="utf8").split("\n")
231+
if self.filepath.suffix == ".rst":
232+
tagstart = ".. tags::"
233+
tagend = ""
234+
elif self.filepath.suffix == ".md":
235+
tagstart = "```{tags}"
236+
tagend = "```"
237+
elif self.filepath.suffix == ".ipynb":
238+
tagstart = '".. tags::'
239+
tagend = '"'
240+
else:
241+
raise ValueError(
242+
"Unknown file extension. Currently, only .rst, .md and .ipynb are supported."
243+
)
244+
245+
self.tags = read_tags(self.lines, tagstart, tagend)
246+
247+
248+
def __repr__(self):
249+
return f"Entry({self.filepath}), {self.tags}"
250+
220251

221252
def assign_to_tags(self, tag_dict):
222253
"""Append ourself to tags"""
223254
for tag in self.tags:
224-
if tag not in tag_dict:
225-
tag_dict[tag] = Tag(tag)
226-
tag_dict[tag].items.append(self)
255+
if tag:
256+
if tag not in tag_dict:
257+
tag_dict[tag] = Tag(tag)
258+
tag_dict[tag].items.append(self)
227259

228260
def relpath(self, root_dir) -> str:
229261
"""Get this entry's path relative to the given root directory"""
230262
return Path(os.path.relpath(self.filepath, root_dir)).as_posix()
231263

264+
def read_tags(lines, tagstart, tagend):
265+
"""Read tags from a list of lines in a file.
266+
267+
"""
268+
tagline = [line for line in lines if tagstart in line]
269+
# Custom attributes
270+
separator = ","
271+
272+
tags = []
273+
if tagline:
274+
# TODO: This is matching the .. tags:: example inside a code-block
275+
# in configuration.rst
276+
tagline = tagline[0].replace(tagstart, "").rstrip(tagend)
277+
tags = [" ".join(tag.strip().split()) for tag in tagline.split(separator)]
278+
tags = [tag for tag in tags if tag != ""]
279+
return tags
280+
232281

233282
def _normalize_tag(tag: str) -> str:
234283
"""Normalize a tag name to use in output filenames and tag URLs.
@@ -291,17 +340,32 @@ def tagpage(tags, outdir, title, extension, tags_index_head):
291340

292341

293342
def assign_entries(app):
294-
"""Assign all found entries to their tag."""
343+
"""Assign all found entries to their tag.
344+
345+
Returns
346+
-------
347+
tags : dict
348+
Dictionary of tags, keyed by tag name
349+
pages : list
350+
List of Entry objects
351+
"""
295352
pages = []
296353
tags = {}
297354

298-
for docname in app.env.found_docs:
299-
doctags = app.env.metadata[docname].get("tags", None)
300-
if doctags is None:
301-
continue # skip if no tags
302-
entry = Entry(app.env.doc2path(docname), doctags)
355+
doc_paths = get_matching_files(
356+
app.srcdir,
357+
include_patterns=[f"**.{extension}" for extension in app.config.tags_extension],
358+
exclude_patterns=app.config.exclude_patterns,
359+
)
360+
361+
for path in doc_paths:
362+
entry = Entry(Path(app.srcdir) / path)
303363
entry.assign_to_tags(tags)
304364
pages.append(entry)
365+
# docname is path without the file extension
366+
# docname = path.split(".", 1)[0]
367+
# register tags to global metadata for document
368+
# app.env.metadata[docname]["tags"] = tags
305369

306370
return tags, pages
307371

@@ -322,14 +386,15 @@ def update_tags(app):
322386
tags, pages = assign_entries(app)
323387

324388
for tag in tags.values():
325-
tag.create_file(
326-
[item for item in pages if tag.name in item.tags],
327-
app.config.tags_extension,
328-
tags_output_dir,
329-
app.srcdir,
330-
app.config.tags_page_title,
331-
app.config.tags_page_header,
332-
)
389+
if tag:
390+
tag.create_file(
391+
[item for item in pages if tag.name in item.tags],
392+
app.config.tags_extension,
393+
tags_output_dir,
394+
app.srcdir,
395+
app.config.tags_page_title,
396+
app.config.tags_page_header,
397+
)
333398

334399
# Create tags overview page
335400
tagpage(

0 commit comments

Comments
 (0)