doc: extensions: Add Doxygen tooltips to main documentation

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>
This commit is contained in:
Benjamin Cabé 2024-08-19 16:14:58 +02:00 committed by Anas Nashif
parent 22bde52b37
commit bd29c5c410
6 changed files with 221 additions and 0 deletions

View file

@ -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,
}

View file

@ -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;
}

View file

@ -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);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -85,6 +85,7 @@ extensions = [
"sphinx_sitemap",
"zephyr.doxyrunner",
"zephyr.doxybridge",
"zephyr.doxytooltip",
"zephyr.gh_utils",
"zephyr.manifest_projects_table",
"notfound.extension",