Commit fdb8b30d authored by Benjamin Cabé's avatar Benjamin Cabé Committed by Benjamin Cabé
Browse files

doc: extensions: Navigate to DTS entries from supported hardware list



Update the gen_board_catalog.py logic to also capture lineno for
each devicetree node. Use that info in the supported hardware list to
create clickable elements that directly take the user to the
corresponding line in the devicetree file on Github.

Signed-off-by: default avatarBenjamin Cabé <benjamin@zephyrproject.org>
parent 235fabb0
Loading
Loading
Loading
Loading
+34 −16
Original line number Diff line number Diff line
@@ -36,7 +36,7 @@ from typing import Any

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
@@ -811,7 +811,8 @@ class BoardSupportedHardwareDirective(SphinxDirective):
      <span class="count disabled-count">2</span>
    </dt>
    <dd>
      Number of instances that are enabled / disabled.
      Number of instances that are enabled / disabled. <br/>
      Click on the label to see the first instance of this feature in the board/SoC DTS files.
    </dd>
    <dt>
      <code class="docutils literal notranslate"><span class="pre">vnd,foo</span></code>
@@ -870,7 +871,7 @@ class BoardSupportedHardwareDirective(SphinxDirective):

                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
@@ -911,22 +912,39 @@ class BoardSupportedHardwareDirective(SphinxDirective):
                    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),
                    role_fn, _ = roles.role(
                        "zephyr_file", self.state_machine.language, self.lineno, self.state.reporter
                    )
                        desc_para += okay_count_indicator

                    if disabled_count > 0:
                        disabled_count_indicator = nodes.inline(
                            classes=["count", "disabled-count"],
                            text=str(disabled_count),
                    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)
                            )

                        # 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
+1 −0
Original line number Diff line number Diff line
@@ -178,6 +178,7 @@
    font-size: 0.7em;
    font-weight: 600;
    margin-left: 4px;
    padding-right: 6px !important;

    &::before {
        content: "×";
+20 −12
Original line number Diff line number Diff line
@@ -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
@@ -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)
@@ -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] = {
                        existing_feature.setdefault(node_list_key, []).append(node_info)
                        continue

                    feature_data = {
                        "description": description,
                        "locations": locations,
                            key: 1
                        "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.