Skip to content

Commit

Permalink
doc: extensions: Add Doxygen tooltips to main documentation
Browse files Browse the repository at this point in the history
Add Doxygen tooltips in the main documentation.

The tooltips are triggered when hovering over C symbols in the
documentation and display a preview of the Doxygen documentation for the
symbol.

Some shadow DOM magic is needed to style the tooltips correctly, without
affecting the rest of the page.

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
  • Loading branch information
kartben authored and nashif committed Aug 29, 2024
1 parent 22bde52 commit bd29c5c
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 0 deletions.
36 changes: 36 additions & 0 deletions doc/_extensions/zephyr/doxytooltip/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""
Doxygen Tooltip Extension
#########################
Copyright (c) 2024 The Linux Foundation
SPDX-License-Identifier: Apache-2.0
A simple Sphinx extension that adds JS and CSS resources to the app
to enable tooltips for C domain links.
"""

from pathlib import Path

from typing import Any, Dict

from sphinx.application import Sphinx
from sphinx.util import logging

logger = logging.getLogger(__name__)

RESOURCES_DIR = Path(__file__).parent / "static"

def setup(app: Sphinx) -> Dict[str, Any]:
app.config.html_static_path.append(RESOURCES_DIR.as_posix())

app.add_js_file("tippy/popper.min.js")
app.add_js_file("tippy/tippy-bundle.umd.min.js")

app.add_js_file("doxytooltip.js")
app.add_css_file("doxytooltip.css")

return {
"version": "0.1",
"parallel_read_safe": True,
"parallel_write_safe": True,
}
36 changes: 36 additions & 0 deletions doc/_extensions/zephyr/doxytooltip/static/doxytooltip.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.tippy-box[data-theme~='doxytooltip'] {
/* provide minimal definition of the CSS variables used by Doxygen Awesome so that we have a
look and feel that's consistent with the rest of the documentation without necessarily aiming
for full fidelity */
--font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', Courier, monospace;
--primary-color: var(--navbar-heading-color);
--param-color: var(--code-literal-color);
--border-radius-small: 4px;
--border-radius-medium: 6px;
--border-radius-large: 8px;

background-color: var(--content-wrap-background-color);
color: var(--body-color);
}

.tippy-box[data-theme~='doxytooltip'][data-placement^='top']>.tippy-arrow::before {
border-top-color: var(--content-wrap-background-color);
}

.tippy-box[data-theme~='doxytooltip'][data-placement^='bottom']>.tippy-arrow::before {
border-bottom-color: var(--content-wrap-background-color);
}

.tippy-box[data-theme~='doxytooltip'][data-placement^='left']>.tippy-arrow::before {
border-left-color: var(--content-wrap-background-color);
}

.tippy-box[data-theme~='doxytooltip'][data-placement^='right']>.tippy-arrow::before {
border-right-color: var(--content-wrap-background-color);
}

.tippy-box[data-theme~='doxytooltip'] .tippy-content {
font-size: 0.9em;
max-height: 300px;
overflow-y: scroll;
}
133 changes: 133 additions & 0 deletions doc/_extensions/zephyr/doxytooltip/static/doxytooltip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/**
* Copyright (c) 2024, Benjamin Cabé <benjamin@zephyrproject.org>
* SPDX-License-Identifier: Apache-2.0
*
* This script adds mouse hover hooks to the current page to display
* Doxygen documentation as tooltips when hovering over a symbol in the
* documentation.
*/

registerDoxygenTooltip = function () {
const parser = new DOMParser();

const tooltip = document.createElement('div');
tooltip.id = 'doxytooltip';
document.body.appendChild(tooltip);

const tooltipShadow = document.createElement('div');
tooltipShadow.id = 'doxytooltip-shadow';
tooltip.attachShadow({ mode: 'open' }).appendChild(tooltipShadow);

/* tippy's JS code automatically adds a <style> tag to the document's <head> with the
* styles for the tooltips. We need to copy it to the tooltip's shadow DOM for it to apply to the
* tooltip's content.
*/
const tippyStyle = document.querySelector('style[data-tippy-stylesheet]');
if (tippyStyle) {
tooltipShadow.appendChild(tippyStyle.cloneNode(true));
tippyStyle.remove();
}

/*
* similarly, doxytooltip.css is added to the document's <head> by the Sphinx extension, but we
* need it in the shadow DOM.
*/
const doxytooltipStyle = document.querySelector('link[href*="doxytooltip.css"]');
if (doxytooltipStyle) {
tooltipShadow.appendChild(doxytooltipStyle.cloneNode(true));
doxytooltipStyle.remove();
}

let firstTimeShowingTooltip = true;
let links = Array.from(document.querySelectorAll('a.reference.internal')).filter((a) =>
a.querySelector('code.c')
);
links.forEach((link) => {
tippy(link, {
content: "Loading...",
allowHTML: true,
maxWidth: '500px',
placement: 'auto',
trigger: 'mouseenter',
touch: false,
theme: 'doxytooltip',
interactive: true,
appendTo: () =>
document
.querySelector('#doxytooltip')
.shadowRoot.getElementById('doxytooltip-shadow'),

onShow(instance) {
const url = link.getAttribute('href');
const targetId = link.getAttribute('href').split('#')[1];

fetch(url)
.then((response) => response.text())
.then((data) => {
const parsedDoc = parser.parseFromString(data, 'text/html');

if (firstTimeShowingTooltip) {
/* Grab the main stylesheets from the Doxygen page and inject them in the tooltip's
shadow DOM */
const doxygenCSSNames = ['doxygen-awesome.css', 'doxygen-awesome-zephyr.css'];
for (const cssName of doxygenCSSNames) {
const cssLink = parsedDoc.querySelector(`link[href$="${cssName}"]`);
if (cssLink) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = new URL(
cssLink.getAttribute('href'),
new URL(url, window.location.href)
);
tooltipShadow.appendChild(link);
}
}

firstTimeShowingTooltip = false;
}

const codeClasses = link.getElementsByTagName('code')[0].classList;
let content;
if (codeClasses.contains('c-enumerator')) {
const target = parsedDoc.querySelector(`tr td a[id="${targetId}"]`);
if (target) {
content = target.parentElement.nextElementSibling;
}
} else if (codeClasses.contains('c-struct')) {
content = parsedDoc.querySelector(`#details ~ div.textblock`);
} else {
/* c-function, c-type, etc. */
const target = parsedDoc.querySelector(
`h2.memtitle span.permalink a[href="#${targetId}"]`
);
if (target) {
content = target.closest('h2').nextElementSibling;
}
}

if (content) {
// rewrite all relative links as they are originally relative to the Doxygen page
content.querySelectorAll('a').forEach((a) => {
const href = a.getAttribute('href');
if (href && !href.startsWith('http')) {
a.href = new URL(href, new URL(url, window.location.href));
}
});

// set the tooltip content
instance.setContent(content.cloneNode(true).innerHTML);
} else {
// don't show tooltip if no meaningful content was found (ex. no brief description)
instance.setContent(null);
return false;
}
})
.catch((err) => {
console.error('Failed to fetch the documentation:', err);
});
},
});
});
};

document.addEventListener('DOMContentLoaded', registerDoxygenTooltip);
8 changes: 8 additions & 0 deletions doc/_extensions/zephyr/doxytooltip/static/tippy/popper.min.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"sphinx_sitemap",
"zephyr.doxyrunner",
"zephyr.doxybridge",
"zephyr.doxytooltip",
"zephyr.gh_utils",
"zephyr.manifest_projects_table",
"notfound.extension",
Expand Down

0 comments on commit bd29c5c

Please sign in to comment.