feat: better error reporting for missing link alt text

this can only really happen using a position as a link target
This commit is contained in:
Tobias Schmitz 2025-07-25 13:22:57 +02:00
parent b7ccf9717e
commit 7a3e2dbcbe
No known key found for this signature in database
5 changed files with 12 additions and 9 deletions

View File

@ -216,13 +216,19 @@ const TERMS_RULE: ShowFn<TermsElem> = |elem, _, styles| {
const LINK_MARKER_RULE: ShowFn<LinkMarker> = |elem, _, _| Ok(elem.body.clone()); const LINK_MARKER_RULE: ShowFn<LinkMarker> = |elem, _, _| Ok(elem.body.clone());
const LINK_RULE: ShowFn<LinkElem> = |elem, engine, styles| { const LINK_RULE: ShowFn<LinkElem> = |elem, engine, styles| {
let span = elem.span();
let body = elem.body.clone(); let body = elem.body.clone();
let dest = elem.dest.resolve(engine.introspector).at(elem.span())?; let dest = elem.dest.resolve(engine.introspector).at(elem.span())?;
let alt = match elem.alt.get_cloned(styles) { let alt = match elem.alt.get_cloned(styles) {
Some(alt) => Some(alt), Some(alt) => Some(alt),
None => dest.alt_text(engine, styles)?, None => dest.alt_text(engine, styles)?,
}; };
Ok(body.linked(dest, alt)) // Manually construct link marker that spans the whole link elem, not just
// the body.
Ok(LinkMarker::new(body, dest.clone(), alt)
.pack()
.spanned(span)
.set(LinkElem::current, Some(dest)))
}; };
const HEADING_RULE: ShowFn<HeadingElem> = |elem, engine, styles| { const HEADING_RULE: ShowFn<HeadingElem> = |elem, engine, styles| {

View File

@ -479,7 +479,7 @@ impl Content {
/// Link the content somewhere. /// Link the content somewhere.
pub fn linked(self, dest: Destination, alt: Option<EcoString>) -> Self { pub fn linked(self, dest: Destination, alt: Option<EcoString>) -> Self {
let span = self.span(); let span = self.span();
LinkMarker::new(self, dest.clone(), alt, span) LinkMarker::new(self, dest.clone(), alt)
.pack() .pack()
.spanned(span) .spanned(span)
.set(LinkElem::current, Some(dest)) .set(LinkElem::current, Some(dest))
@ -797,9 +797,6 @@ pub struct LinkMarker {
#[internal] #[internal]
#[required] #[required]
pub alt: Option<EcoString>, pub alt: Option<EcoString>,
#[internal]
#[required]
pub span: Span,
} }
impl Construct for LinkMarker { impl Construct for LinkMarker {

View File

@ -617,7 +617,7 @@ fn convert_error(
let span = to_span(*loc); let span = to_span(*loc);
error!( error!(
span, "{prefix} missing annotation alt text"; span, "{prefix} missing annotation alt text";
hint: "please report this as a bug" hint: "manually add alt text to your links"
) )
} }
ValidationError::MissingAltText(loc) => { ValidationError::MissingAltText(loc) => {

View File

@ -71,7 +71,7 @@ pub(crate) fn handle_link(
let (alt, span) = if let Some((link, nodes)) = tagging_ctx { let (alt, span) = if let Some((link, nodes)) = tagging_ctx {
nodes.push(TagNode::Placeholder(placeholder)); nodes.push(TagNode::Placeholder(placeholder));
let alt = link.alt.as_ref().map(EcoString::to_string); let alt = link.alt.as_ref().map(EcoString::to_string);
(alt, link.span) (alt, link.span())
} else { } else {
(None, Span::detached()) (None, Span::detached())
}; };

View File

@ -535,10 +535,10 @@ impl TagStack {
pub(crate) fn find_parent_link( pub(crate) fn find_parent_link(
&mut self, &mut self,
) -> Option<(LinkId, &LinkMarker, &mut Vec<TagNode>)> { ) -> Option<(LinkId, &Packed<LinkMarker>, &mut Vec<TagNode>)> {
self.0.iter_mut().rev().find_map(|e| { self.0.iter_mut().rev().find_map(|e| {
let (link_id, link) = e.kind.as_link()?; let (link_id, link) = e.kind.as_link()?;
Some((link_id, link.as_ref(), &mut e.nodes)) Some((link_id, link, &mut e.nodes))
}) })
} }