Swift Markdown is a Swift package for parsing, building, editing, and analyzing Markdown documents.

Last update: Aug 7, 2022

Swift Markdown

Swift Markdown is a Swift package for parsing, building, editing, and analyzing Markdown documents.

The parser is powered by GitHub-flavored Markdown's cmark-gfm implementation, so it follows the spec closely. As the needs of the community change, the effective dialect implemented by this library may change.

The markup tree provided by this package is comprised of immutable/persistent, thread-safe, copy-on-write value types that only copy substructure that has changed. Other examples of the main strategy behind this library can be seen in Swift's lib/Syntax and its Swift bindings, SwiftSyntax.

Getting Started Using Markup

In your Package.swift Swift Package Manager manifest, add the following dependency to your dependencies argument:

.package(url: "ssh://[email protected]/apple/swift-markdown.git", .branch("main")),

Add the dependency to any targets you've declared in your manifest:

.target(name: "MyTarget", dependencies: ["Markdown"]),

Parsing

To parse a document, use Document(parsing:), supplying a String or URL:

import Markdown

let source = "This is a markup *document*."
let document = Document(parsing: source)
print(document.debugDescription())
// Document
// └─ Paragraph
//    ├─ Text "This is a markup "
//    ├─ Emphasis
//    │  └─ Text "document"
//    └─ Text "."

Parsing text is just one way to build a tree of Markup elements. You can also build them yourself declaratively.

Building Markup Trees

You can build trees using initializers for the various element types provided.

import Markdown

let document = Document(
    Paragraph(
        Text("This is a "),
        Emphasis(
            Text("paragraph."))))

This would be equivalent to parsing "This is a *paragraph.*" but allows you to programmatically insert content from other data sources into individual elements.

Modifying Markup Trees with Persistence

Swift Markdown uses a persistent tree for its backing storage, providing effectively immutable, copy-on-write value types that only copy the substructure necessary to create a unique root without affecting the previous version of the tree.

Modifying Elements Directly

If you just need to make a quick change, you can modify an element anywhere in a tree, and Swift Markdown will create copies of substructure that cannot be shared.

import Markdown

let source = "This is *emphasized.*"
let document = Document(parsing: source)
print(document.debugDescription())
// Document
// └─ Paragraph
//    ├─ Text "This is "
//    └─ Emphasis
//       └─ Text "emphasized."

var text = document.child(through:
    0, // Paragraph
    1, // Emphasis
    0) as! Text // Text

text.string = "really emphasized!"
print(text.root.debugDescription())
// Document
// └─ Paragraph
//    ├─ Text "This is "
//    └─ Emphasis
//       └─ Text "really emphasized!"

// The original document is unchanged:

print(document.debugDescription())
// Document
// └─ Paragraph
//    ├─ Text "This is "
//    └─ Emphasis
//       └─ Text "emphasized."

If you find yourself needing to systematically change many parts of a tree, or even provide a complete transformation into something else, maybe the familiar Visitor Pattern is what you want.

Visitors, Walkers, and Rewriters

There is a core MarkupVisitor protocol that provides the basis for transforming, walking, or rewriting a markup tree.

public protocol MarkupVisitor {
    associatedtype Result
}

Using its Result type, you can transform a markup tree into anything: another markup tree, or perhaps a tree of XML or HTML elements. There are two included refinements of MarkupVisitor for common uses.

The first refinement, MarkupWalker, has an associated Result type of Void, so it's meant for summarizing or detecting aspects of a markup tree. If you wanted to append to a string as elements are visited, this might be a good tool for that.

import Markdown

/// Counts `Link`s in a `Document`.
struct LinkCounter: MarkupWalker {
    var count = 0
    mutating func visitLink(_ link: Link) {
        if link.destination == "https://swift.org" {
            count += 1
        }
        descendInto(link)
    }
}

let source = "There are [two](https://swift.org) links to 
   
     here.
    "
   
let document = Document(parsing: source)
print(document.debugDescription())
var linkCounter = LinkCounter()
linkCounter.visit(document)
print(linkCounter.count)
// 2

