Skip to content

Commit

Permalink
refactor(docs): split monolithic api reference into multiple files (D…
Browse files Browse the repository at this point in the history
…isnakeDev#722)

This PR splits up the monolithic API reference documentation into multiple files.

Pages now load much faster, particularly on low-end devices, and sphinx can 
make better use of parallel builds, speeding those up as well. Old links to the 
`*/api.html` pages will be redirected accordingly with javascript.

Co-authored-by: shiftinv <me@shiftinv.cc>
Co-authored-by: Chromosomologist <55550164+Chromosomologist@users.noreply.github.com>
Co-authored-by: Victor <67214928+Victorsitou@users.noreply.github.com>
Co-authored-by: onerandomusername <me@arielle.codes>
  • Loading branch information
5 people committed Apr 8, 2023
1 parent 96df68b commit 04266b4
Show file tree
Hide file tree
Showing 79 changed files with 9,152 additions and 7,814 deletions.
5 changes: 5 additions & 0 deletions changelog/392.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Split the monolithic API Reference pages into multiple sub-references.
- Main ``disnake`` API reference can now be found at :doc:`API Reference <api/index>`.
- ``disnake.ext.commands`` API reference is now under :doc:`Commands API Reference <ext/commands/api/index>`.
- Legacy ``api.html`` and ``ext/commands/api.html`` pages are deprecated.
- - Links with pre-existing references (eg ``/api.html#disnake.AppInfo``) will be redirected to their appropriate page.
2 changes: 1 addition & 1 deletion disnake/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
class Snowflake(Protocol):
"""An ABC that details the common operations on a Discord model.
Almost all :ref:`Discord models <discord_api_models>` meet this
Almost all :ref:`Discord models <discord_model>` meet this
abstract base class.
If you want to create a snowflake on your own, consider using
Expand Down
1 change: 1 addition & 0 deletions disnake/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def to_dict(self) -> ActivityPayload:
# tag type for user-settable activities
class BaseActivity(_BaseActivity):
"""The base activity that all user-settable activities inherit from.
A user-settable activity is one that can be used in :meth:`Client.change_presence`.
The following types currently count as user-settable:
Expand Down
8 changes: 4 additions & 4 deletions disnake/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1538,7 +1538,7 @@ def wait_for(
In case the event returns multiple arguments, a :class:`tuple` containing those
arguments is returned instead. Please check the
:ref:`documentation <discord-api-events>` for a list of events and their
:ref:`documentation <disnake_api_events>` for a list of events and their
parameters.
This function returns the **first event that meets the requirements**.
Expand Down Expand Up @@ -1594,7 +1594,7 @@ def check(reaction, user):
Parameters
----------
event: Union[:class:`str`, :class:`.Event`]
The event name, similar to the :ref:`event reference <discord-api-events>`,
The event name, similar to the :ref:`event reference <disnake_api_events>`,
but without the ``on_`` prefix, to wait for. It's recommended
to use :class:`.Event`.
check: Optional[Callable[..., :class:`bool`]]
Expand All @@ -1614,7 +1614,7 @@ def check(reaction, user):
Any
Returns no arguments, a single argument, or a :class:`tuple` of multiple
arguments that mirrors the parameters passed in the
:ref:`event reference <discord-api-events>`.
:ref:`event <disnake_api_events>`.
"""
future = self.loop.create_future()
if check is None:
Expand All @@ -1639,7 +1639,7 @@ def _check(*args) -> bool:
def event(self, coro: CoroT) -> CoroT:
"""A decorator that registers an event to listen to.
You can find more info about the events on the :ref:`documentation below <discord-api-events>`.
You can find more info about the events in the :ref:`documentation <disnake_api_events>`.
The events must be a :ref:`coroutine <coroutine>`, if not, :exc:`TypeError` is raised.
Expand Down
40 changes: 20 additions & 20 deletions disnake/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -1001,14 +1001,14 @@ class Event(Enum):
"""Called when a `Guild` creates a new `Role`.
Represents the :func:`on_guild_role_create` event.
"""
guild_role_update = "guild_role_update"
"""Called when a `Guild` updates a `Role`.
Represents the :func:`on_guild_role_update` event.
"""
guild_role_delete = "guild_role_delete"
"""Called when a `Guild` deletes a `Role`.
Represents the :func:`on_guild_role_delete` event.
"""
guild_role_update = "guild_role_update"
"""Called when a `Guild` updates a `Role`.
Represents the :func:`on_guild_role_update` event.
"""
guild_emojis_update = "guild_emojis_update"
"""Called when a `Guild` adds or removes `Emoji`.
Represents the :func:`on_guild_emojis_update` event.
Expand Down Expand Up @@ -1069,6 +1069,10 @@ class Event(Enum):
"""Called when an `AutoModRule` is deleted.
Represents the :func:`on_automod_rule_delete` event.
"""
audit_log_entry_create = "audit_log_entry_create"
"""Called when an audit log entry is created.
Represents the :func:`on_audit_log_entry_create` event.
"""
integration_create = "integration_create"
"""Called when an integration is created.
Represents the :func:`on_integration_create` event.
Expand All @@ -1085,14 +1089,14 @@ class Event(Enum):
"""Called when a `Member` joins a `Guild`.
Represents the :func:`on_member_join` event.
"""
member_update = "member_update"
"""Called when a `Member` updates their profile.
Represents the :func:`on_member_update` event.
"""
member_remove = "member_remove"
"""Called when a `Member` leaves a `Guild`.
Represents the :func:`on_member_remove` event.
"""
member_update = "member_update"
"""Called when a `Member` updates their profile.
Represents the :func:`on_member_update` event.
"""
raw_member_remove = "raw_member_remove"
"""Called when a member leaves a `Guild` regardless of the member cache.
Represents the :func:`on_raw_member_remove` event.
Expand All @@ -1101,10 +1105,6 @@ class Event(Enum):
"""Called when a member updates their profile regardless of the member cache.
Represents the :func:`on_raw_member_update` event.
"""
audit_log_entry_create = "audit_log_entry_create"
"""Called when an audit log entry is created.
Represents the :func:`on_audit_log_entry_create` event.
"""
member_ban = "member_ban"
"""Called when user gets banned from a `Guild`.
Represents the :func:`on_member_ban` event.
Expand All @@ -1129,14 +1129,14 @@ class Event(Enum):
"""Called when a `StageInstance` is created for a `StageChannel`.
Represents the :func:`on_stage_instance_create` event.
"""
stage_instance_update = "stage_instance_update"
"""Called when a `StageInstance` is updated.
Represents the :func:`on_stage_instance_update` event.
"""
stage_instance_delete = "stage_instance_delete"
"""Called when a `StageInstance` is deleted for a `StageChannel`.
Represents the :func:`on_stage_instance_delete` event.
"""
stage_instance_update = "stage_instance_update"
"""Called when a `StageInstance` is updated.
Represents the :func:`on_stage_instance_update` event.
"""
application_command = "application_command"
"""Called when an application command is invoked.
Represents the :func:`on_application_command` event.
Expand Down Expand Up @@ -1181,14 +1181,14 @@ class Event(Enum):
"""Called when messages are bulk deleted.
Represents the :func:`on_bulk_message_delete` event.
"""
raw_message_delete = "raw_message_delete"
"""Called when a message is deleted regardless of the message being in the internal message cache or not.
Represents the :func:`on_raw_message_delete` event.
"""
raw_message_edit = "raw_message_edit"
"""Called when a message is edited regardless of the state of the internal message cache.
Represents the :func:`on_raw_message_edit` event.
"""
raw_message_delete = "raw_message_delete"
"""Called when a message is deleted regardless of the message being in the internal message cache or not.
Represents the :func:`on_raw_message_delete` event.
"""
raw_bulk_message_delete = "raw_bulk_message_delete"
"""Called when a bulk delete is triggered regardless of the messages being in the internal message cache or not.
Represents the :func:`on_raw_bulk_message_delete` event.
Expand Down
2 changes: 1 addition & 1 deletion disnake/ext/commands/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ class Bot(BotBase, InteractionBotBase, disnake.Client):
help_command: Optional[:class:`.HelpCommand`]
The help command implementation to use. This can be dynamically
set at runtime. To remove the help command pass ``None``. For more
information on implementing a help command, see :ref:`ext_commands_help_command`.
information on implementing a help command, see :ref:`ext_commands_api_help_commands`.
This can be provided as a parameter at creation.
Expand Down
2 changes: 1 addition & 1 deletion disnake/ext/commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,7 @@ async def invoke(self, ctx: Context) -> None:
await self.prepare(ctx)

# terminate the invoked_subcommand chain.
# since we're in a regular command (and not a group) then
# since we're in a regular prefix command (and not a group) then
# the invoked subcommand is None.
ctx.invoked_subcommand = None
ctx.subcommand_passed = None
Expand Down
6 changes: 3 additions & 3 deletions disnake/ext/commands/interaction_bot_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ def slash_command(
extras: Optional[Dict[str, Any]] = None,
**kwargs,
) -> Callable[[CommandCallback], InvokableSlashCommand]:
"""A shortcut decorator that invokes :func:`.slash_command` and adds it to
"""A shortcut decorator that invokes :func:`~disnake.ext.commands.slash_command` and adds it to
the internal command list.
Parameters
Expand Down Expand Up @@ -589,7 +589,7 @@ def user_command(
) -> Callable[
[InteractionCommandCallback[CogT, UserCommandInteraction, P]], InvokableUserCommand
]:
"""A shortcut decorator that invokes :func:`.user_command` and adds it to
"""A shortcut decorator that invokes :func:`~disnake.ext.commands.user_command` and adds it to
the internal command list.
Parameters
Expand Down Expand Up @@ -666,7 +666,7 @@ def message_command(
) -> Callable[
[InteractionCommandCallback[CogT, MessageCommandInteraction, P]], InvokableMessageCommand
]:
"""A shortcut decorator that invokes :func:`.message_command` and adds it to
"""A shortcut decorator that invokes :func:`~disnake.ext.commands.message_command` and adds it to
the internal command list.
Parameters
Expand Down
38 changes: 30 additions & 8 deletions docs/_static/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class Sidebar {
let next = ref.nextElementSibling;

if (next && next.tagName === "UL") {

let icon = document.createElement('span');
icon.className = 'material-icons collapsible-arrow expanded';
icon.innerText = 'expand_more';
Expand All @@ -54,24 +53,36 @@ class Sidebar {

ref.classList.add('ref-internal-padding')
ref.parentNode.insertBefore(icon, ref);

// collapse all top-level toc entries, except the current page's
// (i.e. all entries that don't contain a `#`)
const refUrl = new URL(ref.href);
if (!refUrl.hash) {
// `false` to update immediately
this.collapseSection(icon, false);
}
}
}
}

collapseSection(icon) {
collapseSection(icon, defer = true) {
icon.classList.remove('expanded');
icon.classList.add('collapsed');
let children = icon.nextElementSibling.nextElementSibling;
// <arrow><heading>
// --> <square><children>
setTimeout(() => children.style.display = "none", 75)
const update = () => children.style.display = "none";
if (defer) setTimeout(update, 75);
else update();
}

expandSection(icon) {
expandSection(icon, defer = true) {
icon.classList.remove('collapse');
icon.classList.add('expanded');
let children = icon.nextElementSibling.nextElementSibling;
setTimeout(() => children.style.display = "block", 75)
const update = () => children.style.display = "block";
if (defer) setTimeout(update, 75);
else update();
}

setActiveLink(section) {
Expand All @@ -92,6 +103,13 @@ class Sidebar {
}
}

scrollToCurrent() {
const currentSection = this.element.querySelector("li.current");
if (currentSection) {
// setTimeout(..., 0) to avoid layout race condition
setTimeout(() => currentSection.scrollIntoView({block: "center"}), 0);
}
}
}

function getCurrentSection() {
Expand All @@ -101,10 +119,12 @@ function getCurrentSection() {
}
else {
if (sections) {
const headerOffset = document.querySelector("main").offsetTop; // height of header
sections.forEach(section => {
let rect = section.getBoundingClientRect();
// offset to give space for the sticky header
if (rect.top - 90 + document.body.offsetTop < 1) {
const rect = section.getBoundingClientRect();
// plus offset for more leniency
// (section doesn't have to be scrolled all the way to the top to be considered active)
if (rect.top < headerOffset + 50) {
currentSection = section;
}
});
Expand All @@ -113,9 +133,11 @@ function getCurrentSection() {
return currentSection;
}

// create interactive sidebar
document.addEventListener('DOMContentLoaded', () => {
sidebar = new Sidebar(document.getElementById('sidebar'));
sidebar.createCollapsableSections();
sidebar.scrollToCurrent();

window.addEventListener('scroll', () => {
sidebar.setActiveLink(getCurrentSection());
Expand Down
41 changes: 41 additions & 0 deletions docs/_templates/api_redirect.js_t
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT

"use strict";

const redirects_map = {{ redirect_data }};

(() => {
let url = new URL(document.location.href);

if (!url.pathname.endsWith("/api.html")) return;

// URL_ROOT is relative to `url`, and points to e.g. `<host>/en/latest/`
const root = new URL(DOCUMENTATION_OPTIONS.URL_ROOT, url);
if (!root.pathname.endsWith("/")) root.pathname += "/";

const targetPath = redirects_map[url.hash.slice(1)];

let newUrl = null;
if (targetPath) {
// Get current path relative to documentation root
// `/en/latest/xyz/api.html` => `xyz/api/`
let expectedPrefix = url.pathname.slice(root.pathname.length).replace(/\.html$/, "/");

// The target path (`xyz/api/events.html`) should start with `xyz/api/`
if (targetPath.startsWith(expectedPrefix)) {
newUrl = new URL(targetPath, root);
newUrl.hash = url.hash;
}
}

// If it's still unset, the #hash is unknown or the target doesn't match the current section;
// in that case, just redirect to the relevant index page.
if (newUrl === null) {
newUrl = new URL(url);
newUrl.pathname = newUrl.pathname.replace(/\/api.html$/, "/api/index.html");
newUrl.hash = "";
}

console.log(`Redirecting to ${newUrl.href}`);
window.location.replace(newUrl);
})();
15 changes: 9 additions & 6 deletions docs/_templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
{%- block extrahead %} {% endblock %}
<!-- end extra head -->
<link rel="stylesheet" href="{{ pathto('_static/icons.css', 1)|e }}" type="text/css" />
{%- block css %}
{%- for css in css_files %}
Expand All @@ -22,6 +21,10 @@
<link rel="stylesheet" href="{{ pathto('_static/codeblocks.css', 1) }}" type="text/css" />
{%- block scripts %}
<script id="documentation_options" data-url_root="{{ pathto('', 1) }}" src="{{ pathto('_static/documentation_options.js', 1) }}"></script>
{% if ("/" + pagename).endswith("/api") %}
<meta name="robots" content="noindex" />
<script type="text/javascript" src="{{ pathto('_static/api_redirect.js', 1) }}"></script>
{% endif %}
{%- for js in script_files %}
{{ js_tag(js) }}
{%- endfor %}
Expand Down Expand Up @@ -66,7 +69,7 @@
<header class="top-header mobile">
<nav>
<div class="header-breakpoint">
<a href="{{ pathto('api')|e }}" class="extension-nav">API Reference</a>
<a href="{{ pathto('api/index')|e }}" class="extension-nav">API Reference</a>
{%- for ext, p in discord_extensions %}
{% set ext_name = ext.split('.') %}
<a href="{{ pathto(p + '/index')|e }}" class="extension-nav {% if pagename.startswith(p) %}current-page{% endif %}">{{ ext_name[-1] }}</a>
Expand All @@ -76,11 +79,11 @@
</header>
<header class="sticky-header">
<nav>
<a href="{{ pathto(master_doc)|e }}" class="main-heading desktop">
<a href="{{ pathto(root_doc)|e }}" class="main-heading desktop">
<img class="disnake-main-logo" src="{{ pathto('_static/disnake.svg', 1)|e }}" alt="disnake">
</a>
<div class="header-breakpoint desktop extension-breakpoint desktop">
<a href="{{ pathto('api')|e }}" class="extension-nav">{{ _('API Reference') }}</a>
<a href="{{ pathto('api/index')|e }}" class="extension-nav">{{ _('API Reference') }}</a>
{%- for ext, p in discord_extensions %}
{% set ext_name = ext.split('.') %}
<a href="{{ pathto(p + '/index')|e }}" class="extension-nav {% if pagename.startswith(p) %}current-page{% endif %}">{{ ext_name[-1] }}</a>
Expand Down Expand Up @@ -111,11 +114,11 @@
</header>
{#- The sidebar component #}
<aside>
<a href="{{ pathto(master_doc)|e }}" class="mobile-heading">
<a href="{{ pathto(root_doc)|e }}" class="mobile-heading">
<img class="disnake-mobile-logo" src="{{ pathto('_static/disnake.svg', 1)|e }}" alt="disnake">
</a>
<div class="sidebar-extension-list mobile-heading">
<a href="{{ pathto('api')|e }}" class="extension-nav">API Reference</a>
<a href="{{ pathto('api/index')|e }}" class="extension-nav">API Reference</a>
{%- for ext, p in discord_extensions %}
{% set ext_name = ext.split('.') %}
<a href="{{ pathto(p + '/index')|e }}" class="extension-nav {% if pagename.startswith(p) %}current-page{% endif %}">{{ ext_name[-1] }}</a>
Expand Down

0 comments on commit 04266b4

Please sign in to comment.