Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Link to DT nodes from supported features table #86316

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
123 changes: 59 additions & 64 deletions doc/_extensions/zephyr/domain/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

from anytree import ChildResolverError, Node, PreOrderIter, Resolver, search
from docutils import nodes
from docutils.parsers.rst import directives
from docutils.parsers.rst import directives, roles
from docutils.statemachine import StringList
from sphinx import addnodes
from sphinx.application import Sphinx
Expand Down Expand Up @@ -797,55 +797,33 @@ def run(self):
result_nodes.append(note)
return result_nodes

# Add the note before any tables
note = nodes.admonition(classes=["legend"])
note += nodes.title(text="Table Legend", classes=["note"])

legend = nodes.definition_list(classes=["supported-hardware field-list"])

def add_legend_item(legend_list, term_text, term_classes, definition_text):
dl_item = nodes.definition_list_item()
dt = nodes.term()
dt += nodes.inline("", term_text, classes=term_classes)
dd = nodes.definition()
dd += nodes.Text(definition_text)
dl_item += dt
dl_item += dd
legend_list += dl_item

add_legend_item(
legend, "on-chip", ["location-chip", "onchip"], "Feature integrated in the SoC."
)
add_legend_item(
legend, "on-board", ["location-chip", "onboard"], "Feature present on the board."
)
add_legend_item(
legend,
"2",
["count", "okay-count"],
"Number of instances of the feature that are present and enabled.",
)
add_legend_item(
legend,
"2",
["count", "disabled-count"],
"Number of instances of the feature that are present but initially disabled.",
)

dl_item = nodes.definition_list_item()
dt = nodes.term()
dt += nodes.literal(text="vnd,foo")
dd = nodes.definition()
dd += nodes.Text(
"Compatible string for the Devicetree binding matching the feature. "
"Click on the link to checkout the binding documentation."
)
dl_item += dt
dl_item += dd
legend += dl_item

note += legend
result_nodes.append(note)
html_contents = """<div class="legend admonition">
<dl class="supported-hardware field-list">
<dt>
<span class="location-chip onchip">on-chip</span> /
<span class="location-chip onboard">on-board</span>
</dt>
<dd>
Feature integrated in the SoC / present on the board.
</dd>
<dt>
<span class="count okay-count">2</span> /
<span class="count disabled-count">2</span>
</dt>
<dd>
Number of instances that are enabled / disabled. <br/>
Click on the label to see the first instance of this feature in DTS source files.
</dd>
<dt>
<code class="docutils literal notranslate"><span class="pre">vnd,foo</span></code>
</dt>
<dd>
Compatible string for the Devicetree binding matching the feature. <br/>
Click on the link to checkout the binding documentation.
</dd>
</dl>
</div>"""
result_nodes.append(nodes.raw("", html_contents, format="html"))

for target, features in sorted(supported_features.items()):
if not features:
Expand Down Expand Up @@ -893,7 +871,7 @@ def feature_sort_key(feature):

for i, (key, value) in enumerate(items):
row = nodes.row()
if value.get("disabled_count", 0) > 0 and value.get("okay_count", 0) == 0:
if value.get("disabled_nodes", []) and not value.get("okay_nodes", []):
row["classes"].append("disabled")

# TYPE column
Expand Down Expand Up @@ -934,22 +912,39 @@ def feature_sort_key(feature):
desc_para += nodes.Text(value["description"])

# Add count indicators for okay and not-okay instances
okay_count = value.get("okay_count", 0)
disabled_count = value.get("disabled_count", 0)
okay_nodes = value.get("okay_nodes", [])
disabled_nodes = value.get("disabled_nodes", [])

if okay_count > 0:
okay_count_indicator = nodes.inline(
classes=["count", "okay-count"],
text=str(okay_count),
)
desc_para += okay_count_indicator
role_fn, _ = roles.role(
"zephyr_file", self.state_machine.language, self.lineno, self.state.reporter
)

def create_count_indicator(nodes_list, class_type, role_function=role_fn):
if not nodes_list:
return None

count = len(nodes_list)

if role_function is None:
return nodes.inline(
classes=["count", f"{class_type}-count"], text=str(count)
)

