Clarify when named positional attributes are mapped and their precedence
AsciiDoc supports a feature in which positional attributes are mapped onto named attributes if the block provides this mapping. One such example is the alt (text), width and height attributes on the block macro. These are known as named positional attributes, or posattrs for short. We need to clarify when positional attributes are mapped to names and processed and what precedence they have over regular named attributes, if any.
Let's consider a block image macro:
image::diagram.png[Diagram,300,400]
The posattrs for an image block are alt,width,height
. That means that that the first positional attribute will be assigned to the alt
attribute, the second positional attribute will be assigned to the width
attribute, and the third to the height
attribute.
The first question is, when does this happen? To be consistent with Asciidoctor, this mapping occurs after all the attrlists have been parsed and once the block is known. In the block grammar, we may choose to do this assignment in the action for any block (the final action).
if (posattrs) {
for (let i = 0, num = posattrs.length; i < num; i++) {
const posKey = `$${i + 1}`
if (posKey in attributes) attributes[posattrs[i]] = attributes[posKey]
}
}
In order to communicate the list of named positional attributes, the grammar action for a specific block would have to pass this through using a reserved property that's later deleted.
node.posattrs = ['alt', 'width', 'height']
We need to consider what happens if both the positional and named attributes are specified. For example:
image::diagram.png[Diagram,300,400,alt=A symbolic representation of something]
What is the value of the alt
attribute in this case? We could say that if the attribute is already assigned, it cannot be overwritten by a named positional attribute. Keep in mind that block attributes could be defined in block attribute lines too:
[Diagram]
image::diagram.png[,300,400,alt=A symbolic representation of something]
It seems obvious in this case that the named alt
attribute would win out. But note that we can't rely on the order in which the attributes are specified since we won't have that information at the time the posattrs are mapped...unless we were to fundamentally change the process by which attrlists are parsed.
Note that in the initial contribution (the docs from Asciidoctor), a posattr is allowed to override an existing named attribute for blocks. However, it does not override an existing named attribute defined in the boxed attrlist of a block macro. This inconsistency needs to be rectified.
By mapping named positional attributes in the block action, it means those attributes will not be assigned while descendant blocks are being parsed. But this seems acceptable. The alternative would be to map the named positional attributes after the opening delimited block or marker is matched, though that would result in a much noisier grammar.
Another issue that comes up are the positional content attributes on quote and verse blocks. Consider the following case:
[,'https://en.wikipedia.org/wiki/Martin_Luther_King_Jr.[Martin Luther King Jr.]']
____
World peace through nonviolent means is neither absurd nor unattainable.
____
We currently rely on the explicit name (e.g., attribution
) to determine if the value should be parsed as a content attribute (parsed into inlines). However, when parsing attrlists, we don't yet know the posattrs, or even know that we're above a quote block or paragraph. And yet we have to parse the attrlists ahead of the block in order to know the style (such as the discrete style above a heading or the cols attribute above a table). So there's a circular dependency we have to unlock here.
One option would be to run the inline parser on any single-quoted positional attribute. The problem here is that a positional attribute which is not enclosed in single-quotes may end up being mapped as a content attribute, and thus wouldn't be in the right form as an array of inlines. So we're kind of stuck in between.
Another option would be make a special exception for quote and verse blocks. To do so, the block metadata action would need to look ahead to see if it's above a quote container and, if so, pass that information in to the attrlist parser. The attrlist parser would then recognize the second and third positional attributes as content attributes and parse them appropriately. The attrlist parser would also need to check if the explicit style is quote or verse and activate this feature in that case as well. With this approach, there would still be room for supporting this feature for custom blocks since that information would be accessible from the explicit style, which is always required in the case of a custom block. So this solution might actually work. The only downside is that the explicit style would have to appear in document order before any positional attributes it impacts.
In summary, there are several decisions to make:
- How is the named positional attribute list (posattrs) communicated in the grammar?
- When are the named positional attributes mapped during parsing?
- Do named positional attributes override named attributes already defined?
- How are the positional content attributes for quote and verse blocks handled?
- Is it possible for a custom block to define named positional content attributes (like quote and verse blocks?)