Skip to content

Commit a824a97

Browse files
committed
update
1 parent 8dd5624 commit a824a97

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed

src/sphinxnotes/any/view.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
"""
2+
sphinxnotes.any.directives
3+
~~~~~~~~~~~~~~~~~~~~~~~~~~
4+
5+
Directive implementations.
6+
7+
:copyright: Copyright 2021 Shengyu Zhang
8+
:license: BSD, see LICENSE for details.
9+
"""
10+
11+
from __future__ import annotations
12+
from typing import Type
13+
14+
from docutils import nodes
15+
from docutils.nodes import Node, Element, fully_normalize_name
16+
from docutils.statemachine import StringList
17+
from docutils.parsers.rst import directives
18+
from sphinx import addnodes
19+
from sphinx.util.docutils import SphinxDirective
20+
from sphinx.util.nodes import make_id, nested_parse_with_titles
21+
from sphinx.util import logging
22+
23+
from .objects import Schema, Object
24+
25+
logger = logging.getLogger(__name__)
26+
27+
28+
def anchor(arg: str):
29+
return directives.choice(arg, ['no', 'section', 'objdesc'])
30+
31+
class ViewDirective(SphinxDirective):
32+
# Member of parent
33+
has_content = True
34+
required_arguments = 1
35+
optional_arguments = 0
36+
final_argument_whitespace = True
37+
option_spec = {
38+
'jinja': directives.unchanged,
39+
'anchor': anchor,
40+
}
41+
42+
def _fetch_data_source(self, src: str) -> dict[str, str]:
43+
return self.env.current_document['sphinxnotes-any-data-src']
44+
45+
def _setup_nodes(
46+
self, obj: Object, sectnode: Element, ahrnode: Element | None, contnode: Element
47+
) -> None:
48+
"""
49+
Attach necessary informations to nodes and note them.
50+
51+
The necessary information contains: domain info, basic attributes for nodes
52+
(ids, names, classes...), name of anchor, description content and so on.
53+
54+
:param sectnode: Section node, used as container of the whole object description
55+
:param ahrnode: Anchor node, used to mark the location of object description
56+
:param contnode: Content node, which contains the description content
57+
"""
58+
# Setup anchor
59+
if ahrnode is not None:
60+
_, objid = self.schema.identifier_of(obj)
61+
ahrid = make_id(self.env, self.state.document, prefix=objtype, term=objid)
62+
ahrnode['ids'].append(ahrid)
63+
# Add object name to node's names attribute.
64+
# 'names' is space-separated list containing normalized reference
65+
# names of an element.
66+
name = self.schema.name_of(obj)
67+
if isinstance(name, str):
68+
ahrnode['names'].append(fully_normalize_name(name))
69+
elif isinstance(name, list):
70+
ahrnode['names'].extend([fully_normalize_name(x) for x in name])
71+
self.state.document.note_explicit_target(ahrnode)
72+
# Note object by docu fields
73+
domain.note_object(
74+
self.env.docname, ahrid, self.schema, obj
75+
) # FIXME: Cast to AnyDomain
76+
77+
# Parse description
78+
nested_parse_with_titles(
79+
self.state, StringList(self.schema.render_description(obj)), contnode
80+
)
81+
82+
def _run_section(self, obj: Object) -> list[Node]:
83+
# Get the title of the "section" where the directive is located
84+
sectnode = self.state.parent
85+
titlenode = sectnode.next_node(nodes.title)
86+
if not titlenode or titlenode.parent != sectnode:
87+
# Title should be direct child of section
88+
msg = 'Failed to get title of current section'
89+
logger.warning(msg, location=sectnode)
90+
sm = nodes.system_message(
91+
msg, type='WARNING', level=2, backrefs=[], source=''
92+
)
93+
sectnode += sm
94+
title = ''
95+
else:
96+
title = titlenode.astext()
97+
# Replace the first name "_" with section title
98+
name = title + obj.name[1:]
99+
# Object is immutable, so create a new one
100+
obj = self.schema.object(name=name, attrs=obj.attrs, content=obj.content)
101+
# NOTE: In _setup_nodes, the anchor node(ahrnode) will be noted by
102+
# `note_explicit_target` for ahrnode, while `sectnode` is already noted
103+
# by `note_implicit_target`.
104+
# Multiple `note_xxx_target` calls to same node causes undefined behavior,
105+
# so we use `titlenode` as anchor node
106+
#
107+
# See https://github.com/sphinx-notes/any/issues/18
108+
self._setup_nodes(obj, sectnode, titlenode, sectnode)
109+
# Add all content to existed section, so return nothing
110+
return []
111+
112+
def _run_objdesc(self, obj: Object) -> list[Node]:
113+
descnode = addnodes.desc()
114+
115+
# Generate signature node
116+
title = self.schema.title_of(obj)
117+
if title is None:
118+
# Use non-generated object ID as replacement of title
119+
idfield, objid = self.schema.identifier_of(obj)
120+
title = objid if idfield is not None else None
121+
if title is not None:
122+
signode = addnodes.desc_signature(title, '')
123+
signode += addnodes.desc_name(title, title)
124+
descnode.append(signode)
125+
else:
126+
signode = None
127+
128+
# Generate content node
129+
contnode = addnodes.desc_content()
130+
descnode.append(contnode)
131+
self._setup_nodes(obj, descnode, signode, contnode)
132+
return [descnode]
133+
134+
def run(self) -> list[Node]:
135+
obj = self._build_object()
136+
if self.schema.title_of(obj) == '_':
137+
# If no argument is given, or the first argument is '_',
138+
# use the section title as object name and anchor,
139+
# append content nodes to section node
140+
return self._run_section(obj)
141+
else:
142+
# Else, create Sphinx ObjectDescription(sphinx.addnodes.dsec_*)
143+
return self._run_objdesc(obj)

0 commit comments

Comments
 (0)