The second refinement, MarkupRewriter, has an associated Result type of Markup?, so it's meant to change or even remove elements from a markup tree. You can return nil to delete an element, or return another element to substitute in its place.

import Markdown

/// Delete all **strong** elements in a markup tree.
struct StrongDeleter: MarkupRewriter {
    mutating func visitStrong(_ strong: Strong) -> Markup? {
        return nil
    }
}

let source = "Now you see me, **now you don't**"
let document = Document(parsing: source)
var strongDeleter = StrongDeleter()
let newDocument = strongDeleter.visit(document)

print(newDocument!.debugDescription())
// Document
// └─ Paragraph
//    └─ Text "Now you see me, "

Block Directives

Swift Markdown includes a syntax extension for attributed block elements. See Block Directive documentation for more information.

Getting Involved

Submitting a Bug Report

Swift Markdown tracks all bug reports with Swift JIRA. You can use the "SwiftMarkdown" component for issues and feature requests specific to Swift Markdown. When you submit a bug report we ask that you follow the Swift Bug Reporting guidelines and provide as many details as possible.

Submitting a Feature Request

For feature requests, please feel free to create an issue on Swift JIRA with the New Feature type or start a discussion on the Swift Forums.

Don't hesitate to submit a feature request if you see a way Swift Markdown can be improved to better meet your needs.

Contributing to Swift Markdown

Please see the contributing guide for more information.

GitHub

