Skip to content

Commit ec6c907

Browse files
committed
Make the linker always output link tags only. No <code> tags. The <code> tags are now added by the html translator when the document is a docstring. Otherwise it does not add the enclosing <code> tags because we're already in the middle of a code tag or similar <span class="rst-literal">.
Adjust the themes so the <code> tags and <span class="rst-literal"> are really equivalent.
1 parent 02063d3 commit ec6c907

File tree

14 files changed

+58
-37
lines changed

14 files changed

+58
-37
lines changed

pydoctor/epydoc/docutils.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
"""
44
from __future__ import annotations
55

6-
from typing import Iterable, Iterator, Optional, TypeVar, cast
6+
from typing import Iterable, Iterator, Optional, TypeVar, cast, TYPE_CHECKING
7+
8+
if TYPE_CHECKING:
9+
from typing import Literal
710

811
import optparse
912

@@ -14,11 +17,11 @@
1417

1518
_DEFAULT_DOCUTILS_SETTINGS: Optional[optparse.Values] = None
1619

17-
def new_document(source_path: str, settings: Optional[optparse.Values] = None) -> nodes.document:
20+
def new_document(source: Literal['docstring', 'code'], settings: Optional[optparse.Values] = None) -> nodes.document:
1821
"""
1922
Create a new L{nodes.document} using the provided settings or cached default settings.
2023
21-
@returns: L{nodes.document}
24+
@returns: L{nodes.document} which a C{source} attribute that matches the provided source.
2225
"""
2326
global _DEFAULT_DOCUTILS_SETTINGS
2427
# If we have docutils >= 0.19 we use get_default_settings to calculate and cache
@@ -29,7 +32,7 @@ def new_document(source_path: str, settings: Optional[optparse.Values] = None) -
2932

3033
settings = _DEFAULT_DOCUTILS_SETTINGS
3134

32-
return utils.new_document(source_path, settings)
35+
return utils.new_document(source, settings)
3336

3437
def _set_nodes_parent(nodes: Iterable[nodes.Node], parent: nodes.Element) -> Iterator[nodes.Node]:
3538
"""

pydoctor/epydoc/markup/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ def __init__(self, fields: Sequence['Field']):
150150
self._stan: Optional[Tag] = None
151151
self._summary: Optional['ParsedDocstring'] = None
152152

