[Pitch] Markdown List Delimiters

Hi everyone! I have a new pitch below for a new AttributedString attribute that I'd like to populate during markdown parsing to provide what delimiter was used in markdown source for entries in a list so that a rendered view of the markdown can use an appropriate matching delimiter. Take a look and let me know if you have any thoughts!


Markdown List Item Delimiters

Revision history

  • v1 Initial version

Introduction/Motivation

The markdown spec supports two types of lists: bullet lists and ordered lists. In Foundation's API today, we refer to them as unordered and ordered lists respectively via PresentationIntent.Kind.unorderedList/PresentationIntent.Kind.orderedList. Every item in a list begins with a "list item marker". For ordered lists, the list item marker is an ordinal number followed by a "." (U+002E Full Stop) or ")" (U+0029 Right Parenthesis) delimiter character (ex. "1." or "2)"). For unordered lists, the list item marker is one of three possible characters: "-" (U+002D Hyphen-Minus), "+" (U+002B Plus Sign), or "*" (U+002A Asterisk). Note that lists must use a consistent list marker for every item in the list.

Today, an ordered list item's ordinal number is exposed via Foundation's APIs via the .listItem(ordinal: Int) PresentationIntent.Kind case for Swift and the ordinal property on the NSPresentationIntent type for Objective-C. However, we do not currently expose which list item marker a parsed unordered list used or which delimiter follows the ordinal numbers in an ordered list. It's reasonable that some markdown rendering views may wish to render the produced AttributedString's list using the same list item characters in the original source text (or a comparible symbol to the original item) in which case it is important that the produced AttributedString provides which list item marker was used to denote items in a list.

Proposed solution and example

To solve this, we propose adding a new attributed string attribute that will expose the list item delimiter for the current list in the produced attributed string. The value of this attribute will be a "." or ")" for ordered lists and a "-", "+", or "*" for unordered lists. Developers will be able to access this value like the following:

let attrStr = try AttributedString(markdown: /* ... some markdown string ... */)

for (intent, range) in attrStr.runs[\.presentationIntent] {
    guard let component = intent.components.last else { continue }
    
    switch component.kind {
    case .listItem(let ordinal):
        let isOrdered = intent.components[intent.components.count - 2] == .orderedList
        let listItemDelimiter = attrStr[range].listItemDelimiter
        // Process list item using isOrdered, ordinal, and listItemDelimiter...
    }
}

For a full detailed design and considered alternatives, check out the full proposal in the swift-foundation repo PR.

1 Like