https://github.com/apple/swift-markdown
Comments
  • 1. Bump Swift version

    Summary

    Bump the Swift version Package.swift use to 5.4. And add a new 5.5 Package.swift file

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [x] Ran the ./bin/test script and it succeeded
    Reviewed by Kyle-Ye at 2021-10-17 08:32
  • 2. Fix macOS deprecated API

    Bug/issue #, if applicable:

    Summary

    Fix deprecated FileHandle.readDataToEndOfFile, Process.launch and Process.launchPath API on macOS since they can be removed at a future release.

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [x] Ran the ./bin/test script and it succeeded
    Reviewed by Kyle-Ye at 2021-10-17 08:06
  • 3. Update Aside to parse custom titles

    Bug/issue #, if applicable: rdar://65856948 #59

    Summary

    Adds support for parsing custom aside titles.

    Note: This is a source-breaking change for the Aside.Kind API and removes case-insensitivity support for aside tags.

    Dependencies

    https://github.com/apple/swift-docc/pull/303

    Testing

    See https://github.com/apple/swift-docc/pull/303 for details.

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [X] Added tests
    • [x] Ran the ./bin/test script and it succeeded
    • [X] Updated documentation if necessary
    Reviewed by micpringle at 2022-06-15 09:49
  • 4. Revert "revert" to test toolchain change

    • Revert "Revert "Fix deprecated launch and launchPath API on Process""
    • Revert "Revert "Fix deprecated readDataToEndOfFile API""
    • Revert "Revert "Bump Swift version (#4)" (#11)"

    Bug/issue #, if applicable:

    Summary

    Revert #11 and #12 to test the change in the toolchain build. @franklinsch

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [x] Ran the ./bin/test script and it succeeded
    Reviewed by Kyle-Ye at 2021-11-03 02:46
  • 5. Fix MarkupTest's missing array code convention

    Summary

    Found the missing code convention of the array in the MarkupTest code. So, add it to this.

    Checklist

    • [x] Added tests
    • [x] Ran the ./bin/test script and it succeeded
    Reviewed by GREENOVER at 2021-10-16 23:04
  • 6. Improve performance of `Markup.child(at:)` method while still accurately tracking metadata

    Summary

    While investigating general performance improvements in Swift-DocC for:

    • https://github.com/apple/swift-docc/pull/166

    I found that Swift-DocC was spending a lot of time creating iterators while rewriting markup elements in Swift-Markdown code using MarkupRewriters. This PR is a refactoring of the Markup.child(at:) method to improve performance.

    I put up a PR with a similar change in #36 where @bitjammer pointed out that I was incorrectly tracking metadata for child elements. This is a second iteration on that PR that resolves that issue and adds a test to catch the issue I missed before. It appears to have the same performance win as the original PR when testing with the Swift-DocC performance suite.

    Testing

    I added tests that confirm the metadata returned for child elements via the new child(at:) implementation is the same as the metadata for child elements returned via the existing sequence implementation. Swift-Markdown and Swift-DocC's existing tests continue to pass with this change.

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [x] Added tests
    • [x] Ran the ./bin/test script and it succeeded
    • [x] Updated documentation if necessary
    Reviewed by ethan-kusters at 2022-05-14 17:34
  • 7. Add support for an initial directive argument without a name

    Bug/issue #, if applicable:

    Summary

    This adds support for the first directive argument to be unlabeled / unnamed. This can be useful—avoid duplication in the authoring syntax—when the directive name acts also describes the first argument. For example:

    @DisplayName("A custom name")
    

    instead of

    @DisplayName(name: "A custom name")
    

    Only the first argument is can be unlabeled / unnamed. I feel that this provides most of the authoring syntax benefits without impacting the ability to diagnose directive argument syntax errors.

    Dependencies

    n/a

    Testing

    I will update this with testing instructions when I mark this as non-draft.

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [x] Added tests
    • [x] Ran the ./bin/test script and it succeeded
    • [x] Updated documentation if necessary
    Reviewed by d-ronnqvist at 2022-02-18 00:53
  • 8. Fix/remove argument-parser

    Summary

    Remove the executable "markdown-tool" from the product so that ArgumentParser is not added unnecessarily when used it as a library.

    In my case, I added swift-markdown to my CLI tool that it uses swift-argument-parser v1.0.1 then I got the following error.

    Dependencies could not be resolved because root depends on 'swift-argument-parser' 0.4.4..<0.5.0 and root depends on 'swift-argument-parser' 1.0.0..<1.1.0.
    

    This PR will fix this issue.

    Dependencies

    none.

    Testing

    All tests passed my local environment.

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [x] Added tests
      • I don’t think we need to add tests for this PR.
    • [x] Ran the ./bin/test script and it succeeded
      • added Package*.swift as exceptions
    • [x] Updated documentation if necessary
      • I don’t think we need to add tests for this PR.
    Reviewed by griffin-stewie at 2021-10-17 11:53
  • 9. [Feature] Add Basic DocC Documentation Support

    Summary

    After upgrading the Swift version, we can use DocC to document the Markdown module.

    So this PR add a .docc file in Markdown module, add basic DocC support and move the original ./Documentation/BlockDirectives.md to the DocC Hierarchy

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [x] Ran the ./bin/test script and it succeeded
    • [x] Updated documentation if necessary
    Reviewed by Kyle-Ye at 2021-10-17 09:37
  • 10. Improve efficiency of Aside.Kind.init(rawValue:)

    Bug/issue #, if applicable:

    Close #48

    Summary

    Use String.caseInsensitiveCompare to replace the lowercase compute logic.

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [x] Added tests
    • [x] Ran the ./bin/test script and it succeeded
    • [x] Updated documentation if necessary
    Reviewed by Kyle-Ye at 2022-06-06 06:12
  • 11. Updated link

    Bug/issue #, if applicable: Close #54

    Summary

    Updated bug report link Provide a description of what your PR addresses, explaining the expected user experience. Also, provide an overview of your implementation.

    Dependencies

    Describe any dependencies this PR might have, such as an associated branch in another repository.

    Testing

    Describe how a reviewer can test the functionality of your PR. Provide test content to test with if applicable.

    Steps:

    1. Provide setup instructions.
    2. Explain in detail how the functionality can be tested.

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [ ] Added tests
    • [ ] Ran the ./bin/test script and it succeeded
    • [ ] Updated documentation if necessary
    Reviewed by PrajwalBorkar at 2022-06-06 07:30
  • 12. Link titles not supported?

    Example 481 of the commonmark spec shows the use of a title in a link:

    [link](/uri "title")
    

    however I don't see the title property in the Link documentation

    When I test, it seems as though the title is discarded:

    let document = Document(parsing: "[link](/uri \"title\")")
    print(document.debugDescription())
    

    Outputs:

    Document
    └─ Paragraph
       └─ Link destination: "/uri"
          └─ Text "link"
    

    Title is supported for Images:

    let document2 = Document(parsing: "![foo](/url \"title\")")
    print(document2.debugDescription())
    

    Outputs the title:

    Document
    └─ Paragraph
       └─ Image source: "/url" title: "title"
          └─ Text "foo"
    

    Is this a version thing?

    Thanks, Damian

    Reviewed by DamianMehers at 2022-08-01 18:39
  • 13. Fix single-line directive parsing issue

    Bug/issue #, if applicable:

    Close #64 Close #65

    Summary

    Provide a description of what your PR addresses, explaining the expected user experience. Also, provide an overview of your implementation.

    Dependencies

    Describe any dependencies this PR might have, such as an associated branch in another repository.

    Testing

    Describe how a reviewer can test the functionality of your PR. Provide test content to test with if applicable.

    Steps:

    1. Provide setup instructions.
    2. Explain in detail how the functionality can be tested.

    Checklist

    Make sure you check off the following items. If they cannot be completed, provide a reason.

    • [ ] Added tests
    • [ ] Ran the ./bin/test script and it succeeded
    • [ ] Updated documentation if necessary
    Reviewed by Kyle-Ye at 2022-08-01 12:49
  • 14. DocC drops content have single-line directive

    Description

    When compiling this documentation comment:

    /// Hello
    ///
    /// Some content
    ///
    /// @Comment { This is a comment }
    ///
    /// Hello World
    

    The text after the comment directive incorrectly gets dropped. This doesn't reproduce when writing the directive over multiple lines.

    Checklist

    • [X] If possible, I've reproduced the issue using the main branch of this package.
    • [X] This issue hasn't been addressed in an existing GitHub issue.

    Expected Behavior

    DocC should not drop content after single-line directives.

    Actual behavior

    No response

    Steps To Reproduce

    No response

    Swift-DocC Version Information

    Swift 5.7

    Swift Compiler Version Information

    No response

    Reviewed by franklinsch at 2022-08-01 10:04
  • 15. Including a @Comment in silently breaks symbol's doc rendering

    Description

    Let's say I have a symbol Example.

    In my Sources/ExampleThing/ExampleThing.docc/Example.md i have

    # ``Example``
    
    @Comment { Don't forget X }
    
    ## More things here
    
    Lorem ipsum...
    

    This silently breaks the rendering of the entire page's rendering -- it appears completely empty and no warnings are emitted either.

    I'm guessing @Comment can only be used in a tutorial -- which is a bit meh... the syntax should be the same in al types of files.

    Checklist

    • [ ] If possible, I've reproduced the issue using the main branch of this package.
    • [X] This issue hasn't been addressed in an existing GitHub issue.

    Expected Behavior

    The @Comment should work as expected -- just not be rendered.

    Actual behavior

    The rendering of the entire documentation page for the symbol is empty / broken by the presence of a @Comment

    Steps To Reproduce

    • document any symbol
    • add @Comment { hello }
    • observe the comment prevented content, other than the sub-title from being rendered

    Swift-DocC Version Information

    5.7

    Swift Compiler Version Information

    -> % swift --version
    swift-driver version: 1.62.1 Apple Swift version 5.7 (swiftlang-5.7.0.123.6 clang-1400.0.29.50)
    
    Reviewed by ktoso at 2022-07-25 09:06
  • 16. Question: how to convert Markup string to AttributedString

    I have a simple question. Can you please explain briefly, how to convert a Markup string to AttributedString using this lib? Ideally, with a simple function call. I scrolled the documentation and found that it specialised on DOM model, which is overkill for my case. But probably the function I'm looking for is hidden somewhere.

    Reviewed by kvaDrug at 2022-07-23 12:45
Swift Package Manager plugin which runs ActionBuilder to create a Github Actions workflow for a swift package.

ActionBuilderPlugin A Swift Package Manager command which builds a Github Actions workflow for the current package. By default the workflow file will

Jul 20, 2022
Parsing indeterminate types with Decodable and Either enum using Swift

Decodable + Either Parsing indeterminate types with Decodable and Either enum us

Jan 9, 2022
Tools and helpers to make building apps faster and safer.

The UBFoundation framework provides a set of useful tools and helpers to make building apps faster and safer.

Jul 22, 2022
Framework for easily parsing your JSON data directly to Swift object.
Framework for easily parsing your JSON data directly to Swift object.

Server sends the all JSON data in black and white format i.e. its all strings & we make hard efforts to typecast them into their respective datatypes

Dec 17, 2019
IOS-Bootcamp-Examples - Learn to Swift while building apps - With IOS Development Bootcamp
IOS-Bootcamp-Examples - Learn to Swift while building apps - With IOS Development Bootcamp

IOS-Bootcamp-Examples Learn to Swift while building apps - With IOS Development

Jul 28, 2022
A result builder that allows to define shape building closures
A result builder that allows to define shape building closures

ShapeBuilder A result builder implementation that allows to define shape building closures and variables. Problem In SwiftUI, you can end up in a situ

Aug 1, 2022
A danger-swift plug-in to manage/post danger checking results with markdown style

DangerSwiftShoki A danger-swift plug-in to manage/post danger checking results with markdown style Install DangerSwiftShoki SwiftPM (Recommended) Add

Dec 17, 2021
Generate Markdown documentation from source code
Generate Markdown documentation from source code

SourceDocs SourceDocs is a command line tool that generates markdown documentation files from inline source code comments. Similar to Sphinx or Jazzy,

Jul 27, 2022
A command-line tool and Swift Package for generating class diagrams powered by PlantUML
A command-line tool and Swift Package for generating class diagrams powered by PlantUML

SwiftPlantUML Generate UML class diagrams from swift code with this Command Line Interface (CLI) and Swift Package. Use one or more Swift files as inp

Aug 11, 2022
A Swift package for rapid development using a collection of micro utility extensions for Standard Library, Foundation, and other native frameworks.
A Swift package for rapid development using a collection of micro utility extensions for Standard Library, Foundation, and other native frameworks.

ZamzamKit ZamzamKit is a Swift package for rapid development using a collection of micro utility extensions for Standard Library, Foundation, and othe

Aug 4, 2022
Swift package adding fraction and percentage types.

swift-rationals Rationals is a package containing Fraction and Percentage types for the Swift programming language. Contents The package currently pro

Jul 28, 2022
Units is a Swift package to manipulate, compare, and convert between physical quantities.

Units ?? Units is a Swift package to manipulate, compare, and convert between physical quantities. This package models measurements, which are a numer

Jun 22, 2022
WWDCKit - Creating and Using a Swift Package
 WWDCKit - Creating and Using a Swift Package

WWDCKit - Creating and Using a Swift Package 1. Create the Package in Xcode Navigate to File >> New >> Package. Give the Package a name e.g "WWDCKit".

Jun 17, 2022
Protected is a Swift Package that allows you to specify the read and write rights for any type, depending on context by using Phantom types
Protected is a Swift Package that allows you to specify the read and write rights for any type, depending on context by using Phantom types

Protected is a Swift Package that allows you to specify the read and write rights for any type, depending on context by using Phantom types

Jun 16, 2022
Approximate is a Swift package that provides implementations of floating point comparisons for the Swift ecosystem

Approximate Approximate floating point equality comparisons for the Swift Programming Language. Introduction Approximate is a Swift package that provi

Jun 1, 2022
Swift implementation of the package url spec

PackageURL Swift implementation of the package url specification. Requirements Swift 5.3+ Usage import PackageURL let purl: PackageURL = "pkg:swift/a

Jun 14, 2022
Swift Package for Decoding RSS Feeds.

SyndiKit Swift Package built on top of XMLCoder for Decoding RSS Feeds. Check out the DocC-Built Site! Table of Contents Introduction Features Install

Jul 17, 2022
A simple Swift package for counting the Syllables in a sentence.

A simple Swift package for counting the Syllables in a sentence.

Jan 3, 2022
Azure Maps iOS SDK binary distribution for Swift Package Manager
Azure Maps iOS SDK binary distribution for Swift Package Manager

Azure Maps Control for iOS Installation In your Xcode iOS Project settings, under Project setting’s Package Dependencies, click on + button to add pac

Nov 1, 2021