153-
@abc.abstractproperty
153+
@property
154+
@abc.abstractmethod
154155
def has_body(self) -> bool:
155156
"""
156157
Does this docstring have a non-empty body?
@@ -168,7 +169,7 @@ def get_toc(self, depth: int) -> Optional['ParsedDocstring']:
168169
except NotImplementedError:
169170
return None
170171
contents = build_table_of_content(document, depth=depth)
171-
docstring_toc = new_document('toc')
172+
docstring_toc = new_document('docstring')
172173
if contents:
173174
docstring_toc.extend(contents)
174175
from pydoctor.epydoc.markup.restructuredtext import ParsedRstDocstring
@@ -439,7 +440,7 @@ def visit_paragraph(self, node: nodes.paragraph) -> None:
439440
self.other_docs = True
440441
raise nodes.StopTraversal()
441442

442-
summary_doc = new_document('summary')
443+
summary_doc = new_document('docstring')
443444
summary_pieces: list[nodes.Node] = []
444445

445446
# Extract the first sentences from the first paragraph until maximum number

pydoctor/epydoc/markup/_pyval_repr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ def colorize(self, pyval: Any) -> ColorizedPyvalRepr:
333333
is_complete = True
334334

335335
# Put it all together.
336-
document = new_document('pyval_repr')
336+
document = new_document('code')
337337
# This ensure the .parent and .document attributes of the child nodes are set correcly.
338338
set_node_attributes(document, children=[set_node_attributes(node, document=document) for node in state.result])
339339
return ColorizedPyvalRepr(document, is_complete, state.warnings)

pydoctor/epydoc/markup/epytext.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1379,7 +1379,7 @@ def to_node(self) -> nodes.document:
13791379
if self._document is not None:
13801380
return self._document
13811381

1382-
self._document = new_document('epytext')
1382+
self._document = new_document('docstring')
13831383

13841384
if self._tree is not None:
13851385
node, = self._to_node(self._tree)

pydoctor/epydoc/markup/plaintext.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def to_node(self) -> nodes.document:
6262
return self._document
6363
else:
6464
# create document
65-
_document = new_document('plaintext')
65+
_document = new_document('docstring')
6666

6767
# split text into paragraphs
6868
paragraphs = [set_node_attributes(nodes.paragraph('',''), children=[

pydoctor/epydoc/markup/restructuredtext.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def get_transforms(self) -> List[Transform]:
176176
if t != frontmatter.DocInfo]
177177

178178
def new_document(self) -> nodes.document:
179-
document = new_document(self.source.source_path, self.settings)
179+
document = new_document('docstring', self.settings)
180180
# Capture all warning messages.
181181
document.reporter.attach_observer(self.report)
182182
# Return the new document.

pydoctor/epydoc2stan.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1155,7 +1155,7 @@ def get_constructors_extra(cls:model.Class) -> ParsedDocstring | None:
11551155
if not constructors:
11561156
return None
11571157

1158-
document = new_document('constructors')
1158+
document = new_document('docstring')
11591159

11601160
elements: list[nodes.Node] = []
11611161
plural = 's' if len(constructors)>1 else ''

pydoctor/linker.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,14 @@ def link_xref(self, target: str, label: "Flattenable", lineno: int) -> Tag:
153153
try:
154154
resolved = self._resolve_identifier_xref(target, lineno)
155155
except LookupError:
156-
xref = label
156+
xref = tags.transparent(label)
157157
else:
158158
if isinstance(resolved, str):
159159
xref = intersphinx_link(label, url=resolved)
160160
else:
161161
xref = taglink(resolved, self.page_url, label)
162162

163-
return tags.code(xref)
163+
return xref
164164

165165
def _resolve_identifier_xref(self,
166166
identifier: str,
@@ -286,13 +286,17 @@ def switch_context(self, ob:Optional['model.Documentable']) -> Iterator[None]:
286286
yield
287287

288288
class NotFoundLinker(DocstringLinker):
289-
"""A DocstringLinker implementation that cannot find any links."""
289+
"""
290+
A DocstringLinker implementation that cannot find any links.
291+
292+
It will always output link tag with no C{href} attribute.
293+
"""
290294

291295
def link_to(self, target: str, label: "Flattenable") -> Tag:
292-
return tags.transparent(label)
296+
return tags.a(label)
293297

294298
def link_xref(self, target: str, label: "Flattenable", lineno: int) -> Tag:
295-
return tags.code(label)
299+
return tags.a(label)
296300

297301
@contextlib.contextmanager
298302
def switch_context(self, ob: Optional[model.Documentable]) -> Iterator[None]:

pydoctor/node2stan.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,20 @@ def __init__(self,
102102
# h1 is reserved for the page nodes.title.
103103
self.section_level += 1
104104

105+
# All documents should be created with pydoctor.epydoc.docutils.new_document() helper
106+
# such that the source attribute will always be one of the supported values.
107+
self._document_is_code = is_code = document.attributes.get('source') == 'code'
108+
if is_code:
109+
# Do not wrap links in <code> tags if we're renderring a code-like parsed element.
110+
self._link_xref = self._linker.link_xref
111+
else:
112+
self._link_xref = lambda target, label, lineno: Tag('code')(self._linker.link_xref(target, label, lineno))
113+
114+
105115
# Handle interpreted text (crossreferences)
106116
def visit_title_reference(self, node: nodes.title_reference) -> None:
107117
lineno = get_lineno(node)
108-
self._handle_reference(node, link_func=lambda target, label: self._linker.link_xref(target, label, lineno))
118+
self._handle_reference(node, link_func=lambda target, label: self._link_xref(target, label, lineno))
109119

110120
# Handle internal references
111121
def visit_obj_reference(self, node: obj_reference) -> None:
@@ -134,6 +144,8 @@ def _handle_reference(self, node: nodes.title_reference, link_func: Callable[[st
134144
def should_be_compact_paragraph(self, node: nodes.Element) -> bool:
135145
if self.document.children == [node]:
136146
return True
147+
elif self._document_is_code:
148+
return True
137149
else:
138150
return super().should_be_compact_paragraph(node) # type: ignore[no-any-return]
139151

pydoctor/test/test_astbuilder.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,25 +1339,25 @@ def __init__(self):
13391339
assert type2html(b) == 'string'
13401340
c = C.contents['c']
13411341
assert c.docstring == """third"""
1342-
assert type2html(c) == '<code>str</code>'
1342+
assert type2html(c) == '<code><a>str</a></code>'
13431343
d = C.contents['d']
13441344
assert d.docstring == """fourth"""
1345-
assert type2html(d) == '<code>str</code>'
1345+
assert type2html(d) == '<code><a>str</a></code>'
13461346
e = C.contents['e']
13471347
assert e.docstring == """fifth"""
1348-
assert type2html(e) == '<code>List[C]</code>'
1348+
assert type2html(e) == '<code><a>List</a>[<a>C</a>]</code>'
13491349
f = C.contents['f']
13501350
assert f.docstring == """sixth"""
1351-
assert type2html(f) == '<code>List[C]</code>'
1351+
assert type2html(f) == '<code><a>List</a>[<a>C</a>]</code>'
13521352
g = C.contents['g']
13531353
assert g.docstring == """seventh"""
1354-
assert type2html(g) == '<code>List[C]</code>'
1354+
assert type2html(g) == '<code><a>List</a>[<a>C</a>]</code>'
13551355
s = C.contents['s']
13561356
assert s.docstring == """instance"""
1357-
assert type2html(s) == '<code>List[str]</code>'
1357+
assert type2html(s) == '<code><a>List</a>[<a>str</a>]</code>'
13581358
m = mod.contents['m']
13591359
assert m.docstring == """module-level"""
1360-
assert type2html(m) == '<code>bytes</code>'
1360+
assert type2html(m) == '<code><a>bytes</a></code>'
13611361

13621362
@systemcls_param
13631363
def test_type_comment(systemcls: Type[model.System], capsys: CapSys) -> None:

0 commit comments

Comments
 (0)