• Stars
    star
    386
  • Rank 111,213 (Top 3 %)
  • Language
    Swift
  • License
    Other
  • Created over 10 years ago
  • Updated almost 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Markdown processor written in Swift (translation of MarkdownSharp)

(Note: I am no longer actively working on or maintaining Markingbird. If anyone wants to take on the responsibility of ongoing maintenance, please fork this repo and let me know, and I'll direct people to your fork.)

Markingbird: A Markdown Processor in Swift

This library provides a Markdown processor written in Swift for OS X and iOS. It is a translation/port of the MarkdownSharp processor used by Stack Overflow.

The port currently passes all of the test cases in MarkdownSharp's SimpleTests, ConfigTest, and MDTestTests test suites. However, it has not been extensively tested or used in production applications. If you find issues, please submit bug reports and fixes.

How To Use It

The Xcode project packages the library as a Cocoa framework. However, as all the code is in the file Markingbird/Markdown.swift, the easiest way to use it is to simply copy that file into your own projects.

Typically, an app obtains some Markdown-formatted text somehow (read from a file, entered by a user, etc.), creates a Markdown object, and then calls its transform(String) -> String method to generate HTML.

    // If using Markingbird framework, import the module.
    // (If Markdown.swift is in your target, this is unneeded.)
    import Markingbird

	let inputText: String = getMarkdownFormatTextSomehow()

    // Note: must use `var` rather than `let` because the
    // Markdown struct mutates itself during processing
    var markdown = Markdown()
    let outputHtml: String = markdown.transform(inputText)

    // Use MarkdownOptions to enable non-default features
    var options = MarkdownOptions()
    options.autoHyperlink = true
    options.autoNewlines = true
    options.emptyElementSuffix = ">"
    options.encodeProblemUrlCharacters = true
    options.linkEmails = false
    options.strictBoldItalic = true
    var fancyMarkdown = Markdown(options: options)
	let fancyOutput = fancyMarkdown.transform(inputText)

A single Markdown instance can be used multiple times. However, it is not safe to use the Markdown class or its instances simultaneously from multiple threads, due to unsynchronized use of shared static data.

To-Do

  • Eliminate all uses of ! to force-unwrap Optionals (use safer if let, ?? or pattern-matching instead)
  • Re-examine the ways that characters and substrings are processed (the current implementation is a mish-mash of String and NSString bridging)
  • Eliminate mutable class-level state and add whatever synchronization is necessary to make it safe to use Markingbird instances in different threads.
  • Create sample apps for OS X and iOS.

Implementation Notes

For the most part, the code is a straightforward line-by-line translation of the C# code into Swift. Nothing has been done to make the code more "Swift-like" (whatever that means) or to improve the design. The C# code is itself a translation of Perl and PHP code, and the result is a bit of a mess. It is not a good example of Swift code, nor a good example of how to process Markdown. (It's really not a good example of anything.)

Aside from changes necessary for compatibility with Swift syntax and semantics, these are the only stylistic changes made during the translation:

  • Lowercase identifiers for properties and methods
  • Indentation and brace style matching Xcode's defaults and examples in The Swift Programming Language guide
  • Keyword arguments
  • Use of let instead of var where possible
  • Minimal use of Optional types (only used where the C# code explicitly uses or checks for null)
  • Use of Swift string interpolation where the C# code uses String.Format()

When the Swift language and its standard library stabilize, it might make sense to reimplement this library to take advantage of advanced Swift features, but for now a simple translation of a mature implementation in a similar programming language is desirable as it is unlikely to break as Swift evolves.

A Markdown processor is implemented as a struct. It might be preferable to implement it as a class, because it mutates itself during processing. However, the C# implementation uses a lot of class-level data members, and Swift does not yet support class variables in classes. Swift does support static members in structs, so it made sense to use them. Whenever Swift does support class variables, the struct vs. class decision should be revisited.

To ease translation from C# to Swift, the private types MarkdownRegex, MarkdownRegexOptions, and MarkdownRegexMatch wrap Cocoa's NSRegularExpression class with interfaces similar to those of .NET's Regex, RegexOptions, and Match types.

The implementation uses many complex regular expressions. The C# version declares these using verbatim string literals that span multiple lines and eliminate the need to escape special characters. Unfortunately, Swift does not allow string literals to span multiple lines, and requires escaping of all special characters. To translate the multi-line regular expression strings to Swift in a faithful and readable way, the String.join method is used to concatenate an array of lines copied from the original C# source. For example:

	// Original C# source
    string attr = @"
    (?>				    # optional tag attributes
      \s			    # starts with whitespace
      (?>
        [^>""/]+	    # text outside quotes
      |
        /+(?!>)		    # slash not followed by >
      |
        ""[^""]*""		# text inside double quotes (tolerate >)
      |
        '[^']*'	        # text inside single quotes (tolerate >)
      )*
    )?
	";
	// Translated to Swift
    let attr = "\n".join([
        "(?>            # optional tag attributes",
        "  \\s          # starts with whitespace",
        "  (?>",
        "    [^>\"/]+   # text outside quotes",
        "  |",
        "    /+(?!>)    # slash not followed by >",
        "  |",
        "    \"[^\"]*\" # text inside double quotes (tolerate >)",
        "  |",
        "    '[^']*'    # text inside single quotes (tolerate >)",
        "  )*",
        ")?"
        ])

The following transformations were made to the C# verbatim string literals to make them legal Swift literals and to conform to NSRegularExpression's regular expression syntax:

  • Convert each \ to \\
  • Convert each "" to \"
  • Convert each [ ] (a set containing a single space) to \\p{Z}
  • In expressions that used String.Format, convert each {{ to { and each }} to }

More Repositories

1

MenubarCountdown

Menubar countdown timer for macOS
Swift
168
star
2

cxxforth

A simple public-domain Forth implementation in C++
C++
56
star
3

KJSimpleBinding

Simple data bindings for iOS
Objective-C
50
star
4

suwaneeforth

A Forth interpreter implemented in Swift, based upon JONESFORTH
Swift
39
star
5

tilessample

Core Animation example code that emulates the iPhone home screen re-organization UI.
Objective-C
33
star
6

gradientbuttons

Shiny iOS Buttons Without Photoshop
Objective-C
26
star
7

KJYield

"yield" for Swift, inspired by Python and F#
Swift
23
star
8

KJGridLayout

Grid-based view layout manager for iOS
Objective-C
18
star
9

bitsybasic

Tiny BASIC interpreter written in Swift
Swift
12
star
10

KJMachPortServer

Simple example of using NSMachPort for interprocess communication
Objective-C
12
star
11

KJTipCalculator

Simple iOS tip calculator app, written in Swift
Swift
11
star
12

wiringPi_gforth

Gforth interface to the wiringPi GPIO interface library for Raspberry Pi
Forth
11
star
13

advent

Original PDP-10 Fortran source code for Will Crowther's Colossal Cave Adventure game
Fortran
10
star
14

jonesforth

Mirror of git://git.annexia.org/git/jonesforth.git
Assembly
8
star
15

kjchess

Chess engine implemented in Swift
Swift
8
star
16

SublimeTaskPaper

Sublime Text 2 syntax support for TaskPaper-format files
7
star
17

tscp-rust

Port of Tom Kerrigan's Simple Chess Program (TSCP) to Rust
Rust
7
star
18

MonochromeSublimeText

Monochrome color schemes for Sublime Text
6
star
19

KJMessagePortTestServer

Simple example of using the CFMessagePort API from Swift
Swift
6
star
20

KJMenuTableViewController

Simplified creation of UITableViewController-based menus
Objective-C
6
star
21

PokerSwiftUI

Jacks-or-Better video poker game implemented using SwiftUI
Swift
5
star
22

kjsp

Kris Johnson's Sonic Pi Collection
Ruby
4
star
23

lunar-c

Port of classic text-based lunar lander game to C
C
3
star
24

StackMachine

Forth-inspired language interpreter, written in Swift
Swift
3
star
25

perspectivetest

Simple iPhone app demonstrating how to create a spinning picture with an underlying reflection.
Objective-C
3
star
26

lunar-fortran

Port of classic Lunar Lander game to Fortran
Fortran
3
star
27

KJTipCalculatorOSX

Sample app for experimenting with bindings and other Cocoa features
Swift
2
star
28

resume

Kristopher Johnson's Resumé
HTML
2
star
29

MonochromeXcode

Monochrome color themes for Xcode
2
star
30

KJLunarLander

iOS Lunar Lander game, implemented with SpriteKit
Swift
2
star
31

Steady

SwiftUI metronome app for iOS and macOS
Swift
2
star
32

pforth-xcode

Fork of pForth (https://code.google.com/p/pforth/) with Xcode 6-compatible Makefile and project files
C
1
star
33

KDJSwiftStdIO

Swift wrappers for C stdio
Swift
1
star
34

alpine-pfe

Docker container with Portable Forth Environment (PFE) on an Alpine image
Makefile
1
star
35

swiftcat

Re-implementation of cat(1) in Swift
Swift
1
star
36

HideStackOverflowDeletedAnswers

Safari extension that hides all deleted answers on stackoverflow.com
CSS
1
star
37

kristopherjohnson.github.io

Personal GitHub Pages site for Kris Johnson
HTML
1
star
38

KDJEmptySplitViewApp

iOS app that just displays an empty split view, useful for screenshots for launch images, documentation, and/or design
Objective-C
1
star
39

trek73c

C/Curses version of Trek73
C
1
star
40

alpine-gforth

Docker container with Gforth on an Alpine image
Makefile
1
star
41

ASCIIRef

SwiftUI app that lists ASCII codes
Swift
1
star
42

DahlonegaWeatherSafariExtension

When visiting weather.gov, automatically pre-fill "city" field with Dahlonega's zip code (30533)
JavaScript
1
star
43

KJFacebookSafariExtension

Safari extension that customizes Facebook for me
JavaScript
1
star
44

music-python

Music experiments with Python
Jupyter Notebook
1
star
45

Breaths

Apple Watch mindfulness app
Swift
1
star
46

TWikiRawEditSafariExtension

Change behavior of TWiki "Edit" button such that it always does raw edit rather than WYSIWYG edit
JavaScript
1
star