Skip to content

Setext Heading LRDs take precedence over Paragraph LRDs #298

@madimadica

Description

@madimadica

The only reference to precedence I could find in the spec is this:

If there are multiple matching reference link definitions, the one that comes first in the document is used. (It is desirable in such cases to emit a warning.)

The issue is that when you define a LRD (link reference definition) in a Setext heading (or a probable Setext heading*), this link will populate the refmap before any paragraph LRDs are parsed, which occurs at the document finalization step.

*A paragraph interrupted by a setext heading underline that only contains links, thus creating an empty <p>, which is immediately unlinked from the AST.

Example inputs:

[x]: fizz

[x]: buzz
===
[x]

has an href=buzz,

Whereas

[x]: fizz

[x]: buzz

[x]

has an href=fizz.

My interpretation of the spec is that both should result in href=fizz as that is the first LRD.

Assuming this is a bug, my idea of a fix would be to use a modified Parser.refmap where it has an Map<number, Array<LRD>> and the key would be the line number of the containing paragraph (sourcepos[0][0]), as the exact lines don't matter as long as the array is populated in-order.

So if this was the input

[line1]: a
[line2]: b
rest of paragraph

[line5]: e
===

would have the map

{
 5: [e],
 1: [a, b]
}

(where a, b, e are objects of typedef LRD {destination: string, title: string, label: string} with map def
Map<number, Array<LRD>>)

Then setext headings' block start and document finalization can occur in the order they do, but could add another step after document finalization. E.g.

/**
 * @param {Map<number, Array<{destination: string, title: string, label: string}>>} unorderedRefmap
 */
function getFinalRefmap(unorderedRefmap) {
  const sortedKeys = [...unorderedRefmap.keys()].sort((a, b) => a - b);
  const finalRefmap = {};
  for (const k of sortedKeys) {
    const values = unorderedRefmap.get(k);
    for (const v of values) {
      if (!finalRefmap[v.label]) {
        finalRefmap[v.label] = {destination: v.destination, title: v.title};
      }
    }
  }
  return finalRefmap ;
}

Something like that should work, its just a matter of adding to the placeholder refmap correctly, which is a bit trickier since inline.js needs a reference to it and mutates it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions