diff --git a/markdown_it/main.py b/markdown_it/main.py
index bb294a99..ad33c9ab 100644
--- a/markdown_it/main.py
+++ b/markdown_it/main.py
@@ -2,7 +2,7 @@
 
 from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping
 from contextlib import contextmanager
-from typing import Any, Literal, overload
+from typing import Any, Literal, Sequence, overload
 
 from . import helpers, presets
 from .common import normalize_url, utils
@@ -144,7 +144,7 @@ def configure(
         return self
 
     def get_all_rules(self) -> dict[str, list[str]]:
-        """Return the names of all active rules."""
+        """Return the names of all rules."""
         rules = {
             chain: self[chain].ruler.get_all_rules()
             for chain in ["core", "block", "inline"]
@@ -227,14 +227,23 @@ def reset_rules(self) -> Generator[None, None, None]:
         self.inline.ruler2.enableOnly(chain_rules["inline2"])
 
     def add_render_rule(
-        self, name: str, function: Callable[..., Any], fmt: str = "html"
+        self,
+        name: str,
+        function: Callable[
+            [RendererProtocol, Sequence[Token], int, OptionsDict, EnvType], str
+        ],
+        fmt: str = "html",
     ) -> None:
         """Add a rule for rendering a particular Token type.
 
         Only applied when ``renderer.__output__ == fmt``
+
+        :param name: the name of the token type
+        :param function: the function to call to render the token;
+            it should have the signature ``function(renderer, tokens, idx, options, env)``
         """
         if self.renderer.__output__ == fmt:
-            self.renderer.rules[name] = function.__get__(self.renderer)  # type: ignore
+            self.renderer.rules[name] = function.__get__(self.renderer)
 
     def use(
         self, plugin: Callable[..., None], *params: Any, **options: Any
diff --git a/markdown_it/parser_block.py b/markdown_it/parser_block.py
index 72360f9b..de9b3b2b 100644
--- a/markdown_it/parser_block.py
+++ b/markdown_it/parser_block.py
@@ -57,7 +57,7 @@ def __init__(self) -> None:
 
     def tokenize(self, state: StateBlock, startLine: int, endLine: int) -> None:
         """Generate tokens for input range."""
-        rules = self.ruler.getRules("")
+        rules = self.ruler.getRules()
         line = startLine
         maxNesting = state.md.options.maxNesting
         hasEmptyLines = False
diff --git a/markdown_it/parser_core.py b/markdown_it/parser_core.py
index ca5ab256..82b628b7 100644
--- a/markdown_it/parser_core.py
+++ b/markdown_it/parser_core.py
@@ -41,5 +41,5 @@ def __init__(self) -> None:
 
     def process(self, state: StateCore) -> None:
         """Executes core chain rules."""
-        for rule in self.ruler.getRules(""):
+        for rule in self.ruler.getRules():
             rule(state)
diff --git a/markdown_it/parser_inline.py b/markdown_it/parser_inline.py
index 0026c383..d0c587a4 100644
--- a/markdown_it/parser_inline.py
+++ b/markdown_it/parser_inline.py
@@ -67,7 +67,7 @@ def skipToken(self, state: StateInline) -> None:
         """
         ok = False
         pos = state.pos
-        rules = self.ruler.getRules("")
+        rules = self.ruler.getRules()
         maxNesting = state.md.options["maxNesting"]
         cache = state.cache
 
@@ -106,7 +106,7 @@ def skipToken(self, state: StateInline) -> None:
     def tokenize(self, state: StateInline) -> None:
         """Generate tokens for input range."""
         ok = False
-        rules = self.ruler.getRules("")
+        rules = self.ruler.getRules()
         end = state.posMax
         maxNesting = state.md.options["maxNesting"]
 
@@ -141,7 +141,7 @@ def parse(
         """Process input string and push inline tokens into `tokens`"""
         state = StateInline(src, md, env, tokens)
         self.tokenize(state)
-        rules2 = self.ruler2.getRules("")
+        rules2 = self.ruler2.getRules()
         for rule in rules2:
             rule(state)
         return state.tokens
diff --git a/markdown_it/renderer.py b/markdown_it/renderer.py
index 7fee9ffa..1438c4d0 100644
--- a/markdown_it/renderer.py
+++ b/markdown_it/renderer.py
@@ -9,21 +9,31 @@ class Renderer
 
 from collections.abc import Sequence
 import inspect
-from typing import Any, ClassVar, Protocol
+from typing import TYPE_CHECKING, Any, Callable, ClassVar, MutableMapping, Protocol
 
 from .common.utils import escapeHtml, unescapeAll
 from .token import Token
 from .utils import EnvType, OptionsDict
 
+if TYPE_CHECKING:
+    from markdown_it import MarkdownIt
+
 
 class RendererProtocol(Protocol):
     __output__: ClassVar[str]
+    rules: MutableMapping[
+        str,
+        Callable[[Sequence[Token], int, OptionsDict, EnvType], str],
+    ]
 
     def render(
         self, tokens: Sequence[Token], options: OptionsDict, env: EnvType
     ) -> Any:
         ...
 
+    # note container and admon plugins also expect renderToken to be defined,
+    # but it is unclear if this should be a requirement for all renderers
+
 
 class RendererHTML(RendererProtocol):
     """Contains render rules for tokens. Can be updated and extended.
@@ -57,7 +67,7 @@ def strong_close(self, tokens, idx, options, env):
 
     __output__ = "html"
 
-    def __init__(self, parser: Any = None):
+    def __init__(self, parser: None | MarkdownIt = None):
         self.rules = {
             k: v
             for k, v in inspect.getmembers(self, predicate=inspect.ismethod)
diff --git a/markdown_it/ruler.py b/markdown_it/ruler.py
index bd8baba3..a38a4037 100644
--- a/markdown_it/ruler.py
+++ b/markdown_it/ruler.py
@@ -59,6 +59,7 @@ def srcCharCode(self) -> tuple[int, ...]:
 
 class RuleOptionsType(TypedDict, total=False):
     alt: list[str]
+    """list of rules which can be terminated by this one."""
 
 
 RuleFuncTv = TypeVar("RuleFuncTv")
@@ -71,9 +72,12 @@ class Rule(Generic[RuleFuncTv]):
     enabled: bool
     fn: RuleFuncTv = field(repr=False)
     alt: list[str]
+    """list of rules which can be terminated by this one."""
 
 
 class Ruler(Generic[RuleFuncTv]):
+    """Class to manage functions (rules) which identify syntax elements."""
+
     def __init__(self) -> None:
         # List of added rules.
         self.__rules__: list[Rule[RuleFuncTv]] = []
@@ -255,10 +259,13 @@ def disable(
 
     def getRules(self, chainName: str = "") -> list[RuleFuncTv]:
         """Return array of active functions (rules) for given chain name.
+
         It analyzes rules configuration, compiles caches if not exists and returns result.
 
-        Default chain name is `''` (empty string). It can't be skipped.
-        That's done intentionally, to keep signature monomorphic for high speed.
+        :param chainName: name of chain to return rules for:
+            - The default `""` means all "top-level rules for this ruler.
+            - A specific name can be used to fetch only rules which can terminate
+              the named rule (used for block level rules like paragraph, list, etc.)
 
         """
         if self.__cache__ is None:
diff --git a/markdown_it/rules_block/state_block.py b/markdown_it/rules_block/state_block.py
index 445ad265..ffcdf386 100644
--- a/markdown_it/rules_block/state_block.py
+++ b/markdown_it/rules_block/state_block.py
@@ -28,40 +28,68 @@ def __init__(
 
         self.tokens = tokens
 
-        self.bMarks: list[int] = []  # line begin offsets for fast jumps
-        self.eMarks: list[int] = []  # line end offsets for fast jumps
-        # offsets of the first non-space characters (tabs not expanded)
+        self.bMarks: list[int] = []
+        """line begin offsets for fast jumps"""
+
+        self.eMarks: list[int] = []
+        """line end offsets for fast jumps"""
+
         self.tShift: list[int] = []
-        self.sCount: list[int] = []  # indents for each line (tabs expanded)
+        """Offsets of the first non-space characters (tabs not expanded)"""
+
+        self.sCount: list[int] = []
+        """indents for each line (tabs expanded)"""
 
-        # An amount of virtual spaces (tabs expanded) between beginning
-        # of each line (bMarks) and real beginning of that line.
-        #
-        # It exists only as a hack because blockquotes override bMarks
-        # losing information in the process.
-        #
-        # It's used only when expanding tabs, you can think about it as
-        # an initial tab length, e.g. bsCount=21 applied to string `\t123`
-        # means first tab should be expanded to 4-21%4 === 3 spaces.
-        #
         self.bsCount: list[int] = []
+        """
+        An amount of virtual spaces (tabs expanded) between beginning
+        of each line (bMarks) and real beginning of that line.
+
+        It exists only as a hack because blockquotes override bMarks
+        losing information in the process.
+
+        It's used only when expanding tabs, you can think about it as
+        an initial tab length, e.g. `bsCount=21` applied to string `\\t123`
+        means first tab should be expanded to `4-21 % 4 == 3` spaces.
+        """
 
+        #
         # block parser variables
-        self.blkIndent = 0  # required block content indent (for example, if we are
-        # inside a list, it would be positioned after list marker)
-        self.line = 0  # line index in src
-        self.lineMax = 0  # lines count
-        self.tight = False  # loose/tight mode for lists
-        self.ddIndent = -1  # indent of the current dd block (-1 if there isn't any)
-        self.listIndent = -1  # indent of the current list block (-1 if there isn't any)
-
-        # can be 'blockquote', 'list', 'root', 'paragraph' or 'reference'
-        # used in lists to determine if they interrupt a paragraph
+        #
+
+        self.blkIndent = 0
+        """required block content indent
+        (for example, if we are inside a list, it would be positioned after list marker)
+        """
+
+        self.line = 0
+        """line index in src"""
+        self.lineMax = 0
+        """Total lines count"""
+
+        self.tight = False
+        """loose/tight mode for lists"""
+
+        self.ddIndent = -1
+        """indent of the current dd block (-1 if there isn't any),
+        used only by deflist plugin
+        """
+
+        self.listIndent = -1
+        """indent of the current list block (-1 if there isn't any)"""
+
         self.parentType = "root"
+        """
+        can be 'blockquote', 'list', 'root', 'paragraph' or 'reference'
+        used in lists to determine if they interrupt a paragraph
+        """
 
         self.level = 0
+        """Current nesting level of tokens,
+        +1 when adding opening token, -1 when adding closing token
+        """
 
-        # renderer
+        # renderer (does not appear to be used)
         self.result = ""
 
         # Create caches
diff --git a/markdown_it/rules_inline/state_inline.py b/markdown_it/rules_inline/state_inline.py
index c0c491c4..0eb75ff2 100644
--- a/markdown_it/rules_inline/state_inline.py
+++ b/markdown_it/rules_inline/state_inline.py
@@ -51,28 +51,42 @@ def __init__(
         self.tokens_meta: list[dict[str, Any] | None] = [None] * len(outTokens)
 
         self.pos = 0
+        """Current position in src string"""
         self.posMax = len(self.src)
+        """Length of the src string"""
         self.level = 0
+        """Current nesting level of tokens,
+        +1 when adding opening token, -1 when adding closing token
+        """
         self.pending = ""
+        """Accumulated text not yet converted to a token.
+        This will be added as a `text` token when the next token is pushed (before it),
+        or when the parser finishes running (after all other tokens).
+        """
         self.pendingLevel = 0
+        """The nesting level of the pending text"""
 
-        # Stores { start: end } pairs. Useful for backtrack
-        # optimization of pairs parse (emphasis, strikes).
         self.cache: dict[int, int] = {}
+        """
+        Stores { start: end } pairs.
+        Useful for backtrack optimization of pairs parse (emphasis, strikes).
+        """
 
-        # List of emphasis-like delimiters for current tag
         self.delimiters: list[Delimiter] = []
+        """List of emphasis-like delimiters for current tag"""
 
-        # Stack of delimiter lists for upper level tags
         self._prev_delimiters: list[list[Delimiter]] = []
+        """Stack of delimiter lists for upper level tags"""
 
-        # backticklength => last seen position
         self.backticks: dict[int, int] = {}
+        """backticklength => last seen position"""
         self.backticksScanned = False
 
-        # Counter used to disable inline linkify-it execution
-        # inside  and markdown links
         self.linkLevel = 0
+        """
+        Counter used to disable inline linkify-it execution
+        inside `` and markdown links
+        """
 
     def __repr__(self) -> str:
         return (
diff --git a/pyproject.toml b/pyproject.toml
index ea7cd036..633eef2e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -45,7 +45,7 @@ compare = [
 linkify = ["linkify-it-py>=1,<3"]
 plugins = ["mdit-py-plugins"]
 rtd = [
-    "mdit-py-plugins @ git+https://github.com/executablebooks/mdit-py-plugins@master",
+    "mdit-py-plugins",
     "myst-parser",
     "pyyaml",
     "sphinx",