if disabled_count > 0:
disabled_count_indicator = nodes.inline(
classes=["count", "disabled-count"],
text=str(disabled_count),
# Create a reference to the first node in the list
first_node = nodes_list[0]
file_ref = f"{count} <{first_node['filename']}#L{first_node['lineno']}>"

role_nodes, _ = role_function(
"zephyr_file", file_ref, file_ref, self.lineno, self.state.inliner
)
desc_para += disabled_count_indicator

count_node = role_nodes[0]
count_node["classes"] = ["count", f"{class_type}-count"]

return count_node

desc_para += create_count_indicator(okay_nodes, "okay")
desc_para += create_count_indicator(disabled_nodes, "disabled")

desc_entry += desc_para
row += desc_entry
Expand Down
2 changes: 1 addition & 1 deletion doc/_extensions/zephyr/domain/static/css/board.css
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@

.legend {
font-size: 0.9em;
max-width: 600px;
border-radius: 6px;
margin: auto;

Expand Down Expand Up @@ -179,6 +178,7 @@
font-size: 0.7em;
font-weight: 600;
margin-left: 4px;
padding-right: 6px !important;

&::before {
content: "×";
Expand Down
8 changes: 7 additions & 1 deletion doc/_extensions/zephyr/link-roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ def role(
link_text = text
link = text

line_ref = ""
line_match = re.search(r"(.+?)(?:#(L\d+(?:-L\d+)?))?$", link)
if line_match and line_match.group(2):
link = line_match.group(1)
line_ref = f"?plain=1#{line_match.group(2)}"

module_match = re.search(r"(.+?):\s*(.+)", link)
if module_match:
module = module_match.group(1).strip()
Expand Down Expand Up @@ -124,7 +130,7 @@ def role(
f"{link} not found in {config.link_roles_manifest_project} {trace}"
)

url = f"{baseurl}/{format}/{rev}/{link}"
url = f"{baseurl}/{format}/{rev}/{link}{line_ref}"
node = nodes.reference(rawtext, link_text, refuri=url, **options)
return [node], []

Expand Down
32 changes: 20 additions & 12 deletions doc/_scripts/gen_boards_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def get_catalog(generate_hw_features=False):
# Use pre-gathered build info and DTS files
if board.name in board_devicetrees:
for board_target, edt in board_devicetrees[board.name].items():
target_features = {}
features = {}
for node in edt.nodes:
if node.binding_path is None:
continue
Expand All @@ -271,6 +271,7 @@ def get_catalog(generate_hw_features=False):

description = DeviceTreeUtils.get_cached_description(node)
filename = node.filename
lineno = node.lineno
locations = set()
if Path(filename).is_relative_to(ZEPHYR_BASE):
filename = Path(filename).relative_to(ZEPHYR_BASE)
Expand All @@ -279,23 +280,30 @@ def get_catalog(generate_hw_features=False):
else:
locations.add("soc")

existing_feature = target_features.get(binding_type, {}).get(
existing_feature = features.get(binding_type, {}).get(
node.matching_compat
)

node_info = {"filename": str(filename), "lineno": lineno}
node_list_key = "okay_nodes" if node.status == "okay" else "disabled_nodes"

if existing_feature:
locations.update(existing_feature["locations"])
key = "okay_count" if node.status == "okay" else "disabled_count"
existing_feature[key] = existing_feature.get(key, 0) + 1
else:
key = "okay_count" if node.status == "okay" else "disabled_count"
target_features.setdefault(binding_type, {})[node.matching_compat] = {
"description": description,
"locations": locations,
key: 1
}
existing_feature.setdefault(node_list_key, []).append(node_info)
continue

feature_data = {
"description": description,
"locations": locations,
"okay_nodes": [],
"disabled_nodes": [],
}
feature_data[node_list_key].append(node_info)

features.setdefault(binding_type, {})[node.matching_compat] = feature_data

# Store features for this specific target
supported_features[board_target] = target_features
supported_features[board_target] = features

# Grab all the twister files for this board and use them to figure out all the archs it
# supports.
Expand Down
22 changes: 21 additions & 1 deletion doc/contribute/documentation/guidelines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -942,7 +942,26 @@ very file can be done using the :rst:role:`zephyr_file` role.

Check out :zephyr_file:`doc/contribute/documentation/guidelines.rst` for more information.

You may use the :rst:role:`zephyr_raw` role instead if you want to reference the "raw" content.
You can reference specific lines or line ranges in a file by appending :samp:`#L{line_number}` or
:samp:`#L{start_line}-L{end_line}` to the file path::

See :zephyr_file:`doc/contribute/documentation/guidelines.rst#L3` for the main heading of
this document.

Will render as:

See :zephyr_file:`doc/contribute/documentation/guidelines.rst#L3` for the main heading of
this document.

The role automatically verifies that the referenced file exists in the Zephyr tree and will
generate a warning during documentation build if the file is not found.

.. note::

Use the line references sparingly as keeping them accurate over time can be challenging as the
content of the linked file is subject to change.

You may use the :rst:role:`zephyr_raw` role instead if you want to reference the "raw" content.

.. rst:role:: zephyr_raw

Expand All @@ -964,6 +983,7 @@ You may use the :rst:role:`zephyr_raw` role instead if you want to reference the

Check out :module_file:`hal_stm32:CMakeLists.txt` for more information.

Similar to :rst:role:`zephyr_file`, you can reference specific lines or line ranges in a file.

Cross-referencing GitHub issues and pull requests
=================================================
Expand Down