Thanks for this: the document-relative content_offset is exactly the contract I was missing.
One adjacent bug this PR doesn't cover, in case you'd like to fold it in while you're in here: body_offset is inflated by one when a directive has an option block and its body ends in blank line(s). It's independent of the relative-to-absolute reframe: it lives in the len(content.splitlines()) - len(body_lines) line, which this PR leaves unchanged, so it persists on this branch.
See:
from docutils.parsers.rst.directives.admonitions import Note
from myst_parser.parsers.directives import parse_directive_text
parse_directive_text(Note, "", ":class: tip\nbody").body_offset # 1 (correct)
parse_directive_text(Note, "", ":class: tip\nbody\n\n\n").body_offset # 2 (should be 1)
# YAML-style option block inflates the same way: 3 -> 4
Root cause: body_lines comes back from _parse_directive_options through a "\n".join(...) round-trip that drops exactly one trailing blank line, so when the body ends in blank line(s) the subtraction over-counts the option block by one. Every body element is then reported one source line too low.
Originally posted by @kdeldycke in #1175 (comment)
Thanks for this: the document-relative
content_offsetis exactly the contract I was missing.One adjacent bug this PR doesn't cover, in case you'd like to fold it in while you're in here:
body_offsetis inflated by one when a directive has an option block and its body ends in blank line(s). It's independent of the relative-to-absolute reframe: it lives in thelen(content.splitlines()) - len(body_lines)line, which this PR leaves unchanged, so it persists on this branch.See:
Root cause:
body_linescomes back from_parse_directive_optionsthrough a"\n".join(...)round-trip that drops exactly one trailing blank line, so when the body ends in blank line(s) the subtraction over-counts the option block by one. Every body element is then reported one source line too low.Originally posted by @kdeldycke in #1175 (comment)