├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ └── swiftlint.yml
├── .gitignore
├── .swiftlint.yml
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── .travis.yml
├── CHANGELOG.md
├── Down-Example
├── Down-Example.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── Down-Example.xcscheme
│ │ └── macOS Demo.xcscheme
├── Down-Example
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ └── ViewController.swift
├── Shared
│ └── README-sample.md
└── macOS Demo
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── Main.storyboard
│ ├── Info.plist
│ ├── ViewController.swift
│ └── macOS Demo.entitlements
├── Down.podspec
├── Down.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── swiftpm
│ │ └── Package.resolved
└── xcshareddata
│ └── xcschemes
│ └── Down.xcscheme
├── Images
└── ohhai.gif
├── LICENSE
├── LinuxMain.swift
├── Package.swift
├── README.md
├── Sources
├── Down
│ ├── AST
│ │ ├── Nodes
│ │ │ ├── BaseNode.swift
│ │ │ ├── BlockQuote.swift
│ │ │ ├── ChildSequence.swift
│ │ │ ├── Code.swift
│ │ │ ├── CodeBlock.swift
│ │ │ ├── CustomBlock.swift
│ │ │ ├── CustomInline.swift
│ │ │ ├── Document.swift
│ │ │ ├── Emphasis.swift
│ │ │ ├── Heading.swift
│ │ │ ├── HtmlBlock.swift
│ │ │ ├── HtmlInline.swift
│ │ │ ├── Image.swift
│ │ │ ├── Item.swift
│ │ │ ├── LineBreak.swift
│ │ │ ├── Link.swift
│ │ │ ├── List.swift
│ │ │ ├── Node.swift
│ │ │ ├── Paragraph.swift
│ │ │ ├── SoftBreak.swift
│ │ │ ├── Strong.swift
│ │ │ ├── Text.swift
│ │ │ └── ThematicBreak.swift
│ │ ├── Styling
│ │ │ ├── Attribute Collections
│ │ │ │ ├── ColorCollection.swift
│ │ │ │ ├── FontCollection.swift
│ │ │ │ └── ParagraphStyleCollection.swift
│ │ │ ├── Custom Attributes
│ │ │ │ ├── BlockBackgroundColorAttribute.swift
│ │ │ │ ├── QuoteStripeAttribute.swift
│ │ │ │ └── ThematicBreakAttribute.swift
│ │ │ ├── Helpers
│ │ │ │ ├── Extensions
│ │ │ │ │ ├── CGPoint+Translate.swift
│ │ │ │ │ ├── CGRect+Helpers.swift
│ │ │ │ │ ├── NSAttributedString+Helpers.swift
│ │ │ │ │ ├── NSMutableAttributedString+Attributes.swift
│ │ │ │ │ └── UIFont+Traits.swift
│ │ │ │ └── ListItemParagraphStyler.swift
│ │ │ ├── Layout Managers
│ │ │ │ ├── DownDebugLayoutManager.swift
│ │ │ │ └── DownLayoutManager.swift
│ │ │ ├── Options
│ │ │ │ ├── CodeBlockOptions.swift
│ │ │ │ ├── ListItemOptions.swift
│ │ │ │ ├── QuoteStripeOptions.swift
│ │ │ │ └── ThematicBreakOptions.swift
│ │ │ ├── Stylers
│ │ │ │ ├── DownStyler.swift
│ │ │ │ ├── DownStylerConfiguration.swift
│ │ │ │ └── Styler.swift
│ │ │ └── Text Views
│ │ │ │ ├── DownDebugTextView.swift
│ │ │ │ └── DownTextView.swift
│ │ └── Visitors
│ │ │ ├── AttributedStringVisitor.swift
│ │ │ ├── DebugVisitor.swift
│ │ │ ├── ListItemPrefixGenerator.swift
│ │ │ └── Visitor.swift
│ ├── Down.h
│ ├── Down.swift
│ ├── Enums & Options
│ │ ├── DownErrors.swift
│ │ └── DownOptions.swift
│ ├── Extensions
│ │ ├── NSAttributedString+HTML.swift
│ │ └── String+ToHTML.swift
│ ├── Renderers
│ │ ├── DownASTRenderable.swift
│ │ ├── DownAttributedStringRenderable.swift
│ │ ├── DownCommonMarkRenderable.swift
│ │ ├── DownGroffRenderable.swift
│ │ ├── DownHTMLRenderable.swift
│ │ ├── DownLaTeXRenderable.swift
│ │ ├── DownRenderable.swift
│ │ └── DownXMLRenderable.swift
│ ├── Resources
│ │ ├── DownView (macOS).bundle
│ │ │ ├── css
│ │ │ │ └── down.min.css
│ │ │ ├── index.html
│ │ │ └── js
│ │ │ │ ├── down.js
│ │ │ │ └── highlight.min.js
│ │ └── DownView.bundle
│ │ │ ├── css
│ │ │ └── down.min.css
│ │ │ ├── index.html
│ │ │ └── js
│ │ │ ├── down.js
│ │ │ └── highlight.min.js
│ └── Views
│ │ ├── BundleHelper.swift
│ │ └── DownView.swift
└── cmark
│ ├── COPYING
│ ├── blocks.c
│ ├── buffer.c
│ ├── buffer.h
│ ├── case_fold_switch.inc
│ ├── chunk.h
│ ├── cmark.c
│ ├── cmark.h
│ ├── cmark_ctype.c
│ ├── cmark_ctype.h
│ ├── cmark_export.h
│ ├── cmark_version.h
│ ├── commonmark.c
│ ├── config.h
│ ├── entities.inc
│ ├── houdini.h
│ ├── houdini_href_e.c
│ ├── houdini_html_e.c
│ ├── houdini_html_u.c
│ ├── html.c
│ ├── include
│ └── module.modulemap
│ ├── inlines.c
│ ├── inlines.h
│ ├── iterator.c
│ ├── iterator.h
│ ├── latex.c
│ ├── man.c
│ ├── node.c
│ ├── node.h
│ ├── parser.h
│ ├── references.c
│ ├── references.h
│ ├── render.c
│ ├── render.h
│ ├── scanners.c
│ ├── scanners.h
│ ├── utf8.c
│ ├── utf8.h
│ └── xml.c
├── Supporting Files
├── Configurations
│ ├── Deployment-Targets.xcconfig
│ ├── Universal-Framework-Target.xcconfig
│ └── Universal-Target-Base.xcconfig
├── Down-Info.plist
└── DownTests-Info.plist
├── Tests
└── DownTests
│ ├── AST
│ ├── ListItemPrefixGeneratorTests.swift
│ ├── NodeTests.swift
│ ├── VisitorTests.swift
│ └── __Snapshots__
│ │ └── VisitorTests
│ │ ├── testAttributedStringVisitor.1.txt
│ │ ├── testBlockQuote.1.txt
│ │ ├── testBlockQuote.2.txt
│ │ ├── testCodeBlock.1.txt
│ │ ├── testCodeBlock.2.txt
│ │ ├── testHeading.1.txt
│ │ ├── testHeading.2.txt
│ │ ├── testHtmlBlock.1.txt
│ │ ├── testHtmlBlock.2.txt
│ │ ├── testInline.1.txt
│ │ ├── testInline.2.txt
│ │ ├── testLineBreak.1.txt
│ │ ├── testLineBreak.2.txt
│ │ ├── testLink.1.txt
│ │ ├── testLink.2.txt
│ │ ├── testList.1.txt
│ │ ├── testList.2.txt
│ │ ├── testParagraph.1.txt
│ │ ├── testParagraph.2.txt
│ │ ├── testSimpleMarkdown.1.txt
│ │ ├── testSoftBreak.1.txt
│ │ ├── testSoftBreak.2.txt
│ │ ├── testThematicBreak.1.txt
│ │ └── testThematicBreak.2.txt
│ ├── BindingTests.swift
│ ├── DownViewTests.swift
│ ├── Fixtures
│ └── TestDownView.bundle
│ │ ├── css
│ │ └── down.min.css
│ │ ├── index.html
│ │ └── js
│ │ ├── down.js
│ │ └── highlight.min.js
│ ├── NSAttributedStringTests.swift
│ ├── StringTests.swift
│ ├── Styler
│ ├── BlockQuoteStyleTests.swift
│ ├── CodeBlockStyleTests.swift
│ ├── DownDebugLayoutManagerTests.swift
│ ├── HeadingStyleTests.swift
│ ├── Helpers
│ │ ├── CGPointTranslateTests.swift
│ │ ├── CGRectHelpersTests.swift
│ │ ├── NSAttributedString+HelpersTests.swift
│ │ └── NSMutableAttributedString+AttributesTests.swift
│ ├── InlineStyleTests.swift
│ ├── LinkStyleTests.swift
│ ├── ListItemStyleTests.swift
│ ├── StylerTestSuite.swift
│ ├── ThematicBreakSyleTests.swift
│ └── __Snapshots__
│ │ ├── BlockQuoteStyleTests
│ │ ├── testThat_NestedQuotes_Have_TheirOwnStripes.1.png
│ │ ├── testThat_QuoteAlignment_Obeys_TextContainerOffset.1.png
│ │ ├── testThat_QuoteContent_Aligns.1.png
│ │ ├── testThat_QuoteContent_Preserves_BlockElements.1.png
│ │ ├── testThat_QuoteContent_Preserves_InlineElements.1.png
│ │ ├── testThat_QuoteContent_Preserves_ListFormatting.1.png
│ │ ├── testThat_QuoteContent_Preserves_ThematicBreak.1.png
│ │ ├── testThat_QuoteStripe_AlignsTo_Margin.1.png
│ │ ├── testThat_QuotedList_WithinA_ListItem_AlignsCorrectly.1.png
│ │ └── testThat_Quotes_WithinA_ListItem_AlignsTo_ListItemContent.1.png
│ │ ├── CodeBlockStyleTests
│ │ ├── testThat_CodeBlock_IsStyled.1.png
│ │ └── testThat_HtmlBlock_IsStyled.1.png
│ │ ├── DownDebugLayoutManagerTests
│ │ └── testThat_LineFragments_AreDrawn.1.png
│ │ ├── HeadingStyleTests
│ │ ├── testThat_HeadingStyle_Preserves_StrongEmphasisAndMonospaceTraits.1.png
│ │ ├── testThat_Heading_LevelOne_IsStyled.1.png
│ │ ├── testThat_Heading_LevelThree_IsStyled.1.png
│ │ ├── testThat_Heading_LevelTwo_IsStyled.1.png
│ │ └── testThat_Heading_LevelsThreeToSix_AreStyledEqually.1.png
│ │ ├── InlineStyleTests
│ │ ├── testThat_CodeText_IsStyled.1.png
│ │ ├── testThat_EmphasizedCode_IsStyled.1.png
│ │ ├── testThat_EmphasizedStrongCode_IsStyled.1.png
│ │ ├── testThat_EmphasizedStrongText_IsStyled.1.png
│ │ ├── testThat_EmphasizedText_IsStyled.1.png
│ │ ├── testThat_StrongCode_IsStyled.1.png
│ │ ├── testThat_StrongEmphasizedCode_IsStyled.1.png
│ │ ├── testThat_StrongEmphasizedText_IsStyled.1.png
│ │ └── testThat_StrongText_IsStyled.1.png
│ │ ├── LinkStyleTests
│ │ ├── testThat_Link_IsStyled.1.png
│ │ └── testThat_Link_Preserves_InlineStyles.1.png
│ │ ├── ListItemStyleTests
│ │ ├── testThat_DigitAndBulletPrefixes_Align.1.png
│ │ ├── testThat_DigitPrefixes_ExceedingMaxPrefixLength_DontPush_WrappedLines.1.png
│ │ ├── testThat_DigitPrefixes_ExceedingMaxPrefixLength_Push_FirstLine.1.png
│ │ ├── testThat_DigitPrefixes_UpToMaxPrefixLength_Align.1.png
│ │ ├── testThat_FirstParagraph_WithLineBreaks_AlignTo_FirstLine.1.png
│ │ ├── testThat_FirstParagraph_WrappedLines_AlignTo_FirstLine.1.png
│ │ ├── testThat_ListItems_Preseve_InlineElements.1.png
│ │ ├── testThat_NestedList_AlignsTo_OuterList.1.png
│ │ ├── testThat_NestedList_InMiddleParagraph_AlignsTo_OuterList.1.png
│ │ ├── testThat_NestedList_InTrailingParagraph_AlignsTo_OuterList.1.png
│ │ ├── testThat_NestedList_With_MultipleParagraphs_Align.1.png
│ │ ├── testThat_NestedLists_AlignTo_ParentLists.1.png
│ │ ├── testThat_TrailingParagraphs_FirstLines_AlignTo_FirstParagraph.1.png
│ │ └── testThat_TrailingParagraphs_WrappedLines_AlignTo_FirstLines.1.png
│ │ └── ThematicBreakSyleTests
│ │ ├── testThat_ThematicBreak_CanBe_Indented.1.png
│ │ ├── testThat_ThematicBreak_InOffsetTextContainer_IsStyled.1.png
│ │ └── testThat_ThematicBreak_IsStyled.1.png
│ └── __Snapshots__
│ └── BindingTests
│ ├── testCommonMarkBindngsWork.1.txt
│ ├── testGroffBindingsWork.1.txt
│ ├── testHTMLBindingsWork.1.txt
│ ├── testLaTeXBindngsWork.1.txt
│ └── testXMLBindingsWork.1.txt
├── codecov.yml
└── docker
├── Dockerfile
├── README.md
├── docker-compose.yml
└── down-rebuild.sh
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Help isolate and fix bugs
4 |
5 | ---
6 |
7 | Please help prevent duplicate issues before submitting a new one:
8 |
9 | * [ ] I've searched other open/closed issues for duplicates before opening up this new issue.
10 |
11 | # Report
12 |
13 | ## What did you do?
14 |
15 | ℹ Please replace this with what you did.
16 |
17 | ## What did you expect to happen?
18 |
19 | ℹ Please replace this with what you expected to happen.
20 |
21 | ## What happened instead?
22 |
23 | ℹ Please replace this with what happened instead (e.g. the issue).
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an enhancement for this project
4 |
5 | ---
6 |
7 | Please help prevent duplicate requests before submitting a new one:
8 |
9 | * [ ] I've searched other open/closed issues for duplicates before opening up this new issue.
10 |
11 | # Feature Request
12 |
13 | ## Is your feature request related to a problem? Please describe.
14 |
15 | ℹ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
16 |
17 | ## Describe the solution you'd like
18 |
19 | ℹ A clear and concise description of what you want to happen.
20 |
21 | ## Describe alternatives you've considered
22 |
23 | ℹ A clear and concise description of any alternative solutions or features you've considered.
24 |
--------------------------------------------------------------------------------
/.github/workflows/swiftlint.yml:
--------------------------------------------------------------------------------
1 | name: SwiftLint
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - '.github/workflows/swiftlint.yml'
7 | - '.swiftlint.yml'
8 | - '**/*.swift'
9 |
10 | jobs:
11 | SwiftLint:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v1
15 | - name: GitHub Action for SwiftLint
16 | uses: norio-nomura/action-swiftlint@3.2.1
17 |
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Pods/
2 | .build/
3 | .DS_Store
4 | xcuserdata
5 |
6 | # additional ignores that will save us time
7 | *.pbxuser
8 | !default.pbxuser
9 | *.mode1v3
10 | !default.mode1v3
11 | *.mode2v3
12 | !default.mode2v3
13 | *.perspectivev3
14 | !default.perspectivev3
15 | xcuserdata
16 | *.xccheckout
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 | *.xcuserstate
22 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | included:
2 | - Sources/Down
3 | - Tests
4 |
5 | large_tuple:
6 | warning: 3
7 | error: 4
8 |
9 | cyclomatic_complexity:
10 | ignores_case_statements: true
11 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | osx_image: xcode12.2
2 | language: objective-c
3 | before_install:
4 | - set -o pipefail
5 | - xcrun simctl boot "iPhone 12" || echo "(Pre)Launched the simulator."
6 | script:
7 | - travis_retry xcodebuild -project Down.xcodeproj -scheme "Down" -sdk iphonesimulator -destination "platform=iOS Simulator,OS=14.2,name=iPhone 12" -enableCodeCoverage YES ONLY_ACTIVE_ARCH=YES -quiet test
8 | - bash <(curl -s https://codecov.io/bash)
9 | - travis_retry xcodebuild -project Down.xcodeproj -scheme "Down" -sdk macosx -destination 'platform=OS X,arch=x86_64' -enableCodeCoverage YES -quiet test
10 | - bash <(curl -s https://codecov.io/bash)
11 | - travis_retry xcodebuild -project Down.xcodeproj -scheme "Down" -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV' -enableCodeCoverage YES -quiet test
12 | - bash <(curl -s https://codecov.io/bash)
13 |
--------------------------------------------------------------------------------
/Down-Example/Down-Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Down-Example/Down-Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Down-Example/Down-Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "SnapshotTesting",
6 | "repositoryURL": "https://github.com/pointfreeco/swift-snapshot-testing.git",
7 | "state": {
8 | "branch": null,
9 | "revision": "c466812aa2e22898f27557e2e780d3aad7a27203",
10 | "version": "1.8.2"
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/Down-Example/Down-Example.xcodeproj/xcshareddata/xcschemes/Down-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/Down-Example/Down-Example.xcodeproj/xcshareddata/xcschemes/macOS Demo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/Down-Example/Down-Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Down-Example
4 | //
5 | // Created by Keaton Burleson on 7/1/17.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 | var window: UIWindow?
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/Down-Example/Down-Example/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/Down-Example/Down-Example/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Down-Example/Down-Example/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Down-Example/Down-Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UIStatusBarHidden
32 |
33 | UIStatusBarStyle
34 | UIStatusBarStyleDefault
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Down-Example/Down-Example/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Down-Example
4 | //
5 | // Created by Keaton Burleson on 7/1/17.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Down
11 |
12 | final class ViewController: UIViewController {
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 |
17 | renderDownInWebView()
18 | }
19 |
20 | }
21 |
22 | private extension ViewController {
23 |
24 | func renderDownInWebView() {
25 | guard let readMeURL = Bundle.main.url(forResource: nil, withExtension: "md"),
26 | let readMeContents = try? String(contentsOf: readMeURL)
27 | else {
28 | showError(message: "Could not load readme contents.")
29 | return
30 | }
31 |
32 | do {
33 | let downView = try DownView(frame: view.bounds, markdownString: readMeContents, didLoadSuccessfully: {
34 | print("Markdown was rendered.")
35 | })
36 | downView.translatesAutoresizingMaskIntoConstraints = false
37 | view.addSubview(downView)
38 | constrain(subview: downView)
39 | createStatusBarBackgrounds(above: downView)
40 | } catch {
41 | showError(message: error.localizedDescription)
42 | }
43 | }
44 |
45 | func createStatusBarBackgrounds(above subview: UIView) {
46 | let blurEffect = UIBlurEffect(style: .prominent)
47 | let blurEffectView = UIVisualEffectView(effect: blurEffect)
48 | blurEffectView.translatesAutoresizingMaskIntoConstraints = false
49 | view.insertSubview(blurEffectView, aboveSubview: subview)
50 | constrain(subview: blurEffectView, bottomAnchor: topLayoutGuide.bottomAnchor)
51 | }
52 |
53 | func constrain(subview: UIView, bottomAnchor: NSLayoutYAxisAnchor? = nil) {
54 | NSLayoutConstraint.activate([
55 | subview.leadingAnchor.constraint(equalTo: view.leadingAnchor),
56 | subview.trailingAnchor.constraint(equalTo: view.trailingAnchor),
57 | subview.topAnchor.constraint(equalTo: topLayoutGuide.topAnchor),
58 | subview.bottomAnchor.constraint(equalTo: bottomAnchor ?? bottomLayoutGuide.bottomAnchor)
59 | ])
60 | }
61 |
62 | func showError(message: String) {
63 | let alertController = UIAlertController(title: "DownView Render Error",
64 | message: message,
65 | preferredStyle: .alert)
66 | self.present(alertController, animated: true, completion: nil)
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/Down-Example/macOS Demo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // macOS Demo
4 | //
5 | // Created by Chris Zielinski on 10/27/18.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {}
13 |
14 |
--------------------------------------------------------------------------------
/Down-Example/macOS Demo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Down-Example/macOS Demo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Down-Example/macOS Demo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2016-2019 Down. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Down-Example/macOS Demo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // macOS Demo
4 | //
5 | // Created by Chris Zielinski on 10/27/18.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import Down
11 |
12 | final class ViewController: NSViewController {
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 |
17 | renderDownInWebView()
18 | }
19 |
20 | }
21 |
22 | private extension ViewController {
23 |
24 | func renderDownInWebView() {
25 | let readMeURL = Bundle.main.url(forResource: nil, withExtension: "md")!
26 | let readMeContents = try! String(contentsOf: readMeURL)
27 |
28 | do {
29 | let downView = try DownView(frame: view.bounds, markdownString: readMeContents, didLoadSuccessfully: {
30 | print("Markdown was rendered.")
31 | })
32 | downView.autoresizingMask = [.width, .height]
33 | view.addSubview(downView, positioned: .below, relativeTo: nil)
34 | } catch {
35 | NSApp.presentError(error)
36 | }
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/Down-Example/macOS Demo/macOS Demo.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 | com.apple.security.network.client
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Down.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = "Down"
3 | spec.summary = "Blazing fast Markdown rendering in Swift, built upon cmark."
4 | spec.version = "0.11.0"
5 | spec.homepage = "https://github.com/johnxnguyen/Down"
6 | spec.license = { :type => "MIT", :file => "LICENSE" }
7 | spec.authors = { "John Nguyen" => "polyxo@protonmail.com" }
8 | spec.source = { :git => "https://github.com/johnxnguyen/Down.git", :tag => "v" + spec.version.to_s }
9 | spec.source_files = "Sources/Down/{AST,Enums & Options,Extensions,Renderers}/**/*.swift", "Sources/cmark/*.{h,c}", "Sources/Down/*"
10 | spec.ios.source_files = "Sources/Down/Views/**"
11 | spec.osx.source_files = "Sources/Down/Views/**"
12 | spec.public_header_files = "Sources/Down/*.h"
13 | spec.ios.deployment_target = "9.0"
14 | spec.tvos.deployment_target = "9.0"
15 | spec.osx.deployment_target = "10.11"
16 | spec.requires_arc = true
17 | spec.module_name = "Down"
18 | spec.preserve_paths = "Sources/cmark/include/module.modulemap", "Sources/cmark/*.inc", "Sources/cmark/COPYING"
19 | spec.pod_target_xcconfig = { 'SWIFT_INCLUDE_PATHS' => '$(SRCROOT)/Down/Sources/cmark/**' }
20 | spec.ios.resource = 'Sources/Down/Resources/DownView.bundle'
21 | spec.osx.resource = 'Sources/Down/Resources/DownView.bundle'
22 | spec.swift_versions = ['5.0', '5.1']
23 | end
24 |
--------------------------------------------------------------------------------
/Down.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Down.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Down.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Down.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "SnapshotTesting",
6 | "repositoryURL": "https://github.com/pointfreeco/swift-snapshot-testing.git",
7 | "state": {
8 | "branch": null,
9 | "revision": "c466812aa2e22898f27557e2e780d3aad7a27203",
10 | "version": "1.8.2"
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/Down.xcodeproj/xcshareddata/xcschemes/Down.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
38 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
64 |
70 |
71 |
72 |
73 |
79 |
80 |
86 |
87 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/Images/ohhai.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Images/ohhai.gif
--------------------------------------------------------------------------------
/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | var tests = [XCTestCaseEntry]()
4 |
5 | // FIXME: Run on macOS `swift test --generate-linuxmain` and maintain Linux Tests.
6 |
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "Down",
7 | platforms: [
8 | .macOS("10.11"),
9 | .iOS("9.0"),
10 | .tvOS("9.0")
11 | ],
12 | products: [
13 | .library(
14 | name: "Down",
15 | targets: ["Down"]
16 | )
17 | ],
18 | targets: [
19 | .target(
20 | name: "libcmark",
21 | dependencies: [],
22 | path: "Sources/cmark",
23 | exclude: [
24 | "include",
25 | "case_fold_switch.inc",
26 | "entities.inc",
27 | "COPYING"
28 | ],
29 | publicHeadersPath: "./"
30 | ),
31 | .target(
32 | name: "Down",
33 | dependencies: ["libcmark"],
34 | path: "Sources/Down",
35 | exclude: ["Down.h"],
36 | resources: [
37 | .copy("Resources/DownView.bundle"),
38 | .copy("Resources/DownView (macOS).bundle"),
39 | ]
40 | ),
41 | .testTarget(
42 | name: "DownTests",
43 | dependencies: ["Down"],
44 | path: "Tests/DownTests",
45 | exclude: [
46 | "AST/VisitorTests.swift",
47 | "AST/__Snapshots__",
48 | "DownViewTests.swift",
49 | "Fixtures",
50 | "Styler/__Snapshots__",
51 | "Styler/BlockQuoteStyleTests.swift",
52 | "Styler/CodeBlockStyleTests.swift",
53 | "Styler/DownDebugLayoutManagerTests.swift",
54 | "Styler/HeadingStyleTests.swift",
55 | "Styler/LinkStyleTests.swift",
56 | "Styler/InlineStyleTests.swift",
57 | "Styler/ListItemStyleTests.swift",
58 | "Styler/StylerTestSuite.swift",
59 | "Styler/ThematicBreakSyleTests.swift"
60 | ]
61 | )
62 | ],
63 | swiftLanguageVersions: [.v5]
64 | )
65 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/BaseNode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseNode.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 21.04.19.
6 | //
7 | //
8 |
9 | import Foundation
10 | import libcmark
11 |
12 | public class BaseNode: Node {
13 |
14 | // MARK: - Properties
15 |
16 | public let cmarkNode: CMarkNode
17 |
18 | public private(set) lazy var children: [Node] = Array(childSequence)
19 |
20 | public private(set) lazy var nestDepth: Int = {
21 | var depth = 0
22 | var next = cmarkNode.parent
23 |
24 | while let current = next {
25 | depth += current.type == cmarkNode.type ? 1 : 0
26 | next = current.parent
27 | }
28 | return depth
29 | }()
30 |
31 | // MARK: - Life cycle
32 |
33 | init(cmarkNode: CMarkNode) {
34 | self.cmarkNode = cmarkNode
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/BlockQuote.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BlockQuote.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class BlockQuote: BaseNode {}
12 |
13 | // MARK: - Debug
14 |
15 | extension BlockQuote: CustomDebugStringConvertible {
16 |
17 | public var debugDescription: String {
18 | return "Block Quote"
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/ChildSequence.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChildSequence.swift
3 | // Down
4 | //
5 | // Created by Sven Weidauer on 05.10.2020
6 | //
7 |
8 | import libcmark
9 |
10 | /// Sequence of child nodes.
11 |
12 | public struct ChildSequence: Sequence {
13 |
14 | // MARK: - Properties
15 |
16 | let node: CMarkNode
17 |
18 | // MARK: - Methods
19 |
20 | public func makeIterator() -> Iterator {
21 | return Iterator(node: cmark_node_first_child(node))
22 | }
23 |
24 | }
25 |
26 | // MARK: - Iterator
27 |
28 | public extension ChildSequence {
29 |
30 | struct Iterator: IteratorProtocol {
31 |
32 | // MARK: - Properties
33 |
34 | var node: CMarkNode?
35 |
36 | // MARK: - Methods
37 |
38 | public mutating func next() -> Node? {
39 | guard let node = node else { return nil }
40 | defer { self.node = cmark_node_next(node) }
41 |
42 | guard let result = node.wrap() else {
43 | assertionFailure("Couldn't wrap node of type: \(node.type)")
44 | return nil
45 | }
46 |
47 | return result
48 | }
49 |
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/Code.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Code.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class Code: BaseNode {
12 |
13 | // MARK: - Properties
14 |
15 | /// The code content, if present.
16 |
17 | public private(set) lazy var literal: String? = cmarkNode.literal
18 |
19 | }
20 |
21 | // MARK: - Debug
22 |
23 | extension Code: CustomDebugStringConvertible {
24 |
25 | public var debugDescription: String {
26 | return "Code - \(literal ?? "nil")"
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/CodeBlock.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeBlock.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class CodeBlock: BaseNode {
12 |
13 | // MARK: - Properties
14 |
15 | /// The code content, if present.
16 |
17 | public private(set) lazy var literal: String? = cmarkNode.literal
18 |
19 | /// The fence info is an optional string that trails the opening sequence of backticks.
20 | /// It can be used to provide some contextual information about the block, such as
21 | /// the name of a programming language.
22 | ///
23 | /// For example:
24 | /// ```
25 | /// '''
26 | ///
27 | /// '''
28 | /// ```
29 | ///
30 |
31 | public private(set) lazy var fenceInfo: String? = cmarkNode.fenceInfo
32 |
33 | }
34 |
35 | // MARK: - Debug
36 |
37 | extension CodeBlock: CustomDebugStringConvertible {
38 |
39 | public var debugDescription: String {
40 | let content = (literal ?? "nil").replacingOccurrences(of: "\n", with: "\\n")
41 | return "Code Block - fenceInfo: \(fenceInfo ?? "nil"), content: \(content)"
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/CustomBlock.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomBlock.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class CustomBlock: BaseNode {
12 |
13 | // MARK: - Properfies
14 |
15 | /// The custom content, if present.
16 |
17 | public private(set) lazy var literal: String? = cmarkNode.literal
18 |
19 | }
20 |
21 | // MARK: - Debug
22 |
23 | extension CustomBlock: CustomDebugStringConvertible {
24 |
25 | public var debugDescription: String {
26 | return "Custom Block - \(literal ?? "nil")"
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/CustomInline.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomInline.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class CustomInline: BaseNode {
12 |
13 | // MARK: - Properties
14 |
15 | /// The custom content, if present.
16 |
17 | public private(set) lazy var literal: String? = cmarkNode.literal
18 | }
19 |
20 | // MARK: - Debug
21 |
22 | extension CustomInline: CustomDebugStringConvertible {
23 |
24 | public var debugDescription: String {
25 | return "Custom Inline - \(literal ?? "nil")"
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/Document.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Document.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class Document: BaseNode {
12 |
13 | // MARK: - Life cycle
14 |
15 | deinit {
16 | cmark_node_free(cmarkNode)
17 | }
18 |
19 | // MARK: - Methods
20 |
21 | /// Accepts the given visitor and return its result.
22 |
23 | @discardableResult
24 | public func accept(_ visitor: T) -> T.Result {
25 | return visitor.visit(document: self)
26 | }
27 |
28 | }
29 |
30 | // MARK: - Debug
31 |
32 | extension Document: CustomDebugStringConvertible {
33 |
34 | public var debugDescription: String {
35 | return "Document"
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/Emphasis.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Emphasis.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class Emphasis: BaseNode {}
12 |
13 | // MARK: - Debug
14 |
15 | extension Emphasis: CustomDebugStringConvertible {
16 |
17 | public var debugDescription: String {
18 | return "Emphasis"
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/Heading.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Heading.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class Heading: BaseNode {
12 |
13 | // MARK: - Properties
14 |
15 | /// The level of the heading, a value between 1 and 6.
16 |
17 | public private(set) lazy var headingLevel: Int = cmarkNode.headingLevel
18 | }
19 |
20 | // MARK: - Debug
21 |
22 | extension Heading: CustomDebugStringConvertible {
23 |
24 | public var debugDescription: String {
25 | return "Heading - L\(headingLevel)"
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/HtmlBlock.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HtmlBlock.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class HtmlBlock: BaseNode {
12 |
13 | // MARK: - Properties
14 |
15 | /// The html content, if present.
16 |
17 | public private(set) lazy var literal: String? = cmarkNode.literal
18 |
19 | }
20 |
21 | // MARK: - Debug
22 |
23 | extension HtmlBlock: CustomDebugStringConvertible {
24 |
25 | public var debugDescription: String {
26 | let content = (literal ?? "nil").replacingOccurrences(of: "\n", with: "\\n")
27 | return "Html Block - content: \(content)"
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/HtmlInline.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HtmlInline.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class HtmlInline: BaseNode {
12 |
13 | // MARK: - Properties
14 |
15 | /// The html tag, if present.
16 |
17 | public private(set) lazy var literal: String? = cmarkNode.literal
18 |
19 | }
20 |
21 | // MARK: - Debug
22 |
23 | extension HtmlInline: CustomDebugStringConvertible {
24 |
25 | public var debugDescription: String {
26 | return "Html Inline - \(literal ?? "nil")"
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/Image.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Image.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class Image: BaseNode {
12 |
13 | // MARK: - Properties
14 |
15 | /// The title of the image, if present.
16 | ///
17 | /// In the example below, the first line is a reference link, with the reference at the
18 | /// bottom. `` is literal text belonging to children nodes. The title occurs
19 | /// after the url and is optional.
20 | ///
21 | /// ```
22 | /// ![][]
23 | /// ...
24 | /// []: ""
25 | /// ```
26 |
27 | public private(set) lazy var title: String? = cmarkNode.title
28 |
29 | /// The url of the image, if present.
30 | ///
31 | /// For example:
32 | ///
33 | /// ```
34 | /// ![]()
35 | /// ```
36 |
37 | public private(set) lazy var url: String? = cmarkNode.url
38 |
39 | }
40 |
41 | // MARK: - Debug
42 |
43 | extension Image: CustomDebugStringConvertible {
44 |
45 | public var debugDescription: String {
46 | return "Image - title: \(title ?? "nil"), url: \(url ?? "nil"))"
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/Item.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Item.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class Item: BaseNode {}
12 |
13 | // MARK: - Debug
14 |
15 | extension Item: CustomDebugStringConvertible {
16 |
17 | public var debugDescription: String {
18 | return "Item"
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/LineBreak.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LineBreak.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class LineBreak: BaseNode {}
12 |
13 | // MARK: - Debug
14 |
15 | extension LineBreak: CustomDebugStringConvertible {
16 |
17 | public var debugDescription: String {
18 | return "Line Break"
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/Link.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Link.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class Link: BaseNode {
12 |
13 | // MARK: - Properties
14 |
15 | /// The title of the link, if present.
16 | ///
17 | /// In the example below, the first line is a reference link, with the reference at the
18 | /// bottom. `` is literal text belonging to children nodes. The title occurs
19 | /// after the url and is optional.
20 | ///
21 | /// ```
22 | /// [][]
23 | /// ...
24 | /// []: ""
25 | /// ```
26 |
27 | public private(set) lazy var title: String? = cmarkNode.title
28 |
29 | /// The url of the link, if present.
30 | ///
31 | /// For example:
32 | ///
33 | /// ```
34 | /// []()
35 | /// ```
36 |
37 | public private(set) lazy var url: String? = cmarkNode.url
38 |
39 | }
40 |
41 | // MARK: - Debug
42 |
43 | extension Link: CustomDebugStringConvertible {
44 |
45 | public var debugDescription: String {
46 | return "Link - title: \(title ?? "nil"), url: \(url ?? "nil"))"
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/List.swift:
--------------------------------------------------------------------------------
1 | //
2 | // List.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class List: BaseNode {
12 |
13 | // MARK: - Properties
14 |
15 | /// The type of the list, either bullet or ordered.
16 |
17 | public lazy var listType: ListType = {
18 | guard let type = ListType(cmarkNode: cmarkNode) else {
19 | assertionFailure("Unsupported or missing list type. Defaulting to .bullet.")
20 | return .bullet
21 | }
22 |
23 | return type
24 | }()
25 |
26 | /// The number of items in the list.
27 |
28 | public lazy var numberOfItems: Int = children.count
29 |
30 | /// Whether the list is "tight".
31 | ///
32 | /// If any of the list items are separated by a blank line, then this property is `false`. This value is
33 | /// a hint to render the list with more (loose) or less (tight) spacing between items.
34 |
35 | public lazy var isTight: Bool = cmark_node_get_list_tight(cmarkNode) == 1
36 |
37 | /// The list delimiter.
38 |
39 | public lazy var delimiter: Delimiter? = Delimiter(cmarkNode.listDelimiter)
40 | }
41 |
42 | // MARK: - List Type
43 |
44 | public extension List {
45 |
46 | enum Delimiter {
47 | case period
48 | case paren
49 |
50 | init?(_ cmark: cmark_delim_type) {
51 | switch cmark {
52 | case CMARK_NO_DELIM: return nil
53 | case CMARK_PERIOD_DELIM: self = .period
54 | case CMARK_PAREN_DELIM: self = .paren
55 | default: preconditionFailure("Invalid delim type")
56 | }
57 | }
58 | }
59 |
60 | enum ListType: CustomDebugStringConvertible {
61 | case bullet
62 | case ordered(start: Int)
63 |
64 | // MARK: - Properties
65 |
66 | public var debugDescription: String {
67 | switch self {
68 | case .bullet: return "Bullet"
69 | case .ordered(let start): return "Ordered (start: \(start))"
70 | }
71 | }
72 |
73 | // MARK: - Life cycle
74 |
75 | init?(cmarkNode: CMarkNode) {
76 | switch cmarkNode.listType {
77 | case CMARK_BULLET_LIST: self = .bullet
78 | case CMARK_ORDERED_LIST: self = .ordered(start: cmarkNode.listStart)
79 | default: return nil
80 | }
81 | }
82 |
83 | }
84 | }
85 |
86 | // MARK: - Debug
87 |
88 | extension List: CustomDebugStringConvertible {
89 |
90 | public var debugDescription: String {
91 | var result = "List - type: \(listType), isTight: \(isTight)"
92 | if let delim = delimiter {
93 | result += ", delimiter: \(delim)"
94 | }
95 | return result
96 | }
97 |
98 | }
99 |
100 | extension List.Delimiter: CustomDebugStringConvertible {
101 | public var debugDescription: String {
102 | switch self {
103 | case .paren: return "paren"
104 | case .period: return "period"
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/Paragraph.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Paragraph.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class Paragraph: BaseNode {}
12 |
13 | // MARK: - Debug
14 |
15 | extension Paragraph: CustomDebugStringConvertible {
16 |
17 | public var debugDescription: String {
18 | return "Paragraph"
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/SoftBreak.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SoftBreak.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class SoftBreak: BaseNode {}
12 |
13 | // MARK: - Debug
14 |
15 | extension SoftBreak: CustomDebugStringConvertible {
16 |
17 | public var debugDescription: String {
18 | return "Soft Break"
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/Strong.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Strong.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class Strong: BaseNode {}
12 |
13 | // MARK: - Debug
14 |
15 | extension Strong: CustomDebugStringConvertible {
16 |
17 | public var debugDescription: String {
18 | return "Strong"
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/Text.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Text.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class Text: BaseNode {
12 |
13 | // MARK: - Properties
14 |
15 | /// The text content, if present.
16 |
17 | public private(set) lazy var literal: String? = cmarkNode.literal
18 |
19 | }
20 |
21 | // MARK: - Debug
22 |
23 | extension Text: CustomDebugStringConvertible {
24 |
25 | public var debugDescription: String {
26 | return "Text - \(literal ?? "nil")"
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Nodes/ThematicBreak.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThematicBreak.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 | import libcmark
10 |
11 | public class ThematicBreak: BaseNode {}
12 |
13 | // MARK: - Debug
14 |
15 | extension ThematicBreak: CustomDebugStringConvertible {
16 |
17 | public var debugDescription: String {
18 | return "Thematic Break"
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Attribute Collections/ColorCollection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorCollection.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 27.07.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 | public typealias DownColor = UIColor
15 |
16 | #elseif canImport(AppKit)
17 |
18 | import AppKit
19 | public typealias DownColor = NSColor
20 |
21 | #endif
22 |
23 | public protocol ColorCollection {
24 |
25 | var heading1: DownColor { get }
26 | var heading2: DownColor { get }
27 | var heading3: DownColor { get }
28 | var heading4: DownColor { get }
29 | var heading5: DownColor { get }
30 | var heading6: DownColor { get }
31 | var body: DownColor { get }
32 | var code: DownColor { get }
33 | var link: DownColor { get }
34 | var quote: DownColor { get }
35 | var quoteStripe: DownColor { get }
36 | var thematicBreak: DownColor { get }
37 | var listItemPrefix: DownColor { get }
38 | var codeBlockBackground: DownColor { get }
39 |
40 | }
41 |
42 | public struct StaticColorCollection: ColorCollection {
43 |
44 | // MARK: - Properties
45 |
46 | public var heading1: DownColor
47 | public var heading2: DownColor
48 | public var heading3: DownColor
49 | public var heading4: DownColor
50 | public var heading5: DownColor
51 | public var heading6: DownColor
52 | public var body: DownColor
53 | public var code: DownColor
54 | public var link: DownColor
55 | public var quote: DownColor
56 | public var quoteStripe: DownColor
57 | public var thematicBreak: DownColor
58 | public var listItemPrefix: DownColor
59 | public var codeBlockBackground: DownColor
60 |
61 | // MARK: - Life cycle
62 |
63 | public init(
64 | heading1: DownColor = .black,
65 | heading2: DownColor = .black,
66 | heading3: DownColor = .black,
67 | heading4: DownColor = .black,
68 | heading5: DownColor = .black,
69 | heading6: DownColor = .black,
70 | body: DownColor = .black,
71 | code: DownColor = .black,
72 | link: DownColor = .blue,
73 | quote: DownColor = .darkGray,
74 | quoteStripe: DownColor = .darkGray,
75 | thematicBreak: DownColor = .init(white: 0.9, alpha: 1),
76 | listItemPrefix: DownColor = .lightGray,
77 | codeBlockBackground: DownColor = .init(red: 0.96, green: 0.97, blue: 0.98, alpha: 1)
78 | ) {
79 | self.heading1 = heading1
80 | self.heading2 = heading2
81 | self.heading3 = heading3
82 | self.heading4 = heading4
83 | self.heading5 = heading5
84 | self.heading6 = heading6
85 | self.body = body
86 | self.code = code
87 | self.link = link
88 | self.quote = quote
89 | self.quoteStripe = quoteStripe
90 | self.thematicBreak = thematicBreak
91 | self.listItemPrefix = listItemPrefix
92 | self.codeBlockBackground = codeBlockBackground
93 | }
94 |
95 | }
96 |
97 | #endif
98 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Attribute Collections/FontCollection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FontCollection.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 22.06.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 | public typealias DownFont = UIFont
15 |
16 | #elseif canImport(AppKit)
17 |
18 | import AppKit
19 | public typealias DownFont = NSFont
20 |
21 | #endif
22 |
23 | public protocol FontCollection {
24 |
25 | var heading1: DownFont { get }
26 | var heading2: DownFont { get }
27 | var heading3: DownFont { get }
28 | var heading4: DownFont { get }
29 | var heading5: DownFont { get }
30 | var heading6: DownFont { get }
31 | var body: DownFont { get }
32 | var code: DownFont { get }
33 | var listItemPrefix: DownFont { get }
34 |
35 | }
36 |
37 | public struct StaticFontCollection: FontCollection {
38 |
39 | // MARK: - Properties
40 |
41 | public var heading1: DownFont
42 | public var heading2: DownFont
43 | public var heading3: DownFont
44 | public var heading4: DownFont
45 | public var heading5: DownFont
46 | public var heading6: DownFont
47 | public var body: DownFont
48 | public var code: DownFont
49 | public var listItemPrefix: DownFont
50 |
51 | // MARK: - Life cycle
52 |
53 | public init(
54 | heading1: DownFont = .boldSystemFont(ofSize: 28),
55 | heading2: DownFont = .boldSystemFont(ofSize: 24),
56 | heading3: DownFont = .boldSystemFont(ofSize: 20),
57 | heading4: DownFont = .boldSystemFont(ofSize: 20),
58 | heading5: DownFont = .boldSystemFont(ofSize: 20),
59 | heading6: DownFont = .boldSystemFont(ofSize: 20),
60 | body: DownFont = .systemFont(ofSize: 17),
61 | code: DownFont = DownFont(name: "menlo", size: 17) ?? .systemFont(ofSize: 17),
62 | listItemPrefix: DownFont = DownFont.monospacedDigitSystemFont(ofSize: 17, weight: .regular)
63 | ) {
64 | self.heading1 = heading1
65 | self.heading2 = heading2
66 | self.heading3 = heading3
67 | self.heading4 = heading4
68 | self.heading5 = heading5
69 | self.heading6 = heading6
70 | self.body = body
71 | self.code = code
72 | self.listItemPrefix = listItemPrefix
73 | }
74 |
75 | }
76 |
77 | #endif
78 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Attribute Collections/ParagraphStyleCollection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ParagraphStyleCollection.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 27.07.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | public protocol ParagraphStyleCollection {
22 |
23 | var heading1: NSParagraphStyle { get }
24 | var heading2: NSParagraphStyle { get }
25 | var heading3: NSParagraphStyle { get }
26 | var heading4: NSParagraphStyle { get }
27 | var heading5: NSParagraphStyle { get }
28 | var heading6: NSParagraphStyle { get }
29 | var body: NSParagraphStyle { get }
30 | var code: NSParagraphStyle { get }
31 |
32 | }
33 |
34 | public struct StaticParagraphStyleCollection: ParagraphStyleCollection {
35 |
36 | // MARK: - Properties
37 |
38 | public var heading1: NSParagraphStyle
39 | public var heading2: NSParagraphStyle
40 | public var heading3: NSParagraphStyle
41 | public var heading4: NSParagraphStyle
42 | public var heading5: NSParagraphStyle
43 | public var heading6: NSParagraphStyle
44 | public var body: NSParagraphStyle
45 | public var code: NSParagraphStyle
46 |
47 | // MARK: - Life cycle
48 |
49 | public init() {
50 | let headingStyle = NSMutableParagraphStyle()
51 | headingStyle.paragraphSpacing = 8
52 |
53 | let bodyStyle = NSMutableParagraphStyle()
54 | bodyStyle.paragraphSpacingBefore = 8
55 | bodyStyle.paragraphSpacing = 8
56 | bodyStyle.lineSpacing = 8
57 |
58 | let codeStyle = NSMutableParagraphStyle()
59 | codeStyle.paragraphSpacingBefore = 8
60 | codeStyle.paragraphSpacing = 8
61 |
62 | heading1 = headingStyle
63 | heading2 = headingStyle
64 | heading3 = headingStyle
65 | heading4 = headingStyle
66 | heading5 = headingStyle
67 | heading6 = headingStyle
68 | body = bodyStyle
69 | code = codeStyle
70 | }
71 |
72 | }
73 |
74 | #endif
75 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Custom Attributes/BlockBackgroundColorAttribute.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BlockBackgroundColorAttribute.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 11.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | struct BlockBackgroundColorAttribute {
22 |
23 | // MARK: - Properties
24 |
25 | var color: DownColor
26 | var inset: CGFloat
27 |
28 | }
29 |
30 | extension NSAttributedString.Key {
31 |
32 | static let blockBackgroundColor = NSAttributedString.Key("blockBackgroundColor")
33 |
34 | }
35 |
36 | #endif
37 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Custom Attributes/QuoteStripeAttribute.swift:
--------------------------------------------------------------------------------
1 | //
2 | // QuoteStripeAttrbute.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 03.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | struct QuoteStripeAttribute {
22 |
23 | // MARK: - Properties
24 |
25 | var color: DownColor
26 | var thickness: CGFloat
27 | var spacingAfter: CGFloat
28 | var locations: [CGFloat]
29 |
30 | var layoutWidth: CGFloat {
31 | return thickness + spacingAfter
32 | }
33 |
34 | // MARK: - Life cycle
35 |
36 | init(color: DownColor, thickness: CGFloat, spacingAfter: CGFloat, locations: [CGFloat]) {
37 | self.color = color
38 | self.thickness = thickness
39 | self.spacingAfter = spacingAfter
40 | self.locations = locations
41 | }
42 |
43 | init(level: Int, color: DownColor, options: QuoteStripeOptions) {
44 | self.init(color: color, thickness: options.thickness, spacingAfter: options.spacingAfter, locations: [])
45 | locations = (0.. QuoteStripeAttribute {
51 | var copy = self
52 | copy.locations = locations.map { $0 + indentation }
53 | return copy
54 | }
55 |
56 | }
57 |
58 | extension NSAttributedString.Key {
59 |
60 | static let quoteStripe = NSAttributedString.Key(rawValue: "quoteStripe")
61 |
62 | }
63 |
64 | #endif
65 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Custom Attributes/ThematicBreakAttribute.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThematicBreaAttributek.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 02.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | struct ThematicBreakAttribute {
22 |
23 | // MARK: - Properties
24 |
25 | var thickness: CGFloat
26 | var color: DownColor
27 |
28 | }
29 |
30 | extension NSAttributedString.Key {
31 |
32 | static let thematicBreak = NSAttributedString.Key(rawValue: "thematicBreak")
33 |
34 | }
35 |
36 | #endif
37 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Helpers/Extensions/CGPoint+Translate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint+Translate.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 12.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | extension CGPoint {
22 |
23 | func translated(by point: CGPoint) -> CGPoint {
24 | return CGPoint(x: x + point.x, y: y + point.y)
25 | }
26 |
27 | }
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Helpers/Extensions/CGRect+Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGRect+Helpers.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 12.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | extension CGRect {
22 |
23 | init(minX: CGFloat, minY: CGFloat, maxX: CGFloat, maxY: CGFloat) {
24 | self.init(x: minX, y: minY, width: maxX - minX, height: maxY - minY)
25 | }
26 |
27 | func translated(by point: CGPoint) -> CGRect {
28 | return CGRect(origin: origin.translated(by: point), size: size)
29 | }
30 |
31 | }
32 |
33 | #endif
34 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Helpers/Extensions/NSAttributedString+Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSAttributedString+Helpers.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 22.06.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension NSAttributedString {
12 |
13 | typealias Attributes = [NSAttributedString.Key: Any]
14 |
15 | // MARK: - Ranges
16 |
17 | var wholeRange: NSRange {
18 | return NSRange(location: 0, length: length)
19 | }
20 |
21 | func ranges(of key: Key) -> [NSRange] {
22 | return ranges(of: key, in: wholeRange)
23 | }
24 |
25 | func ranges(of key: Key, in range: NSRange) -> [NSRange] {
26 | return ranges(for: key, in: range, where: { $0 != nil })
27 | }
28 |
29 | func rangesMissingAttribute(for key: Key) -> [NSRange] {
30 | return rangesMissingAttribute(for: key, in: wholeRange)
31 | }
32 |
33 | func rangesMissingAttribute(for key: Key, in range: NSRange) -> [NSRange] {
34 | return ranges(for: key, in: range, where: { $0 == nil })
35 | }
36 |
37 | private func ranges(for key: Key, in range: NSRange, where predicate: (Any?) -> Bool) -> [NSRange] {
38 | var ranges = [NSRange]()
39 |
40 | enumerateAttribute(key, in: range, options: []) { value, attrRange, _ in
41 | if predicate(value) {
42 | ranges.append(attrRange)
43 | }
44 | }
45 |
46 | return ranges
47 | }
48 |
49 | func paragraphRanges() -> [NSRange] {
50 | guard length > 0 else { return [] }
51 |
52 | func nextParagraphRange(at location: Int) -> NSRange {
53 | return NSString(string: string).paragraphRange(for: NSRange(location: location, length: 1))
54 | }
55 |
56 | var result = [nextParagraphRange(at: 0)]
57 |
58 | while let currentLocation = result.last?.upperBound, currentLocation < length {
59 | result.append(nextParagraphRange(at: currentLocation))
60 | }
61 |
62 | return result.filter { $0.length > 1 }
63 | }
64 |
65 | // MARK: - Enumerate attributes
66 |
67 | func enumerateAttributes(for key: Key, block: (_ attr: A, _ range: NSRange) -> Void) {
68 | enumerateAttributes(for: key, in: wholeRange, block: block)
69 | }
70 |
71 | func enumerateAttributes(for key: Key, in range: NSRange, block: (_ attr: A, _ range: NSRange) -> Void) {
72 | enumerateAttribute(key, in: range, options: []) { value, range, _ in
73 | if let value = value as? A {
74 | block(value, range)
75 | }
76 | }
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Helpers/Extensions/NSMutableAttributedString+Attributes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSMutableAttributedString+Attributes.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 22.06.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension NSMutableAttributedString {
12 |
13 | func setAttributes(_ attrs: Attributes) {
14 | setAttributes(attrs, range: wholeRange)
15 | }
16 |
17 | func addAttributes(_ attrs: Attributes) {
18 | addAttributes(attrs, range: wholeRange)
19 | }
20 |
21 | func addAttribute(for key: Key, value: Any) {
22 | addAttribute(key, value: value, range: wholeRange)
23 | }
24 |
25 | func removeAttribute(for key: Key) {
26 | removeAttribute(key, range: wholeRange)
27 | }
28 |
29 | func replaceAttribute(for key: Key, value: Any) {
30 | replaceAttribute(for: key, value: value, inRange: wholeRange)
31 | }
32 |
33 | func replaceAttribute(for key: Key, value: Any, inRange range: NSRange) {
34 | removeAttribute(key, range: range)
35 | addAttribute(key, value: value, range: range)
36 | }
37 |
38 | func updateExistingAttributes(for key: Key, using transform: (A) -> A) {
39 | updateExistingAttributes(for: key, in: wholeRange, using: transform)
40 | }
41 |
42 | func updateExistingAttributes(for key: Key, in range: NSRange, using transform: (A) -> A) {
43 | var existingValues = [(value: A, range: NSRange)]()
44 | enumerateAttributes(for: key, in: range) { existingValues.append(($0, $1)) }
45 | existingValues.forEach { addAttribute(key, value: transform($0.0), range: $0.1) }
46 | }
47 |
48 | func addAttributeInMissingRanges(for key: Key, value: A) {
49 | addAttributeInMissingRanges(for: key, value: value, within: wholeRange)
50 | }
51 |
52 | func addAttributeInMissingRanges(for key: Key, value: A, within range: NSRange) {
53 | rangesMissingAttribute(for: key, in: range).forEach {
54 | addAttribute(key, value: value, range: $0)
55 | }
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Helpers/Extensions/UIFont+Traits.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIFont+Traits.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 22.06.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 | public typealias DownFontDescriptor = UIFontDescriptor
15 |
16 | #elseif canImport(AppKit)
17 |
18 | import AppKit
19 | public typealias DownFontDescriptor = NSFontDescriptor
20 |
21 | #endif
22 |
23 | extension DownFont {
24 |
25 | var isStrong: Bool {
26 | return contains(.strong)
27 | }
28 |
29 | var isEmphasized: Bool {
30 | return contains(.emphasis)
31 | }
32 |
33 | var isMonospace: Bool {
34 | return contains(.monoSpace)
35 | }
36 |
37 | var strong: DownFont {
38 | return with(.strong) ?? self
39 | }
40 |
41 | var emphasis: DownFont {
42 | return with(.emphasis) ?? self
43 | }
44 |
45 | var monospace: DownFont {
46 | return with(.monoSpace) ?? self
47 | }
48 |
49 | private func with(_ trait: DownFontDescriptor.SymbolicTraits) -> DownFont? {
50 | guard !contains(trait) else { return self }
51 |
52 | var traits = fontDescriptor.symbolicTraits
53 | traits.insert(trait)
54 |
55 | #if canImport(UIKit)
56 | guard let newDescriptor = fontDescriptor.withSymbolicTraits(traits) else { return self }
57 | return DownFont(descriptor: newDescriptor, size: pointSize)
58 |
59 | #elseif canImport(AppKit)
60 | let newDescriptor = fontDescriptor.withSymbolicTraits(traits)
61 | return DownFont(descriptor: newDescriptor, size: pointSize)
62 |
63 | #endif
64 | }
65 |
66 | private func contains(_ trait: DownFontDescriptor.SymbolicTraits) -> Bool {
67 | return fontDescriptor.symbolicTraits.contains(trait)
68 | }
69 |
70 | }
71 |
72 | #if canImport(UIKit)
73 |
74 | private extension DownFontDescriptor.SymbolicTraits {
75 |
76 | static let strong = DownFontDescriptor.SymbolicTraits.traitBold
77 | static let emphasis = DownFontDescriptor.SymbolicTraits.traitItalic
78 | static let monoSpace = DownFontDescriptor.SymbolicTraits.traitMonoSpace
79 |
80 | }
81 |
82 | #elseif canImport(AppKit)
83 |
84 | private extension DownFontDescriptor.SymbolicTraits {
85 |
86 | static let strong = DownFontDescriptor.SymbolicTraits.bold
87 | static let emphasis = DownFontDescriptor.SymbolicTraits.italic
88 | static let monoSpace = DownFontDescriptor.SymbolicTraits.monoSpace
89 |
90 | }
91 |
92 | #endif
93 |
94 | #endif
95 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Helpers/ListItemParagraphStyler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ParagraphStyler.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 25.06.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | /// A convenient class used to format lists, such that list item prefixes
22 | /// are right aligned and list item content left aligns.
23 |
24 | public class ListItemParagraphStyler {
25 |
26 | // MARK: - Properties
27 |
28 | public var indentation: CGFloat {
29 | return largestPrefixWidth + options.spacingAfterPrefix
30 | }
31 |
32 | /// The paragraph style intended for all paragraphs excluding the first.
33 |
34 | public var trailingParagraphStyle: NSParagraphStyle {
35 | let contentIndentation = indentation
36 | let style = baseStyle
37 | style.firstLineHeadIndent = contentIndentation
38 | style.headIndent = contentIndentation
39 | return style
40 | }
41 |
42 | private let options: ListItemOptions
43 | private let largestPrefixWidth: CGFloat
44 |
45 | private var baseStyle: NSMutableParagraphStyle {
46 | let style = NSMutableParagraphStyle()
47 | style.paragraphSpacingBefore = options.spacingAbove
48 | style.paragraphSpacing = options.spacingBelow
49 | style.alignment = options.alignment
50 | return style
51 | }
52 |
53 | // MARK: - Life cycle
54 |
55 | public init(options: ListItemOptions, prefixFont: DownFont) {
56 | self.options = options
57 | self.largestPrefixWidth = prefixFont.widthOfNumberedPrefix(digits: options.maxPrefixDigits)
58 | }
59 |
60 | // MARK: - Methods
61 |
62 | /// The paragraph style intended for the first paragraph of the list item.
63 | ///
64 | /// - Parameter prefixWidth: the width (in points) of the list item prefix.
65 |
66 | public func leadingParagraphStyle(prefixWidth: CGFloat) -> NSParagraphStyle {
67 | let contentIndentation = indentation
68 | let prefixIndentation: CGFloat = contentIndentation - options.spacingAfterPrefix - prefixWidth
69 | let prefixSpill = max(0, prefixWidth - largestPrefixWidth)
70 | let firstLineContentIndentation = contentIndentation + prefixSpill
71 |
72 | let style = baseStyle
73 | style.firstLineHeadIndent = prefixIndentation
74 | style.tabStops = [tabStop(at: firstLineContentIndentation)]
75 | style.headIndent = contentIndentation
76 | return style
77 | }
78 |
79 | private func tabStop(at location: CGFloat) -> NSTextTab {
80 | return NSTextTab(textAlignment: options.alignment, location: location, options: [:])
81 | }
82 |
83 | }
84 |
85 | // MARK: - Helpers
86 |
87 | private extension DownFont {
88 |
89 | func widthOfNumberedPrefix(digits: UInt) -> CGFloat {
90 | return widthOfLargestDigit * CGFloat(digits) + widthOfPeriod
91 | }
92 |
93 | private var widthOfLargestDigit: CGFloat {
94 | return Int.decimalDigits
95 | .map { NSAttributedString(string: "\($0)", attributes: [.font: self]).size().width }
96 | .max()!
97 | }
98 |
99 | private var widthOfPeriod: CGFloat {
100 | return NSAttributedString(string: ".", attributes: [.font: self])
101 | .size()
102 | .width
103 | }
104 |
105 | }
106 |
107 | private extension Int {
108 |
109 | static var decimalDigits: [Int] {
110 | return Array(0...9)
111 | }
112 |
113 | }
114 |
115 | #endif
116 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Layout Managers/DownDebugLayoutManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownDebugLayoutManager.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 06.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | /// A layout manager that draws the line fragments.
22 | ///
23 | /// Line fragments are the areas with a document that contain lines of text. There
24 | /// are two types.
25 | ///
26 | /// 1. A *line rect* (drawn in red) indicates the maximum rect enclosing the line.
27 | /// This inlcudes not only the textual content, but also the padding (if any) around that text.
28 | /// 2. A *line used rect* (drawn in blue) is the smallest rect enclosing the textual content.
29 | ///
30 | /// The visualization of these rects is useful when determining the paragraph styles
31 | /// of a `DownStyler`.
32 | ///
33 | /// Insert this into a TextKit stack manually, or use the provided `DownDebugTextView`.
34 |
35 | public class DownDebugLayoutManager: DownLayoutManager {
36 |
37 | // MARK: - Drawing
38 |
39 | override public func drawGlyphs(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) {
40 | super.drawGlyphs(forGlyphRange: glyphsToShow, at: origin)
41 | drawLineFragments(forGlyphRange: glyphsToShow, at: origin)
42 | }
43 |
44 | private func drawLineFragments(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) {
45 | enumerateLineFragments(forGlyphRange: glyphsToShow) { rect, usedRect, _, _, _ in
46 | [(usedRect, DownColor.blue), (rect, DownColor.red)].forEach { rectToDraw, color in
47 | let adjustedRect = rectToDraw.translated(by: origin)
48 | self.drawRect(adjustedRect, color: color.cgColor)
49 | }
50 | }
51 | }
52 |
53 | private func drawRect(_ rect: CGRect, color: CGColor) {
54 | guard let context = context else { return }
55 | push(context: context)
56 | defer { popContext() }
57 |
58 | context.setStrokeColor(color)
59 | context.stroke(rect)
60 | }
61 |
62 | }
63 |
64 | #endif
65 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Options/CodeBlockOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeBlockOptions.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 12.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | public struct CodeBlockOptions {
22 |
23 | // MARK: - Properties
24 |
25 | public var containerInset: CGFloat
26 |
27 | // MARK: - Life cycle
28 |
29 | public init(containerInset: CGFloat = 8) {
30 | self.containerInset = containerInset
31 | }
32 |
33 | }
34 |
35 | #endif
36 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Options/ListItemOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListItemOptions.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 04.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | public struct ListItemOptions {
22 |
23 | // MARK: - Properties
24 |
25 | public var maxPrefixDigits: UInt
26 | public var spacingAfterPrefix: CGFloat
27 | public var spacingAbove: CGFloat
28 | public var spacingBelow: CGFloat
29 | public var alignment: NSTextAlignment
30 |
31 | // MARK: - Life cycle
32 |
33 | public init(maxPrefixDigits: UInt = 2,
34 | spacingAfterPrefix: CGFloat = 8,
35 | spacingAbove: CGFloat = 4,
36 | spacingBelow: CGFloat = 8,
37 | alignment: NSTextAlignment = .natural) {
38 |
39 | self.maxPrefixDigits = maxPrefixDigits
40 | self.spacingAfterPrefix = spacingAfterPrefix
41 | self.spacingAbove = spacingAbove
42 | self.spacingBelow = spacingBelow
43 | self.alignment = alignment
44 | }
45 |
46 | }
47 |
48 | #endif
49 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Options/QuoteStripeOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // QuoteStripeOptions.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 04.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | public struct QuoteStripeOptions {
22 |
23 | // MARK: - Properties
24 |
25 | public var thickness: CGFloat
26 | public var spacingAfter: CGFloat
27 |
28 | // MARK: - Life cycle
29 |
30 | public init(thickness: CGFloat = 2, spacingAfter: CGFloat = 8) {
31 | self.thickness = thickness
32 | self.spacingAfter = spacingAfter
33 | }
34 |
35 | }
36 |
37 | #endif
38 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Options/ThematicBreakOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThematicBreakOptions.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 04.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | public struct ThematicBreakOptions {
22 |
23 | // MARK: - Properties
24 |
25 | public var thickness: CGFloat
26 | public var indentation: CGFloat
27 |
28 | // MARK: - Life cycle
29 |
30 | public init(thickness: CGFloat = 1, indentation: CGFloat = 0) {
31 | self.thickness = thickness
32 | self.indentation = indentation
33 | }
34 |
35 | }
36 |
37 | #endif
38 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Stylers/DownStylerConfiguration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownStylerConfiguration.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 10.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | /// A configuration object used to initialze the `DownStyler`.
12 |
13 | public struct DownStylerConfiguration {
14 |
15 | // MARK: - Properties
16 |
17 | public var fonts: FontCollection
18 | public var colors: ColorCollection
19 | public var paragraphStyles: ParagraphStyleCollection
20 |
21 | public var listItemOptions: ListItemOptions
22 | public var quoteStripeOptions: QuoteStripeOptions
23 | public var thematicBreakOptions: ThematicBreakOptions
24 | public var codeBlockOptions: CodeBlockOptions
25 |
26 | // MARK: - Life cycle
27 |
28 | public init(fonts: FontCollection = StaticFontCollection(),
29 | colors: ColorCollection = StaticColorCollection(),
30 | paragraphStyles: ParagraphStyleCollection = StaticParagraphStyleCollection(),
31 | listItemOptions: ListItemOptions = ListItemOptions(),
32 | quoteStripeOptions: QuoteStripeOptions = QuoteStripeOptions(),
33 | thematicBreakOptions: ThematicBreakOptions = ThematicBreakOptions(),
34 | codeBlockOptions: CodeBlockOptions = CodeBlockOptions()
35 | ) {
36 | self.fonts = fonts
37 | self.colors = colors
38 | self.paragraphStyles = paragraphStyles
39 | self.listItemOptions = listItemOptions
40 | self.quoteStripeOptions = quoteStripeOptions
41 | self.thematicBreakOptions = thematicBreakOptions
42 | self.codeBlockOptions = codeBlockOptions
43 | }
44 |
45 | }
46 |
47 | #endif
48 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Text Views/DownDebugTextView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownDebugTextView.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 06.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 |
15 | #elseif canImport(AppKit)
16 |
17 | import AppKit
18 |
19 | #endif
20 |
21 | /// A text view capable of parsing and rendering markdown via the AST, as well as line fragments.
22 | ///
23 | /// See `DownDebugLayoutManager`.
24 |
25 | public class DownDebugTextView: DownTextView {
26 |
27 | // MARK: - Life cycle
28 |
29 | public init(frame: CGRect, styler: Styler = DownStyler()) {
30 | super.init(frame: frame, styler: styler, layoutManager: DownDebugLayoutManager())
31 | }
32 |
33 | required public init?(coder: NSCoder) {
34 | fatalError("init(coder:) has not been implemented")
35 | }
36 |
37 | }
38 |
39 | #endif
40 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Styling/Text Views/DownTextView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownTextView.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 03.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(watchOS) && !os(Linux)
10 |
11 | #if canImport(UIKit)
12 |
13 | import UIKit
14 | public typealias TextView = UITextView
15 |
16 | #elseif canImport(AppKit)
17 |
18 | import AppKit
19 | public typealias TextView = NSTextView
20 |
21 | #endif
22 |
23 | /// A text view capable of parsing and rendering markdown via the AST.
24 |
25 | open class DownTextView: TextView {
26 |
27 | // MARK: - Properties
28 |
29 | open var styler: Styler {
30 | didSet {
31 | try? render()
32 | }
33 | }
34 |
35 | #if canImport(UIKit)
36 |
37 | open override var text: String! {
38 | didSet {
39 | guard oldValue != text else { return }
40 | try? render()
41 | }
42 | }
43 |
44 | #elseif canImport(AppKit)
45 |
46 | open override var string: String {
47 | didSet {
48 | guard oldValue != string else { return }
49 | try? render()
50 | }
51 | }
52 |
53 | #endif
54 |
55 | // MARK: - Life cycle
56 |
57 | public convenience init(frame: CGRect, styler: Styler = DownStyler()) {
58 | self.init(frame: frame, styler: styler, layoutManager: DownLayoutManager())
59 | }
60 |
61 | public init(frame: CGRect, styler: Styler, layoutManager: NSLayoutManager) {
62 | self.styler = styler
63 |
64 | let textStorage = NSTextStorage()
65 | let textContainer = NSTextContainer()
66 |
67 | textStorage.addLayoutManager(layoutManager)
68 | layoutManager.addTextContainer(textContainer)
69 |
70 | super.init(frame: frame, textContainer: textContainer)
71 |
72 | // We don't want the text view to overwrite link attributes set
73 | // by the styler.
74 | linkTextAttributes = [:]
75 | }
76 |
77 | required public init?(coder: NSCoder) {
78 | fatalError("init(coder:) has not been implemented")
79 | }
80 |
81 | // MARK: - Methods
82 |
83 | open func render() throws {
84 | #if canImport(UIKit)
85 | let down = Down(markdownString: text)
86 | let markdown = try down.toAttributedString(styler: styler)
87 | attributedText = markdown
88 |
89 | #elseif canImport(AppKit)
90 | guard let textStorage = textStorage else { return }
91 | let down = Down(markdownString: string)
92 | let markdown = try down.toAttributedString(styler: styler)
93 | textStorage.replaceCharacters(in: textStorage.wholeRange, with: markdown)
94 |
95 | #endif
96 | }
97 |
98 | }
99 |
100 | #endif
101 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Visitors/DebugVisitor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DebugVisitor.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 09.04.19.
6 | //
7 |
8 | import Foundation
9 |
10 | /// This visitor will generate the debug description of an entire abstract syntax tree,
11 | /// indicating relationships between nodes with indentation.
12 |
13 | public class DebugVisitor: Visitor {
14 |
15 | // MARK: - Properties
16 |
17 | private var depth = 0
18 |
19 | private var indent: String {
20 | return String(repeating: " ", count: depth)
21 | }
22 |
23 | // MARK: - Life cycle
24 |
25 | public init() {}
26 |
27 | // MARK: - Helpers
28 |
29 | private func report(_ node: Node) -> String {
30 | return "\(indent)\(node is Document ? "" : "↳ ")\(String(reflecting: node))\n"
31 | }
32 |
33 | private func reportWithChildren(_ node: Node) -> String {
34 | let thisNode = report(node)
35 | depth += 1
36 | let children = visitChildren(of: node).joined()
37 | depth -= 1
38 | return "\(thisNode)\(children)"
39 | }
40 |
41 | // MARK: - Visitor
42 |
43 | public typealias Result = String
44 |
45 | public func visit(document node: Document) -> String {
46 | return reportWithChildren(node)
47 | }
48 |
49 | public func visit(blockQuote node: BlockQuote) -> String {
50 | return reportWithChildren(node)
51 | }
52 |
53 | public func visit(list node: List) -> String {
54 | return reportWithChildren(node)
55 | }
56 |
57 | public func visit(item node: Item) -> String {
58 | return reportWithChildren(node)
59 | }
60 |
61 | public func visit(codeBlock node: CodeBlock) -> String {
62 | return reportWithChildren(node)
63 | }
64 |
65 | public func visit(htmlBlock node: HtmlBlock) -> String {
66 | return reportWithChildren(node)
67 | }
68 |
69 | public func visit(customBlock node: CustomBlock) -> String {
70 | return reportWithChildren(node)
71 | }
72 |
73 | public func visit(paragraph node: Paragraph) -> String {
74 | return reportWithChildren(node)
75 | }
76 |
77 | public func visit(heading node: Heading) -> String {
78 | return reportWithChildren(node)
79 | }
80 |
81 | public func visit(thematicBreak node: ThematicBreak) -> String {
82 | return report(node)
83 | }
84 |
85 | public func visit(text node: Text) -> String {
86 | return report(node)
87 | }
88 |
89 | public func visit(softBreak node: SoftBreak) -> String {
90 | return report(node)
91 | }
92 |
93 | public func visit(lineBreak node: LineBreak) -> String {
94 | return report(node)
95 | }
96 |
97 | public func visit(code node: Code) -> String {
98 | return report(node)
99 | }
100 |
101 | public func visit(htmlInline node: HtmlInline) -> String {
102 | return report(node)
103 | }
104 |
105 | public func visit(customInline node: CustomInline) -> String {
106 | return report(node)
107 | }
108 |
109 | public func visit(emphasis node: Emphasis) -> String {
110 | return reportWithChildren(node)
111 | }
112 |
113 | public func visit(strong node: Strong) -> String {
114 | return reportWithChildren(node)
115 | }
116 |
117 | public func visit(link node: Link) -> String {
118 | return reportWithChildren(node)
119 | }
120 |
121 | public func visit(image node: Image) -> String {
122 | return reportWithChildren(node)
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Visitors/ListItemPrefixGenerator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListItemPrefixGenerator.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 23.06.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// A ListItemPrefixGenerator is an object used to generate list item prefix.
12 | public protocol ListItemPrefixGenerator {
13 | init(listType: List.ListType, numberOfItems: Int, nestDepth: Int)
14 | func next() -> String?
15 | }
16 |
17 | public extension ListItemPrefixGenerator {
18 | init(list: List) {
19 | self.init(listType: list.listType, numberOfItems: list.numberOfItems, nestDepth: list.nestDepth)
20 | }
21 | }
22 |
23 | /// Default implementation of `ListItemPrefixGenerator`.
24 | /// Generating the following symbol based on `List.ListType`:
25 | /// - List.ListType is bullet => "•"
26 | /// - List.ListType is ordered => "X." (where is the item number)
27 | public class StaticListItemPrefixGenerator: ListItemPrefixGenerator {
28 |
29 | // MARK: - Properties
30 |
31 | private var prefixes: IndexingIterator<[String]>
32 |
33 | // MARK: - Life cycle
34 |
35 | required public init(listType: List.ListType, numberOfItems: Int, nestDepth: Int) {
36 | switch listType {
37 | case .bullet:
38 | prefixes = [String](repeating: "•", count: numberOfItems)
39 | .makeIterator()
40 |
41 | case .ordered(let start):
42 | prefixes = (start..<(start + numberOfItems))
43 | .map { "\($0)." }
44 | .makeIterator()
45 | }
46 | }
47 |
48 | // MARK: - Methods
49 |
50 | public func next() -> String? {
51 | prefixes.next()
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/Sources/Down/AST/Visitors/Visitor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Vistor.swift
3 | // Down
4 | //
5 | // Created by John Nguyen on 07.04.19.
6 | //
7 |
8 | import Foundation
9 |
10 | /// Visitor describes a type that is able to traverse the abstract syntax tree. It visits
11 | /// each node of the tree and produces some result for that node. A visitor is "accepted" by
12 | /// the root node (of type `Document`), which will start the traversal by first invoking
13 | /// `visit(document:)`.
14 |
15 | public protocol Visitor {
16 |
17 | associatedtype Result
18 |
19 | func visit(document node: Document) -> Result
20 | func visit(blockQuote node: BlockQuote) -> Result
21 | func visit(list node: List) -> Result
22 | func visit(item node: Item) -> Result
23 | func visit(codeBlock node: CodeBlock) -> Result
24 | func visit(htmlBlock node: HtmlBlock) -> Result
25 | func visit(customBlock node: CustomBlock) -> Result
26 | func visit(paragraph node: Paragraph) -> Result
27 | func visit(heading node: Heading) -> Result
28 | func visit(thematicBreak node: ThematicBreak) -> Result
29 | func visit(text node: Text) -> Result
30 | func visit(softBreak node: SoftBreak) -> Result
31 | func visit(lineBreak node: LineBreak) -> Result
32 | func visit(code node: Code) -> Result
33 | func visit(htmlInline node: HtmlInline) -> Result
34 | func visit(customInline node: CustomInline) -> Result
35 | func visit(emphasis node: Emphasis) -> Result
36 | func visit(strong node: Strong) -> Result
37 | func visit(link node: Link) -> Result
38 | func visit(image node: Image) -> Result
39 | func visitChildren(of node: Node) -> [Result]
40 |
41 | }
42 |
43 | extension Visitor {
44 |
45 | public func visitChildren(of node: Node) -> [Result] {
46 | return node.childSequence.compactMap { child in
47 | switch child {
48 | case let child as Document: return visit(document: child)
49 | case let child as BlockQuote: return visit(blockQuote: child)
50 | case let child as List: return visit(list: child)
51 | case let child as Item: return visit(item: child)
52 | case let child as CodeBlock: return visit(codeBlock: child)
53 | case let child as HtmlBlock: return visit(htmlBlock: child)
54 | case let child as CustomBlock: return visit(customBlock: child)
55 | case let child as Paragraph: return visit(paragraph: child)
56 | case let child as Heading: return visit(heading: child)
57 | case let child as ThematicBreak: return visit(thematicBreak: child)
58 | case let child as Text: return visit(text: child)
59 | case let child as SoftBreak: return visit(softBreak: child)
60 | case let child as LineBreak: return visit(lineBreak: child)
61 | case let child as Code: return visit(code: child)
62 | case let child as HtmlInline: return visit(htmlInline: child)
63 | case let child as CustomInline: return visit(customInline: child)
64 | case let child as Emphasis: return visit(emphasis: child)
65 | case let child as Strong: return visit(strong: child)
66 | case let child as Link: return visit(link: child)
67 | case let child as Image: return visit(image: child)
68 | default:
69 | assertionFailure("Unexpected child")
70 | return nil
71 | }
72 | }
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/Sources/Down/Down.h:
--------------------------------------------------------------------------------
1 | //
2 | // Down.h
3 | // Down
4 | //
5 | // Created by Rob Phillips on 6/1/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | @import Foundation;
10 |
11 | //! Project version number for Down.
12 | FOUNDATION_EXPORT double DownVersionNumber;
13 |
14 | //! Project version string for Down.
15 | FOUNDATION_EXPORT const unsigned char DownVersionString[];
16 |
--------------------------------------------------------------------------------
/Sources/Down/Down.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Down.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 5/28/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Down: DownASTRenderable, DownHTMLRenderable, DownXMLRenderable,
12 | DownLaTeXRenderable, DownGroffRenderable, DownCommonMarkRenderable {
13 | /// A string containing CommonMark Markdown
14 | public var markdownString: String
15 |
16 | /// Initializes the container with a CommonMark Markdown string which can then be
17 | /// rendered depending on protocol conformance.
18 | ///
19 | /// - Parameter markdownString: A string containing CommonMark Markdown
20 | public init(markdownString: String) {
21 | self.markdownString = markdownString
22 | }
23 | }
24 |
25 | #if !os(Linux)
26 | extension Down: DownAttributedStringRenderable { }
27 | #endif // !os(Linux)
28 |
--------------------------------------------------------------------------------
/Sources/Down/Enums & Options/DownErrors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownErrors.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 5/28/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public enum DownErrors: Error {
12 |
13 | /// Thrown when there was an issue converting the Markdown into an abstract syntax tree.
14 |
15 | case markdownToASTError
16 |
17 | /// Thrown when the abstract syntax tree could not be rendered into another format.
18 |
19 | case astRenderingError
20 |
21 | /// Thrown when an HTML string cannot be converted into an `NSData` representation.
22 |
23 | case htmlDataConversionError
24 |
25 | #if os(macOS)
26 |
27 | /// Thrown when a custom template bundle has a non-standard bundle format.
28 | ///
29 | /// Specifically, the file URL of the bundle’s subdirectory containing resource files could
30 | /// not be found (i.e. the bundle's `resourceURL` property is nil).
31 |
32 | case nonStandardBundleFormatError
33 |
34 | #endif
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/Down/Enums & Options/DownOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownOptions.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 5/28/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import libcmark
11 |
12 | public struct DownOptions: OptionSet {
13 |
14 | // MARK: - Properties
15 |
16 | public let rawValue: Int32
17 |
18 | // MARK: - Life cycle
19 |
20 | public init(rawValue: Int32) { self.rawValue = rawValue }
21 |
22 | /// Default options.
23 |
24 | public static let `default` = DownOptions(rawValue: CMARK_OPT_DEFAULT)
25 |
26 | // MARK: - Rendering Options
27 |
28 | /// Include a `data-sourcepos` attribute on all block elements.
29 |
30 | public static let sourcePos = DownOptions(rawValue: CMARK_OPT_SOURCEPOS)
31 |
32 | /// Render `softbreak` elements as hard line breaks.
33 |
34 | public static let hardBreaks = DownOptions(rawValue: CMARK_OPT_HARDBREAKS)
35 |
36 | /// Suppress raw HTML and unsafe links (`javascript:`, `vbscript:`,
37 | /// `file:`, and `data:`, except for `image/png`, `image/gif`,
38 | /// `image/jpeg`, or `image/webp` mime types). Raw HTML is replaced
39 | /// by a placeholder HTML comment. Unsafe links are replaced by
40 | /// empty strings.
41 | ///
42 | /// Note: this is the default option as of cmark v0.29.0. Use `unsafe`
43 | /// to disable this behavior.
44 |
45 | public static let safe = DownOptions(rawValue: CMARK_OPT_SAFE)
46 |
47 | /// Render raw HTML and unsafe links (`javascript:`, `vbscript:`,
48 | /// `file:`, and `data:`, except for `image/png`, `image/gif`,
49 | /// `image/jpeg`, or `image/webp` mime types). By default,
50 | /// raw HTML is replaced by a placeholder HTML comment. Unsafe
51 | /// links are replaced by empty strings.
52 | ///
53 | /// Note: `safe` is the default as of cmark v0.29.0
54 |
55 | public static let unsafe = DownOptions(rawValue: CMARK_OPT_UNSAFE)
56 |
57 | // MARK: - Parsing Options
58 |
59 | /// Normalize tree by consolidating adjacent text nodes.
60 |
61 | public static let normalize = DownOptions(rawValue: CMARK_OPT_NORMALIZE)
62 |
63 | /// Validate UTF-8 in the input before parsing, replacing illegal
64 | /// sequences with the replacement character U+FFFD.
65 |
66 | public static let validateUTF8 = DownOptions(rawValue: CMARK_OPT_VALIDATE_UTF8)
67 |
68 | /// Convert straight quotes to curly, --- to em dashes, -- to en dashes.
69 |
70 | public static let smart = DownOptions(rawValue: CMARK_OPT_SMART)
71 |
72 | // MARK: - Combo Options
73 |
74 | /// Combines 'unsafe' and 'smart' to render raw HTML and produce smart typography.
75 |
76 | public static let smartUnsafe = DownOptions(rawValue: CMARK_OPT_SMART + CMARK_OPT_UNSAFE)
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/Sources/Down/Extensions/NSAttributedString+HTML.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSAttributedString+HTML.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 6/1/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(Linux)
10 |
11 | #if os(macOS)
12 |
13 | import AppKit
14 |
15 | #else
16 |
17 | import UIKit
18 |
19 | #endif
20 |
21 | extension NSAttributedString {
22 |
23 | /// Instantiates an attributed string with the given HTML string
24 | ///
25 | /// - Parameters:
26 | /// - htmlString: An HTML string.
27 | ///
28 | /// - Throws:
29 | /// `HTMLDataConversionError` or an instantiation error.
30 |
31 | convenience init(htmlString: String) throws {
32 | guard let data = htmlString.data(using: String.Encoding.utf8) else {
33 | throw DownErrors.htmlDataConversionError
34 | }
35 |
36 | let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
37 | .documentType: NSAttributedString.DocumentType.html,
38 | .characterEncoding: NSNumber(value: String.Encoding.utf8.rawValue)
39 | ]
40 |
41 | try self.init(data: data, options: options, documentAttributes: nil)
42 | }
43 |
44 | }
45 |
46 | #endif // !os(Linux)
47 |
--------------------------------------------------------------------------------
/Sources/Down/Extensions/String+ToHTML.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+ToHTML.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 6/1/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import libcmark
11 |
12 | extension String {
13 |
14 | /// Generates an HTML string from the contents of the string (self), which should contain CommonMark Markdown.
15 | ///
16 | /// - Parameters:
17 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
18 | /// - Returns:
19 | /// An HTML string.
20 | ///
21 | /// - Throws:
22 | /// `DownErrors` depending on the scenario.
23 |
24 | public func toHTML(_ options: DownOptions = .default) throws -> String {
25 | let ast = try DownASTRenderer.stringToAST(self, options: options)
26 | let html = try DownHTMLRenderer.astToHTML(ast, options: options)
27 | cmark_node_free(ast)
28 | return html
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/Sources/Down/Renderers/DownASTRenderable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownASTRenderable.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 5/31/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import libcmark
11 |
12 | public protocol DownASTRenderable: DownRenderable {
13 |
14 | func toAST(_ options: DownOptions) throws -> CMarkNode
15 |
16 | }
17 |
18 | extension DownASTRenderable {
19 |
20 | /// Generates an abstract syntax tree from the `markdownString` property.
21 | ///
22 | /// - Parametera:
23 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
24 | ///
25 | /// - Returns:
26 | /// An abstract syntax tree representation of the Markdown input.
27 | ///
28 | /// - Throws:
29 | /// `MarkdownToASTError` if conversion fails.
30 |
31 | public func toAST(_ options: DownOptions = .default) throws -> CMarkNode {
32 | return try DownASTRenderer.stringToAST(markdownString, options: options)
33 | }
34 |
35 | /// Parses the `markdownString` property into an abstract syntax tree and returns the root `Document` node.
36 | ///
37 | /// - Parameters:
38 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
39 | ///
40 | /// - Returns:
41 | /// The root Document node for the abstract syntax tree representation of the Markdown input.
42 | ///
43 | /// - Throws:
44 | /// `MarkdownToASTError` if conversion fails.
45 |
46 | public func toDocument(_ options: DownOptions = .default) throws -> Document {
47 | let tree = try toAST(options)
48 |
49 | guard tree.type == CMARK_NODE_DOCUMENT else {
50 | throw DownErrors.astRenderingError
51 | }
52 |
53 | return Document(cmarkNode: tree)
54 | }
55 |
56 | }
57 |
58 | public struct DownASTRenderer {
59 |
60 | /// Generates an abstract syntax tree from the given CommonMark Markdown string.
61 | ///
62 | /// **Important:** It is the caller's responsibility to call `cmark_node_free(ast)` on the returned value.
63 | ///
64 | /// - Parameters:
65 | /// - string: A string containing CommonMark Markdown.
66 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
67 | ///
68 | /// - Returns:
69 | /// An abstract syntax tree representation of the Markdown input.
70 | ///
71 | /// - Throws:
72 | /// `MarkdownToASTError` if conversion fails.
73 | public static func stringToAST(_ string: String, options: DownOptions = .default) throws -> CMarkNode {
74 | var tree: CMarkNode?
75 |
76 | string.withCString {
77 | let stringLength = Int(strlen($0))
78 | tree = cmark_parse_document($0, stringLength, options.rawValue)
79 | }
80 |
81 | guard let ast = tree else {
82 | throw DownErrors.markdownToASTError
83 | }
84 |
85 | return ast
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/Sources/Down/Renderers/DownAttributedStringRenderable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownAttributedStringRenderable.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 6/1/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if !os(Linux)
10 |
11 | import Foundation
12 | import libcmark
13 |
14 | public protocol DownAttributedStringRenderable: DownHTMLRenderable, DownASTRenderable {
15 |
16 | func toAttributedString(_ options: DownOptions, stylesheet: String?) throws -> NSAttributedString
17 | func toAttributedString(_ options: DownOptions, styler: Styler) throws -> NSAttributedString
18 |
19 | }
20 |
21 | extension DownAttributedStringRenderable {
22 |
23 | /// Generates an `NSAttributedString` from the `markdownString` property.
24 | ///
25 | /// **Note:** The attributed string is constructed and rendered via WebKit from html generated from the
26 | /// abstract syntax tree. This process is not background safe and must be executed on the main
27 | /// thread. Additionally, it may be slow to render. For an efficient background safe render,
28 | /// use the `toAttributedString(options: styler:)` method below.
29 | ///
30 | /// - Parameters:
31 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
32 | /// - stylesheet: a `String` to use as the CSS stylesheet when rendering, defaulting
33 | /// to a style that uses the `NSAttributedString` default font.
34 | ///
35 | /// - Returns:
36 | /// An `NSAttributedString`.
37 | /// - Throws:
38 | /// `DownErrors` depending on the scenario.
39 |
40 | public func toAttributedString(_ options: DownOptions = .default,
41 | stylesheet: String? = nil) throws -> NSAttributedString {
42 |
43 | let html = try self.toHTML(options)
44 | let defaultStylesheet = "* {font-family: Helvetica } code, pre { font-family: Menlo }"
45 | return try NSAttributedString(htmlString: "" + html)
46 | }
47 |
48 | /// Generates an `NSAttributedString` from the `markdownString` property.
49 | ///
50 | /// **Note:** The attributed string is constructed directly by traversing the abstract syntax tree. It is
51 | /// much faster than the `toAttributedString(options: stylesheet)` method and it can be also be
52 | /// rendered in a background thread.
53 | ///
54 | /// - Parameters:
55 | /// - options: `DownOptions` to modify parsing or rendering.
56 | /// - styler: a class/struct conforming to `Styler` to use when rendering the various
57 | /// elements of the attributed string
58 | ///
59 | /// - Returns:
60 | /// An `NSAttributedString`.
61 | ///
62 | /// - Throws:
63 | /// `DownErrors` depending on the scenario.
64 |
65 | public func toAttributedString(_ options: DownOptions = .default, styler: Styler) throws -> NSAttributedString {
66 | let document = try self.toDocument(options)
67 | let visitor = AttributedStringVisitor(styler: styler, options: options)
68 | return document.accept(visitor)
69 | }
70 |
71 | }
72 |
73 | #endif // !os(Linux)
74 |
--------------------------------------------------------------------------------
/Sources/Down/Renderers/DownCommonMarkRenderable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownCommonMarkRenderable.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 5/31/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import libcmark
11 |
12 | public protocol DownCommonMarkRenderable: DownRenderable {
13 |
14 | func toCommonMark(_ options: DownOptions, width: Int32) throws -> String
15 |
16 | }
17 |
18 | extension DownCommonMarkRenderable {
19 |
20 | /// Generates a CommonMark Markdown string from the `markdownString` property.
21 | ///
22 | /// - Parameters:
23 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
24 | /// - width: The width to break on, defaulting to 0.
25 | ///
26 | /// - Returns:
27 | /// A CommonMark Markdown string.
28 | ///
29 | /// - Throws:
30 | /// `DownErrors` depending on the scenario.
31 |
32 | public func toCommonMark(_ options: DownOptions = .default, width: Int32 = 0) throws -> String {
33 | let ast = try DownASTRenderer.stringToAST(markdownString, options: options)
34 | let commonMark = try DownCommonMarkRenderer.astToCommonMark(ast, options: options, width: width)
35 | cmark_node_free(ast)
36 | return commonMark
37 | }
38 |
39 | }
40 |
41 | public struct DownCommonMarkRenderer {
42 |
43 | /// Generates a CommonMark Markdown string from the given abstract syntax tree.
44 | ///
45 | /// **Note:** caller is responsible for calling `cmark_node_free(ast)` after this returns.
46 | ///
47 | /// - Parameters:
48 | /// - ast: The `cmark_node` representing the abstract syntax tree.
49 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
50 | /// - width: The width to break on, defaulting to 0.
51 | ///
52 | /// - Returns:
53 | /// A CommonMark Markdown string.
54 | ///
55 | /// - Throws:
56 | /// `ASTRenderingError` if the AST could not be converted.
57 |
58 | public static func astToCommonMark(_ ast: CMarkNode,
59 | options: DownOptions = .default,
60 | width: Int32 = 0) throws -> String {
61 |
62 | guard let cCommonMarkString = cmark_render_commonmark(ast, options.rawValue, width) else {
63 | throw DownErrors.astRenderingError
64 | }
65 |
66 | defer {
67 | free(cCommonMarkString)
68 | }
69 |
70 | guard let commonMarkString = String(cString: cCommonMarkString, encoding: String.Encoding.utf8) else {
71 | throw DownErrors.astRenderingError
72 | }
73 |
74 | return commonMarkString
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/Sources/Down/Renderers/DownGroffRenderable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownGroffRenderable.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 5/31/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import libcmark
11 |
12 | public protocol DownGroffRenderable: DownRenderable {
13 |
14 | func toGroff(_ options: DownOptions, width: Int32) throws -> String
15 |
16 | }
17 |
18 | extension DownGroffRenderable {
19 |
20 | /// Generates a groff man string from the `markdownString` property.
21 | ///
22 | /// - Parameters:
23 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
24 | /// - width: The width to break on, defaulting to 0.
25 | ///
26 | /// - Returns:
27 | /// A groff man string.
28 | ///
29 | /// - Throws:
30 | /// `DownErrors` depending on the scenario.
31 |
32 | public func toGroff(_ options: DownOptions = .default, width: Int32 = 0) throws -> String {
33 | let ast = try DownASTRenderer.stringToAST(markdownString, options: options)
34 | let groff = try DownGroffRenderer.astToGroff(ast, options: options, width: width)
35 | cmark_node_free(ast)
36 | return groff
37 | }
38 |
39 | }
40 |
41 | public struct DownGroffRenderer {
42 |
43 | /// Generates a groff man string from the given abstract syntax tree.
44 | ///
45 | /// **Note:** caller is responsible for calling `cmark_node_free(ast)` after this returns.
46 | ///
47 | /// - Parameters:
48 | /// - ast: The `cmark_node` representing the abstract syntax tree.
49 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
50 | /// - width: The width to break on, defaulting to 0.
51 | ///
52 | /// - Returns:
53 | /// A groff man string.
54 | ///
55 | /// - Throws:
56 | /// `ASTRenderingError` if the AST could not be converted.
57 |
58 | public static func astToGroff(_ ast: CMarkNode,
59 | options: DownOptions = .default,
60 | width: Int32 = 0) throws -> String {
61 |
62 | guard let cGroffString = cmark_render_man(ast, options.rawValue, width) else {
63 | throw DownErrors.astRenderingError
64 | }
65 |
66 | defer {
67 | free(cGroffString)
68 | }
69 |
70 | guard let groffString = String(cString: cGroffString, encoding: String.Encoding.utf8) else {
71 | throw DownErrors.astRenderingError
72 | }
73 |
74 | return groffString
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/Sources/Down/Renderers/DownHTMLRenderable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownHTMLRenderable.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 5/28/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import libcmark
11 |
12 | public protocol DownHTMLRenderable: DownRenderable {
13 |
14 | func toHTML(_ options: DownOptions) throws -> String
15 |
16 | }
17 |
18 | extension DownHTMLRenderable {
19 |
20 | /// Generates an HTML string from the `markdownString` property.
21 | ///
22 | /// - Parameters:
23 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
24 | ///
25 | /// - Returns:
26 | /// An HTML string.
27 | ///
28 | /// - Throws:
29 | /// `DownErrors` depending on the scenario.
30 |
31 | public func toHTML(_ options: DownOptions = .default) throws -> String {
32 | return try markdownString.toHTML(options)
33 | }
34 |
35 | }
36 |
37 | public struct DownHTMLRenderer {
38 |
39 | /// Generates an HTML string from the given abstract syntax tree.
40 | ///
41 | /// **Note:** caller is responsible for calling `cmark_node_free(ast)` after this returns.
42 | ///
43 | /// - Parameters:
44 | /// - ast: The `cmark_node` representing the abstract syntax tree.
45 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
46 | ///
47 | /// - Returns:
48 | /// An HTML string.
49 | ///
50 | /// - Throws:
51 | /// `ASTRenderingError` if the AST could not be converted.
52 |
53 | public static func astToHTML(_ ast: CMarkNode, options: DownOptions = .default) throws -> String {
54 | guard let cHTMLString = cmark_render_html(ast, options.rawValue) else {
55 | throw DownErrors.astRenderingError
56 | }
57 |
58 | defer {
59 | free(cHTMLString)
60 | }
61 |
62 | guard let htmlString = String(cString: cHTMLString, encoding: String.Encoding.utf8) else {
63 | throw DownErrors.astRenderingError
64 | }
65 |
66 | return htmlString
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/Sources/Down/Renderers/DownLaTeXRenderable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownLaTeXRenderable.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 5/31/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import libcmark
11 |
12 | public protocol DownLaTeXRenderable: DownRenderable {
13 |
14 | func toLaTeX(_ options: DownOptions, width: Int32) throws -> String
15 |
16 | }
17 |
18 | extension DownLaTeXRenderable {
19 |
20 | /// Generates a LaTeX string from the `markdownString` property.
21 | ///
22 | /// - Parameters:
23 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
24 | /// - width: The width to break on, defaulting to 0.
25 | ///
26 | /// - Returns:
27 | /// A LaTeX string.
28 | ///
29 | /// - Throws:
30 | /// `DownErrors` depending on the scenario.
31 |
32 | public func toLaTeX(_ options: DownOptions = .default, width: Int32 = 0) throws -> String {
33 | let ast = try DownASTRenderer.stringToAST(markdownString, options: options)
34 | let latex = try DownLaTeXRenderer.astToLaTeX(ast, options: options, width: width)
35 | cmark_node_free(ast)
36 | return latex
37 | }
38 |
39 | }
40 |
41 | public struct DownLaTeXRenderer {
42 |
43 | /// Generates a LaTeX string from the given abstract syntax tree.
44 | ///
45 | /// **Note:** caller is responsible for calling `cmark_node_free(ast)` after this returns.
46 | ///
47 | /// - Parameters:
48 | /// - ast: The `cmark_node` representing the abstract syntax tree.
49 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
50 | /// - width: The width to break on, defaulting to 0.
51 | ///
52 | /// - Returns:
53 | /// A LaTeX string.
54 | ///
55 | /// - Throws:
56 | /// `ASTRenderingError` if the AST could not be converted.
57 |
58 | public static func astToLaTeX(_ ast: CMarkNode,
59 | options: DownOptions = .default,
60 | width: Int32 = 0) throws -> String {
61 |
62 | guard let cLatexString = cmark_render_latex(ast, options.rawValue, width) else {
63 | throw DownErrors.astRenderingError
64 | }
65 |
66 | defer {
67 | free(cLatexString)
68 | }
69 |
70 | guard let latexString = String(cString: cLatexString, encoding: String.Encoding.utf8) else {
71 | throw DownErrors.astRenderingError
72 | }
73 |
74 | return latexString
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/Sources/Down/Renderers/DownRenderable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownRenderable.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 5/28/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public protocol DownRenderable {
12 |
13 | /// A string containing CommonMark Markdown.
14 |
15 | var markdownString: String { get set }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/Down/Renderers/DownXMLRenderable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownXMLRenderable.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 5/31/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import libcmark
11 |
12 | public protocol DownXMLRenderable: DownRenderable {
13 |
14 | func toXML(_ options: DownOptions) throws -> String
15 |
16 | }
17 |
18 | extension DownXMLRenderable {
19 |
20 | /// Generates an XML string from the `markdownString` property.
21 | ///
22 | /// - Parameters:
23 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
24 | ///
25 | /// - Returns:
26 | /// An XML string.
27 | ///
28 | /// - Throws:
29 | /// `DownErrors` depending on the scenario.
30 |
31 | public func toXML(_ options: DownOptions = .default) throws -> String {
32 | let ast = try DownASTRenderer.stringToAST(markdownString, options: options)
33 | let xml = try DownXMLRenderer.astToXML(ast, options: options)
34 | cmark_node_free(ast)
35 | return xml
36 | }
37 |
38 | }
39 |
40 | public struct DownXMLRenderer {
41 |
42 | /// Generates an XML string from the given abstract syntax tree
43 | ///
44 | /// **Note:** caller is responsible for calling `cmark_node_free(ast)` after this returns.
45 | ///
46 | /// - Parameters:
47 | /// - ast: The `cmark_node` representing the abstract syntax tree.
48 | /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default`.
49 | ///
50 | /// - Returns:
51 | /// An XML string.
52 | ///
53 | /// - Throws:
54 | /// `ASTRenderingError` if the AST could not be converted.
55 |
56 | public static func astToXML(_ ast: CMarkNode, options: DownOptions = .default) throws -> String {
57 | guard let cXMLString = cmark_render_xml(ast, options.rawValue) else {
58 | throw DownErrors.astRenderingError
59 | }
60 |
61 | defer {
62 | free(cXMLString)
63 | }
64 |
65 | guard let xmlString = String(cString: cXMLString, encoding: String.Encoding.utf8) else {
66 | throw DownErrors.astRenderingError
67 | }
68 |
69 | return xmlString
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/Sources/Down/Resources/DownView (macOS).bundle/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
26 |
27 | DOWN_HTML
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Sources/Down/Resources/DownView (macOS).bundle/js/down.js:
--------------------------------------------------------------------------------
1 | hljs.initHighlightingOnLoad();
2 |
--------------------------------------------------------------------------------
/Sources/Down/Resources/DownView.bundle/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | DOWN_HTML
13 |
14 |
--------------------------------------------------------------------------------
/Sources/Down/Resources/DownView.bundle/js/down.js:
--------------------------------------------------------------------------------
1 | hljs.initHighlightingOnLoad();
--------------------------------------------------------------------------------
/Sources/Down/Views/BundleHelper.swift:
--------------------------------------------------------------------------------
1 | import class Foundation.Bundle
2 |
3 | // This helps us find the bundle when imported through the Swift Package Manager.
4 |
5 | private class BundleFinder {}
6 |
7 | extension Foundation.Bundle {
8 |
9 | /// Returns the resource bundle associated with the current Swift module.
10 |
11 | static var moduleBundle: Bundle? = {
12 | let bundleName = "Down_Down"
13 |
14 | let candidates = [
15 | // Bundle should be present here when the package is linked into an App.
16 | Bundle.main.resourceURL,
17 |
18 | // Bundle should be present here when the package is linked into a framework.
19 | Bundle(for: BundleFinder.self).resourceURL,
20 |
21 | // For command-line tools.
22 | Bundle.main.bundleURL
23 | ]
24 |
25 | for candidate in candidates {
26 | let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
27 | if let bundle = bundlePath.flatMap(Bundle.init(url:)) {
28 | return bundle
29 | }
30 | }
31 | return nil
32 | }()
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/cmark/buffer.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_BUFFER_H
2 | #define CMARK_BUFFER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include "config.h"
10 | #include "cmark.h"
11 |
12 | #ifdef __cplusplus
13 | extern "C" {
14 | #endif
15 |
16 | typedef int32_t bufsize_t;
17 |
18 | typedef struct {
19 | cmark_mem *mem;
20 | unsigned char *ptr;
21 | bufsize_t asize, size;
22 | } cmark_strbuf;
23 |
24 | extern unsigned char cmark_strbuf__initbuf[];
25 |
26 | #define CMARK_BUF_INIT(mem) \
27 | { mem, cmark_strbuf__initbuf, 0, 0 }
28 |
29 | /**
30 | * Initialize a cmark_strbuf structure.
31 | *
32 | * For the cases where CMARK_BUF_INIT cannot be used to do static
33 | * initialization.
34 | */
35 | void cmark_strbuf_init(cmark_mem *mem, cmark_strbuf *buf,
36 | bufsize_t initial_size);
37 |
38 | /**
39 | * Grow the buffer to hold at least `target_size` bytes.
40 | */
41 | void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size);
42 |
43 | void cmark_strbuf_free(cmark_strbuf *buf);
44 | void cmark_strbuf_swap(cmark_strbuf *buf_a, cmark_strbuf *buf_b);
45 |
46 | bufsize_t cmark_strbuf_len(const cmark_strbuf *buf);
47 |
48 | int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b);
49 |
50 | unsigned char *cmark_strbuf_detach(cmark_strbuf *buf);
51 | void cmark_strbuf_copy_cstr(char *data, bufsize_t datasize,
52 | const cmark_strbuf *buf);
53 |
54 | static CMARK_INLINE const char *cmark_strbuf_cstr(const cmark_strbuf *buf) {
55 | return (char *)buf->ptr;
56 | }
57 |
58 | #define cmark_strbuf_at(buf, n) ((buf)->ptr[n])
59 |
60 | void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data,
61 | bufsize_t len);
62 | void cmark_strbuf_sets(cmark_strbuf *buf, const char *string);
63 | void cmark_strbuf_putc(cmark_strbuf *buf, int c);
64 | void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data,
65 | bufsize_t len);
66 | void cmark_strbuf_puts(cmark_strbuf *buf, const char *string);
67 | void cmark_strbuf_clear(cmark_strbuf *buf);
68 |
69 | bufsize_t cmark_strbuf_strchr(const cmark_strbuf *buf, int c, bufsize_t pos);
70 | bufsize_t cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, bufsize_t pos);
71 | void cmark_strbuf_drop(cmark_strbuf *buf, bufsize_t n);
72 | void cmark_strbuf_truncate(cmark_strbuf *buf, bufsize_t len);
73 | void cmark_strbuf_rtrim(cmark_strbuf *buf);
74 | void cmark_strbuf_trim(cmark_strbuf *buf);
75 | void cmark_strbuf_normalize_whitespace(cmark_strbuf *s);
76 | void cmark_strbuf_unescape(cmark_strbuf *s);
77 |
78 | #ifdef __cplusplus
79 | }
80 | #endif
81 |
82 | #endif
83 |
--------------------------------------------------------------------------------
/Sources/cmark/chunk.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_CHUNK_H
2 | #define CMARK_CHUNK_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "cmark.h"
8 | #include "buffer.h"
9 | #include "cmark_ctype.h"
10 |
11 | #define CMARK_CHUNK_EMPTY \
12 | { NULL, 0, 0 }
13 |
14 | typedef struct {
15 | unsigned char *data;
16 | bufsize_t len;
17 | bufsize_t alloc; // also implies a NULL-terminated string
18 | } cmark_chunk;
19 |
20 | static CMARK_INLINE void cmark_chunk_free(cmark_mem *mem, cmark_chunk *c) {
21 | if (c->alloc)
22 | mem->free(c->data);
23 |
24 | c->data = NULL;
25 | c->alloc = 0;
26 | c->len = 0;
27 | }
28 |
29 | static CMARK_INLINE void cmark_chunk_ltrim(cmark_chunk *c) {
30 | assert(!c->alloc);
31 |
32 | while (c->len && cmark_isspace(c->data[0])) {
33 | c->data++;
34 | c->len--;
35 | }
36 | }
37 |
38 | static CMARK_INLINE void cmark_chunk_rtrim(cmark_chunk *c) {
39 | assert(!c->alloc);
40 |
41 | while (c->len > 0) {
42 | if (!cmark_isspace(c->data[c->len - 1]))
43 | break;
44 |
45 | c->len--;
46 | }
47 | }
48 |
49 | static CMARK_INLINE void cmark_chunk_trim(cmark_chunk *c) {
50 | cmark_chunk_ltrim(c);
51 | cmark_chunk_rtrim(c);
52 | }
53 |
54 | static CMARK_INLINE bufsize_t cmark_chunk_strchr(cmark_chunk *ch, int c,
55 | bufsize_t offset) {
56 | const unsigned char *p =
57 | (unsigned char *)memchr(ch->data + offset, c, ch->len - offset);
58 | return p ? (bufsize_t)(p - ch->data) : ch->len;
59 | }
60 |
61 | static CMARK_INLINE const char *cmark_chunk_to_cstr(cmark_mem *mem,
62 | cmark_chunk *c) {
63 | unsigned char *str;
64 |
65 | if (c->alloc) {
66 | return (char *)c->data;
67 | }
68 | str = (unsigned char *)mem->calloc(c->len + 1, 1);
69 | if (c->len > 0) {
70 | memcpy(str, c->data, c->len);
71 | }
72 | str[c->len] = 0;
73 | c->data = str;
74 | c->alloc = 1;
75 |
76 | return (char *)str;
77 | }
78 |
79 | static CMARK_INLINE void cmark_chunk_set_cstr(cmark_mem *mem, cmark_chunk *c,
80 | const char *str) {
81 | unsigned char *old = c->alloc ? c->data : NULL;
82 | if (str == NULL) {
83 | c->len = 0;
84 | c->data = NULL;
85 | c->alloc = 0;
86 | } else {
87 | c->len = (bufsize_t)strlen(str);
88 | c->data = (unsigned char *)mem->calloc(c->len + 1, 1);
89 | c->alloc = 1;
90 | memcpy(c->data, str, c->len + 1);
91 | }
92 | if (old != NULL) {
93 | mem->free(old);
94 | }
95 | }
96 |
97 | static CMARK_INLINE cmark_chunk cmark_chunk_literal(const char *data) {
98 | bufsize_t len = data ? (bufsize_t)strlen(data) : 0;
99 | cmark_chunk c = {(unsigned char *)data, len, 0};
100 | return c;
101 | }
102 |
103 | static CMARK_INLINE cmark_chunk cmark_chunk_dup(const cmark_chunk *ch,
104 | bufsize_t pos, bufsize_t len) {
105 | cmark_chunk c = {ch->data + pos, len, 0};
106 | return c;
107 | }
108 |
109 | static CMARK_INLINE cmark_chunk cmark_chunk_buf_detach(cmark_strbuf *buf) {
110 | cmark_chunk c;
111 |
112 | c.len = buf->size;
113 | c.data = cmark_strbuf_detach(buf);
114 | c.alloc = 1;
115 |
116 | return c;
117 | }
118 |
119 | #endif
120 |
--------------------------------------------------------------------------------
/Sources/cmark/cmark.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "node.h"
5 | #include "houdini.h"
6 | #include "cmark.h"
7 | #include "buffer.h"
8 |
9 | int cmark_version() { return CMARK_VERSION; }
10 |
11 | const char *cmark_version_string() { return CMARK_VERSION_STRING; }
12 |
13 | static void *xcalloc(size_t nmem, size_t size) {
14 | void *ptr = calloc(nmem, size);
15 | if (!ptr) {
16 | fprintf(stderr, "[cmark] calloc returned null pointer, aborting\n");
17 | abort();
18 | }
19 | return ptr;
20 | }
21 |
22 | static void *xrealloc(void *ptr, size_t size) {
23 | void *new_ptr = realloc(ptr, size);
24 | if (!new_ptr) {
25 | fprintf(stderr, "[cmark] realloc returned null pointer, aborting\n");
26 | abort();
27 | }
28 | return new_ptr;
29 | }
30 |
31 | cmark_mem DEFAULT_MEM_ALLOCATOR = {xcalloc, xrealloc, free};
32 |
33 | char *cmark_markdown_to_html(const char *text, size_t len, int options) {
34 | cmark_node *doc;
35 | char *result;
36 |
37 | doc = cmark_parse_document(text, len, options);
38 |
39 | result = cmark_render_html(doc, options);
40 | cmark_node_free(doc);
41 |
42 | return result;
43 | }
44 |
--------------------------------------------------------------------------------
/Sources/cmark/cmark_ctype.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "cmark_ctype.h"
4 |
5 | /** 1 = space, 2 = punct, 3 = digit, 4 = alpha, 0 = other
6 | */
7 | static const uint8_t cmark_ctype_class[256] = {
8 | /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
9 | /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
10 | /* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
11 | /* 2 */ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
12 | /* 3 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2,
13 | /* 4 */ 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
14 | /* 5 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2,
15 | /* 6 */ 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
16 | /* 7 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 0,
17 | /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18 | /* 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
19 | /* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
20 | /* b */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
21 | /* c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22 | /* d */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
23 | /* e */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24 | /* f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
25 |
26 | /**
27 | * Returns 1 if c is a "whitespace" character as defined by the spec.
28 | */
29 | int cmark_isspace(char c) { return cmark_ctype_class[(uint8_t)c] == 1; }
30 |
31 | /**
32 | * Returns 1 if c is an ascii punctuation character.
33 | */
34 | int cmark_ispunct(char c) { return cmark_ctype_class[(uint8_t)c] == 2; }
35 |
36 | int cmark_isalnum(char c) {
37 | uint8_t result;
38 | result = cmark_ctype_class[(uint8_t)c];
39 | return (result == 3 || result == 4);
40 | }
41 |
42 | int cmark_isdigit(char c) { return cmark_ctype_class[(uint8_t)c] == 3; }
43 |
44 | int cmark_isalpha(char c) { return cmark_ctype_class[(uint8_t)c] == 4; }
45 |
--------------------------------------------------------------------------------
/Sources/cmark/cmark_ctype.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_CMARK_CTYPE_H
2 | #define CMARK_CMARK_CTYPE_H
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | /** Locale-independent versions of functions from ctype.h.
9 | * We want cmark to behave the same no matter what the system locale.
10 | */
11 |
12 | int cmark_isspace(char c);
13 |
14 | int cmark_ispunct(char c);
15 |
16 | int cmark_isalnum(char c);
17 |
18 | int cmark_isdigit(char c);
19 |
20 | int cmark_isalpha(char c);
21 |
22 | #ifdef __cplusplus
23 | }
24 | #endif
25 |
26 | #endif
27 |
--------------------------------------------------------------------------------
/Sources/cmark/cmark_export.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef CMARK_EXPORT_H
3 | #define CMARK_EXPORT_H
4 |
5 | #ifdef CMARK_STATIC_DEFINE
6 | # define CMARK_EXPORT
7 | # define CMARK_NO_EXPORT
8 | #else
9 | # ifndef CMARK_EXPORT
10 | # ifdef libcmark_EXPORTS
11 | /* We are building this library */
12 | # define CMARK_EXPORT __attribute__((visibility("default")))
13 | # else
14 | /* We are using this library */
15 | # define CMARK_EXPORT __attribute__((visibility("default")))
16 | # endif
17 | # endif
18 |
19 | # ifndef CMARK_NO_EXPORT
20 | # define CMARK_NO_EXPORT __attribute__((visibility("hidden")))
21 | # endif
22 | #endif
23 |
24 | #ifndef CMARK_DEPRECATED
25 | # define CMARK_DEPRECATED __attribute__ ((__deprecated__))
26 | #endif
27 |
28 | #ifndef CMARK_DEPRECATED_EXPORT
29 | # define CMARK_DEPRECATED_EXPORT CMARK_EXPORT CMARK_DEPRECATED
30 | #endif
31 |
32 | #ifndef CMARK_DEPRECATED_NO_EXPORT
33 | # define CMARK_DEPRECATED_NO_EXPORT CMARK_NO_EXPORT CMARK_DEPRECATED
34 | #endif
35 |
36 | #if 0 /* DEFINE_NO_DEPRECATED */
37 | # ifndef CMARK_NO_DEPRECATED
38 | # define CMARK_NO_DEPRECATED
39 | # endif
40 | #endif
41 |
42 | #endif /* CMARK_EXPORT_H */
43 |
--------------------------------------------------------------------------------
/Sources/cmark/cmark_version.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_VERSION_H
2 | #define CMARK_VERSION_H
3 |
4 | #define CMARK_VERSION ((0 << 16) | (29 << 8) | 0)
5 | #define CMARK_VERSION_STRING "0.29.0"
6 |
7 | #endif
8 |
--------------------------------------------------------------------------------
/Sources/cmark/config.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_CONFIG_H
2 | #define CMARK_CONFIG_H
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | #define HAVE_STDBOOL_H
9 |
10 | #ifdef HAVE_STDBOOL_H
11 | #include
12 | #elif !defined(__cplusplus)
13 | typedef char bool;
14 | #endif
15 |
16 | #define HAVE___BUILTIN_EXPECT
17 |
18 | #define HAVE___ATTRIBUTE__
19 |
20 | #ifdef HAVE___ATTRIBUTE__
21 | #define CMARK_ATTRIBUTE(list) __attribute__ (list)
22 | #else
23 | #define CMARK_ATTRIBUTE(list)
24 | #endif
25 |
26 | #ifndef CMARK_INLINE
27 | #if defined(_MSC_VER) && !defined(__cplusplus)
28 | #define CMARK_INLINE __inline
29 | #else
30 | #define CMARK_INLINE inline
31 | #endif
32 | #endif
33 |
34 | /* snprintf and vsnprintf fallbacks for MSVC before 2015,
35 | due to Valentin Milea http://stackoverflow.com/questions/2915672/
36 | */
37 |
38 | #if defined(_MSC_VER) && _MSC_VER < 1900
39 |
40 | #include
41 | #include
42 |
43 | #define snprintf c99_snprintf
44 | #define vsnprintf c99_vsnprintf
45 |
46 | CMARK_INLINE int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
47 | {
48 | int count = -1;
49 |
50 | if (size != 0)
51 | count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
52 | if (count == -1)
53 | count = _vscprintf(format, ap);
54 |
55 | return count;
56 | }
57 |
58 | CMARK_INLINE int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
59 | {
60 | int count;
61 | va_list ap;
62 |
63 | va_start(ap, format);
64 | count = c99_vsnprintf(outBuf, size, format, ap);
65 | va_end(ap);
66 |
67 | return count;
68 | }
69 |
70 | #endif
71 |
72 | #ifdef __cplusplus
73 | }
74 | #endif
75 |
76 | #endif
77 |
--------------------------------------------------------------------------------
/Sources/cmark/houdini.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_HOUDINI_H
2 | #define CMARK_HOUDINI_H
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | #include
9 | #include "config.h"
10 | #include "buffer.h"
11 |
12 | #ifdef HAVE___BUILTIN_EXPECT
13 | #define likely(x) __builtin_expect((x), 1)
14 | #define unlikely(x) __builtin_expect((x), 0)
15 | #else
16 | #define likely(x) (x)
17 | #define unlikely(x) (x)
18 | #endif
19 |
20 | #ifdef HOUDINI_USE_LOCALE
21 | #define _isxdigit(c) isxdigit(c)
22 | #define _isdigit(c) isdigit(c)
23 | #else
24 | /*
25 | * Helper _isdigit methods -- do not trust the current locale
26 | * */
27 | #define _isxdigit(c) (strchr("0123456789ABCDEFabcdef", (c)) != NULL)
28 | #define _isdigit(c) ((c) >= '0' && (c) <= '9')
29 | #endif
30 |
31 | #define HOUDINI_ESCAPED_SIZE(x) (((x)*12) / 10)
32 | #define HOUDINI_UNESCAPED_SIZE(x) (x)
33 |
34 | extern bufsize_t houdini_unescape_ent(cmark_strbuf *ob, const uint8_t *src,
35 | bufsize_t size);
36 | extern int houdini_escape_html(cmark_strbuf *ob, const uint8_t *src,
37 | bufsize_t size);
38 | extern int houdini_escape_html0(cmark_strbuf *ob, const uint8_t *src,
39 | bufsize_t size, int secure);
40 | extern int houdini_unescape_html(cmark_strbuf *ob, const uint8_t *src,
41 | bufsize_t size);
42 | extern void houdini_unescape_html_f(cmark_strbuf *ob, const uint8_t *src,
43 | bufsize_t size);
44 | extern int houdini_escape_href(cmark_strbuf *ob, const uint8_t *src,
45 | bufsize_t size);
46 |
47 | #ifdef __cplusplus
48 | }
49 | #endif
50 |
51 | #endif
52 |
--------------------------------------------------------------------------------
/Sources/cmark/houdini_href_e.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "houdini.h"
6 |
7 | /*
8 | * The following characters will not be escaped:
9 | *
10 | * -_.+!*'(),%#@?=;:/,+&$ alphanum
11 | *
12 | * Note that this character set is the addition of:
13 | *
14 | * - The characters which are safe to be in an URL
15 | * - The characters which are *not* safe to be in
16 | * an URL because they are RESERVED characters.
17 | *
18 | * We assume (lazily) that any RESERVED char that
19 | * appears inside an URL is actually meant to
20 | * have its native function (i.e. as an URL
21 | * component/separator) and hence needs no escaping.
22 | *
23 | * There are two exceptions: the chacters & (amp)
24 | * and ' (single quote) do not appear in the table.
25 | * They are meant to appear in the URL as components,
26 | * yet they require special HTML-entity escaping
27 | * to generate valid HTML markup.
28 | *
29 | * All other characters will be escaped to %XX.
30 | *
31 | */
32 | static const char HREF_SAFE[] = {
33 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
34 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
35 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
36 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
37 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
38 | 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
39 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
40 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
42 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44 | };
45 |
46 | int houdini_escape_href(cmark_strbuf *ob, const uint8_t *src, bufsize_t size) {
47 | static const uint8_t hex_chars[] = "0123456789ABCDEF";
48 | bufsize_t i = 0, org;
49 | uint8_t hex_str[3];
50 |
51 | hex_str[0] = '%';
52 |
53 | while (i < size) {
54 | org = i;
55 | while (i < size && HREF_SAFE[src[i]] != 0)
56 | i++;
57 |
58 | if (likely(i > org))
59 | cmark_strbuf_put(ob, src + org, i - org);
60 |
61 | /* escaping */
62 | if (i >= size)
63 | break;
64 |
65 | switch (src[i]) {
66 | /* amp appears all the time in URLs, but needs
67 | * HTML-entity escaping to be inside an href */
68 | case '&':
69 | cmark_strbuf_puts(ob, "&");
70 | break;
71 |
72 | /* the single quote is a valid URL character
73 | * according to the standard; it needs HTML
74 | * entity escaping too */
75 | case '\'':
76 | cmark_strbuf_puts(ob, "'");
77 | break;
78 |
79 | /* the space can be escaped to %20 or a plus
80 | * sign. we're going with the generic escape
81 | * for now. the plus thing is more commonly seen
82 | * when building GET strings */
83 | #if 0
84 | case ' ':
85 | cmark_strbuf_putc(ob, '+');
86 | break;
87 | #endif
88 |
89 | /* every other character goes with a %XX escaping */
90 | default:
91 | hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];
92 | hex_str[2] = hex_chars[src[i] & 0xF];
93 | cmark_strbuf_put(ob, hex_str, 3);
94 | }
95 |
96 | i++;
97 | }
98 |
99 | return 1;
100 | }
101 |
--------------------------------------------------------------------------------
/Sources/cmark/houdini_html_e.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "houdini.h"
6 |
7 | /**
8 | * According to the OWASP rules:
9 | *
10 | * & --> &
11 | * < --> <
12 | * > --> >
13 | * " --> "
14 | * ' --> ' ' is not recommended
15 | * / --> / forward slash is included as it helps end an HTML entity
16 | *
17 | */
18 | static const char HTML_ESCAPE_TABLE[] = {
19 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
20 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4,
21 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0,
22 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
23 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
25 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
26 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
27 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
28 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30 | };
31 |
32 | static const char *HTML_ESCAPES[] = {"", """, "&", "'",
33 | "/", "<", ">"};
34 |
35 | int houdini_escape_html0(cmark_strbuf *ob, const uint8_t *src, bufsize_t size,
36 | int secure) {
37 | bufsize_t i = 0, org, esc = 0;
38 |
39 | while (i < size) {
40 | org = i;
41 | while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0)
42 | i++;
43 |
44 | if (i > org)
45 | cmark_strbuf_put(ob, src + org, i - org);
46 |
47 | /* escaping */
48 | if (unlikely(i >= size))
49 | break;
50 |
51 | /* The forward slash is only escaped in secure mode */
52 | if ((src[i] == '/' || src[i] == '\'') && !secure) {
53 | cmark_strbuf_putc(ob, src[i]);
54 | } else {
55 | cmark_strbuf_puts(ob, HTML_ESCAPES[esc]);
56 | }
57 |
58 | i++;
59 | }
60 |
61 | return 1;
62 | }
63 |
64 | int houdini_escape_html(cmark_strbuf *ob, const uint8_t *src, bufsize_t size) {
65 | return houdini_escape_html0(ob, src, size, 1);
66 | }
67 |
--------------------------------------------------------------------------------
/Sources/cmark/include/module.modulemap:
--------------------------------------------------------------------------------
1 | module libcmark [system][extern_c] {
2 | header "../node.h"
3 | export *
4 | }
5 |
--------------------------------------------------------------------------------
/Sources/cmark/inlines.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_INLINES_H
2 | #define CMARK_INLINES_H
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | #include
9 |
10 | cmark_chunk cmark_clean_url(cmark_mem *mem, cmark_chunk *url);
11 | cmark_chunk cmark_clean_title(cmark_mem *mem, cmark_chunk *title);
12 |
13 | void cmark_parse_inlines(cmark_mem *mem, cmark_node *parent,
14 | cmark_reference_map *refmap, int options);
15 |
16 | bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_chunk *input,
17 | cmark_reference_map *refmap);
18 |
19 | #ifdef __cplusplus
20 | }
21 | #endif
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/Sources/cmark/iterator.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "config.h"
5 | #include "node.h"
6 | #include "cmark.h"
7 | #include "iterator.h"
8 |
9 | static const int S_leaf_mask =
10 | (1 << CMARK_NODE_HTML_BLOCK) | (1 << CMARK_NODE_THEMATIC_BREAK) |
11 | (1 << CMARK_NODE_CODE_BLOCK) | (1 << CMARK_NODE_TEXT) |
12 | (1 << CMARK_NODE_SOFTBREAK) | (1 << CMARK_NODE_LINEBREAK) |
13 | (1 << CMARK_NODE_CODE) | (1 << CMARK_NODE_HTML_INLINE);
14 |
15 | cmark_iter *cmark_iter_new(cmark_node *root) {
16 | if (root == NULL) {
17 | return NULL;
18 | }
19 | cmark_mem *mem = root->content.mem;
20 | cmark_iter *iter = (cmark_iter *)mem->calloc(1, sizeof(cmark_iter));
21 | iter->mem = mem;
22 | iter->root = root;
23 | iter->cur.ev_type = CMARK_EVENT_NONE;
24 | iter->cur.node = NULL;
25 | iter->next.ev_type = CMARK_EVENT_ENTER;
26 | iter->next.node = root;
27 | return iter;
28 | }
29 |
30 | void cmark_iter_free(cmark_iter *iter) { iter->mem->free(iter); }
31 |
32 | static bool S_is_leaf(cmark_node *node) {
33 | return ((1 << node->type) & S_leaf_mask) != 0;
34 | }
35 |
36 | cmark_event_type cmark_iter_next(cmark_iter *iter) {
37 | cmark_event_type ev_type = iter->next.ev_type;
38 | cmark_node *node = iter->next.node;
39 |
40 | iter->cur.ev_type = ev_type;
41 | iter->cur.node = node;
42 |
43 | if (ev_type == CMARK_EVENT_DONE) {
44 | return ev_type;
45 | }
46 |
47 | /* roll forward to next item, setting both fields */
48 | if (ev_type == CMARK_EVENT_ENTER && !S_is_leaf(node)) {
49 | if (node->first_child == NULL) {
50 | /* stay on this node but exit */
51 | iter->next.ev_type = CMARK_EVENT_EXIT;
52 | } else {
53 | iter->next.ev_type = CMARK_EVENT_ENTER;
54 | iter->next.node = node->first_child;
55 | }
56 | } else if (node == iter->root) {
57 | /* don't move past root */
58 | iter->next.ev_type = CMARK_EVENT_DONE;
59 | iter->next.node = NULL;
60 | } else if (node->next) {
61 | iter->next.ev_type = CMARK_EVENT_ENTER;
62 | iter->next.node = node->next;
63 | } else if (node->parent) {
64 | iter->next.ev_type = CMARK_EVENT_EXIT;
65 | iter->next.node = node->parent;
66 | } else {
67 | assert(false);
68 | iter->next.ev_type = CMARK_EVENT_DONE;
69 | iter->next.node = NULL;
70 | }
71 |
72 | return ev_type;
73 | }
74 |
75 | void cmark_iter_reset(cmark_iter *iter, cmark_node *current,
76 | cmark_event_type event_type) {
77 | iter->next.ev_type = event_type;
78 | iter->next.node = current;
79 | cmark_iter_next(iter);
80 | }
81 |
82 | cmark_node *cmark_iter_get_node(cmark_iter *iter) { return iter->cur.node; }
83 |
84 | cmark_event_type cmark_iter_get_event_type(cmark_iter *iter) {
85 | return iter->cur.ev_type;
86 | }
87 |
88 | cmark_node *cmark_iter_get_root(cmark_iter *iter) { return iter->root; }
89 |
90 | void cmark_consolidate_text_nodes(cmark_node *root) {
91 | if (root == NULL) {
92 | return;
93 | }
94 | cmark_iter *iter = cmark_iter_new(root);
95 | cmark_strbuf buf = CMARK_BUF_INIT(iter->mem);
96 | cmark_event_type ev_type;
97 | cmark_node *cur, *tmp, *next;
98 |
99 | while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
100 | cur = cmark_iter_get_node(iter);
101 | if (ev_type == CMARK_EVENT_ENTER && cur->type == CMARK_NODE_TEXT &&
102 | cur->next && cur->next->type == CMARK_NODE_TEXT) {
103 | cmark_strbuf_clear(&buf);
104 | cmark_strbuf_put(&buf, cur->as.literal.data, cur->as.literal.len);
105 | tmp = cur->next;
106 | while (tmp && tmp->type == CMARK_NODE_TEXT) {
107 | cmark_iter_next(iter); // advance pointer
108 | cmark_strbuf_put(&buf, tmp->as.literal.data, tmp->as.literal.len);
109 | cur->end_column = tmp->end_column;
110 | next = tmp->next;
111 | cmark_node_free(tmp);
112 | tmp = next;
113 | }
114 | cmark_chunk_free(iter->mem, &cur->as.literal);
115 | cur->as.literal = cmark_chunk_buf_detach(&buf);
116 | }
117 | }
118 |
119 | cmark_strbuf_free(&buf);
120 | cmark_iter_free(iter);
121 | }
122 |
--------------------------------------------------------------------------------
/Sources/cmark/iterator.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_ITERATOR_H
2 | #define CMARK_ITERATOR_H
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | #include "cmark.h"
9 |
10 | typedef struct {
11 | cmark_event_type ev_type;
12 | cmark_node *node;
13 | } cmark_iter_state;
14 |
15 | struct cmark_iter {
16 | cmark_mem *mem;
17 | cmark_node *root;
18 | cmark_iter_state cur;
19 | cmark_iter_state next;
20 | };
21 |
22 | #ifdef __cplusplus
23 | }
24 | #endif
25 |
26 | #endif
27 |
--------------------------------------------------------------------------------
/Sources/cmark/node.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_NODE_H
2 | #define CMARK_NODE_H
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | #include
9 | #include
10 |
11 | #include "cmark.h"
12 | #include "buffer.h"
13 | #include "chunk.h"
14 |
15 | typedef struct {
16 | cmark_list_type list_type;
17 | int marker_offset;
18 | int padding;
19 | int start;
20 | cmark_delim_type delimiter;
21 | unsigned char bullet_char;
22 | bool tight;
23 | } cmark_list;
24 |
25 | typedef struct {
26 | cmark_chunk info;
27 | cmark_chunk literal;
28 | uint8_t fence_length;
29 | uint8_t fence_offset;
30 | unsigned char fence_char;
31 | int8_t fenced;
32 | } cmark_code;
33 |
34 | typedef struct {
35 | int level;
36 | bool setext;
37 | } cmark_heading;
38 |
39 | typedef struct {
40 | cmark_chunk url;
41 | cmark_chunk title;
42 | } cmark_link;
43 |
44 | typedef struct {
45 | cmark_chunk on_enter;
46 | cmark_chunk on_exit;
47 | } cmark_custom;
48 |
49 | enum cmark_node__internal_flags {
50 | CMARK_NODE__OPEN = (1 << 0),
51 | CMARK_NODE__LAST_LINE_BLANK = (1 << 1),
52 | CMARK_NODE__LAST_LINE_CHECKED = (1 << 2),
53 | };
54 |
55 | struct cmark_node {
56 | cmark_strbuf content;
57 |
58 | struct cmark_node *next;
59 | struct cmark_node *prev;
60 | struct cmark_node *parent;
61 | struct cmark_node *first_child;
62 | struct cmark_node *last_child;
63 |
64 | void *user_data;
65 |
66 | int start_line;
67 | int start_column;
68 | int end_line;
69 | int end_column;
70 | int internal_offset;
71 | uint16_t type;
72 | uint16_t flags;
73 |
74 | union {
75 | cmark_chunk literal;
76 | cmark_list list;
77 | cmark_code code;
78 | cmark_heading heading;
79 | cmark_link link;
80 | cmark_custom custom;
81 | int html_block_type;
82 | } as;
83 | };
84 |
85 | static CMARK_INLINE cmark_mem *cmark_node_mem(cmark_node *node) {
86 | return node->content.mem;
87 | }
88 | CMARK_EXPORT int cmark_node_check(cmark_node *node, FILE *out);
89 |
90 | #ifdef __cplusplus
91 | }
92 | #endif
93 |
94 | #endif
95 |
--------------------------------------------------------------------------------
/Sources/cmark/parser.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_AST_H
2 | #define CMARK_AST_H
3 |
4 | #include
5 | #include "references.h"
6 | #include "node.h"
7 | #include "buffer.h"
8 |
9 | #ifdef __cplusplus
10 | extern "C" {
11 | #endif
12 |
13 | #define MAX_LINK_LABEL_LENGTH 1000
14 |
15 | struct cmark_parser {
16 | struct cmark_mem *mem;
17 | struct cmark_reference_map *refmap;
18 | struct cmark_node *root;
19 | struct cmark_node *current;
20 | int line_number;
21 | bufsize_t offset;
22 | bufsize_t column;
23 | bufsize_t first_nonspace;
24 | bufsize_t first_nonspace_column;
25 | bufsize_t thematic_break_kill_pos;
26 | int indent;
27 | bool blank;
28 | bool partially_consumed_tab;
29 | cmark_strbuf curline;
30 | bufsize_t last_line_length;
31 | cmark_strbuf linebuf;
32 | int options;
33 | bool last_buffer_ended_with_cr;
34 | };
35 |
36 | #ifdef __cplusplus
37 | }
38 | #endif
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/Sources/cmark/references.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_REFERENCES_H
2 | #define CMARK_REFERENCES_H
3 |
4 | #include "chunk.h"
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | #define REFMAP_SIZE 16
11 |
12 | struct cmark_reference {
13 | struct cmark_reference *next;
14 | unsigned char *label;
15 | cmark_chunk url;
16 | cmark_chunk title;
17 | unsigned int hash;
18 | };
19 |
20 | typedef struct cmark_reference cmark_reference;
21 |
22 | struct cmark_reference_map {
23 | cmark_mem *mem;
24 | cmark_reference *table[REFMAP_SIZE];
25 | };
26 |
27 | typedef struct cmark_reference_map cmark_reference_map;
28 |
29 | cmark_reference_map *cmark_reference_map_new(cmark_mem *mem);
30 | void cmark_reference_map_free(cmark_reference_map *map);
31 | cmark_reference *cmark_reference_lookup(cmark_reference_map *map,
32 | cmark_chunk *label);
33 | extern void cmark_reference_create(cmark_reference_map *map, cmark_chunk *label,
34 | cmark_chunk *url, cmark_chunk *title);
35 |
36 | #ifdef __cplusplus
37 | }
38 | #endif
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/Sources/cmark/render.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_RENDER_H
2 | #define CMARK_RENDER_H
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | #include
9 | #include "buffer.h"
10 | #include "chunk.h"
11 |
12 | typedef enum { LITERAL, NORMAL, TITLE, URL } cmark_escaping;
13 |
14 | struct cmark_renderer {
15 | cmark_mem *mem;
16 | cmark_strbuf *buffer;
17 | cmark_strbuf *prefix;
18 | int column;
19 | int width;
20 | int need_cr;
21 | bufsize_t last_breakable;
22 | bool begin_line;
23 | bool begin_content;
24 | bool no_linebreaks;
25 | bool in_tight_list_item;
26 | void (*outc)(struct cmark_renderer *, cmark_escaping, int32_t, unsigned char);
27 | void (*cr)(struct cmark_renderer *);
28 | void (*blankline)(struct cmark_renderer *);
29 | void (*out)(struct cmark_renderer *, const char *, bool, cmark_escaping);
30 | };
31 |
32 | typedef struct cmark_renderer cmark_renderer;
33 |
34 | void cmark_render_ascii(cmark_renderer *renderer, const char *s);
35 |
36 | void cmark_render_code_point(cmark_renderer *renderer, uint32_t c);
37 |
38 | char *cmark_render(cmark_node *root, int options, int width,
39 | void (*outc)(cmark_renderer *, cmark_escaping, int32_t,
40 | unsigned char),
41 | int (*render_node)(cmark_renderer *renderer,
42 | cmark_node *node,
43 | cmark_event_type ev_type, int options));
44 |
45 | #ifdef __cplusplus
46 | }
47 | #endif
48 |
49 | #endif
50 |
--------------------------------------------------------------------------------
/Sources/cmark/scanners.h:
--------------------------------------------------------------------------------
1 | #include "cmark.h"
2 | #include "chunk.h"
3 |
4 | #ifdef __cplusplus
5 | extern "C" {
6 | #endif
7 |
8 | bufsize_t _scan_at(bufsize_t (*scanner)(const unsigned char *), cmark_chunk *c,
9 | bufsize_t offset);
10 | bufsize_t _scan_scheme(const unsigned char *p);
11 | bufsize_t _scan_autolink_uri(const unsigned char *p);
12 | bufsize_t _scan_autolink_email(const unsigned char *p);
13 | bufsize_t _scan_html_tag(const unsigned char *p);
14 | bufsize_t _scan_html_block_start(const unsigned char *p);
15 | bufsize_t _scan_html_block_start_7(const unsigned char *p);
16 | bufsize_t _scan_html_block_end_1(const unsigned char *p);
17 | bufsize_t _scan_html_block_end_2(const unsigned char *p);
18 | bufsize_t _scan_html_block_end_3(const unsigned char *p);
19 | bufsize_t _scan_html_block_end_4(const unsigned char *p);
20 | bufsize_t _scan_html_block_end_5(const unsigned char *p);
21 | bufsize_t _scan_link_title(const unsigned char *p);
22 | bufsize_t _scan_spacechars(const unsigned char *p);
23 | bufsize_t _scan_atx_heading_start(const unsigned char *p);
24 | bufsize_t _scan_setext_heading_line(const unsigned char *p);
25 | bufsize_t _scan_open_code_fence(const unsigned char *p);
26 | bufsize_t _scan_close_code_fence(const unsigned char *p);
27 | bufsize_t _scan_entity(const unsigned char *p);
28 | bufsize_t _scan_dangerous_url(const unsigned char *p);
29 |
30 | #define scan_scheme(c, n) _scan_at(&_scan_scheme, c, n)
31 | #define scan_autolink_uri(c, n) _scan_at(&_scan_autolink_uri, c, n)
32 | #define scan_autolink_email(c, n) _scan_at(&_scan_autolink_email, c, n)
33 | #define scan_html_tag(c, n) _scan_at(&_scan_html_tag, c, n)
34 | #define scan_html_block_start(c, n) _scan_at(&_scan_html_block_start, c, n)
35 | #define scan_html_block_start_7(c, n) _scan_at(&_scan_html_block_start_7, c, n)
36 | #define scan_html_block_end_1(c, n) _scan_at(&_scan_html_block_end_1, c, n)
37 | #define scan_html_block_end_2(c, n) _scan_at(&_scan_html_block_end_2, c, n)
38 | #define scan_html_block_end_3(c, n) _scan_at(&_scan_html_block_end_3, c, n)
39 | #define scan_html_block_end_4(c, n) _scan_at(&_scan_html_block_end_4, c, n)
40 | #define scan_html_block_end_5(c, n) _scan_at(&_scan_html_block_end_5, c, n)
41 | #define scan_link_title(c, n) _scan_at(&_scan_link_title, c, n)
42 | #define scan_spacechars(c, n) _scan_at(&_scan_spacechars, c, n)
43 | #define scan_atx_heading_start(c, n) _scan_at(&_scan_atx_heading_start, c, n)
44 | #define scan_setext_heading_line(c, n) \
45 | _scan_at(&_scan_setext_heading_line, c, n)
46 | #define scan_open_code_fence(c, n) _scan_at(&_scan_open_code_fence, c, n)
47 | #define scan_close_code_fence(c, n) _scan_at(&_scan_close_code_fence, c, n)
48 | #define scan_entity(c, n) _scan_at(&_scan_entity, c, n)
49 | #define scan_dangerous_url(c, n) _scan_at(&_scan_dangerous_url, c, n)
50 |
51 | #ifdef __cplusplus
52 | }
53 | #endif
54 |
--------------------------------------------------------------------------------
/Sources/cmark/utf8.h:
--------------------------------------------------------------------------------
1 | #ifndef CMARK_UTF8_H
2 | #define CMARK_UTF8_H
3 |
4 | #include
5 | #include "buffer.h"
6 |
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 |
11 | void cmark_utf8proc_case_fold(cmark_strbuf *dest, const uint8_t *str,
12 | bufsize_t len);
13 | void cmark_utf8proc_encode_char(int32_t uc, cmark_strbuf *buf);
14 | int cmark_utf8proc_iterate(const uint8_t *str, bufsize_t str_len, int32_t *dst);
15 | void cmark_utf8proc_check(cmark_strbuf *dest, const uint8_t *line,
16 | bufsize_t size);
17 | int cmark_utf8proc_is_space(int32_t uc);
18 | int cmark_utf8proc_is_punctuation(int32_t uc);
19 |
20 | #ifdef __cplusplus
21 | }
22 | #endif
23 |
24 | #endif
25 |
--------------------------------------------------------------------------------
/Supporting Files/Configurations/Deployment-Targets.xcconfig:
--------------------------------------------------------------------------------
1 | SWIFT_VERSION = 5.0
2 |
3 | IPHONEOS_DEPLOYMENT_TARGET = 9.0
4 | MACOSX_DEPLOYMENT_TARGET = 10.11
5 | TVOS_DEPLOYMENT_TARGET = 9.0
6 |
--------------------------------------------------------------------------------
/Supporting Files/Configurations/Universal-Framework-Target.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Universal-Target-Base.xcconfig"
2 |
3 | // OSX-specific default settings
4 | FRAMEWORK_VERSION[sdk=macosx*] = A
5 | COMBINE_HIDPI_IMAGES[sdk=macosx*] = YES
6 |
7 | // iOS-specific default settings
8 | TARGETED_DEVICE_FAMILY[sdk=iphonesimulator*] = 1,2
9 | TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2
10 |
11 | // TV-specific default settings
12 | TARGETED_DEVICE_FAMILY[sdk=appletvsimulator*] = 3
13 | TARGETED_DEVICE_FAMILY[sdk=appletv*] = 3
14 |
15 | ENABLE_BITCODE[sdk=macosx*] = NO
16 | ENABLE_BITCODE[sdk=iphonesimulator*] = NO
17 | ENABLE_BITCODE[sdk=iphone*] = YES
18 | ENABLE_BITCODE[sdk=appletvsimulator*] = YES
19 | ENABLE_BITCODE[sdk=appletv*] = YES
20 |
--------------------------------------------------------------------------------
/Supporting Files/Configurations/Universal-Target-Base.xcconfig:
--------------------------------------------------------------------------------
1 | SUPPORTED_PLATFORMS = macosx iphonesimulator iphoneos appletvos appletvsimulator
2 | VALID_ARCHS[sdk=macosx*] = i386 x86_64 arm64
3 | VALID_ARCHS[sdk=iphoneos*] = arm64 armv7 armv7s
4 | VALID_ARCHS[sdk=iphonesimulator*] = i386 x86_64 arm64
5 | VALID_ARCHS[sdk=appletv*] = arm64
6 | VALID_ARCHS[sdk=appletvsimulator*] = x86_64
7 |
8 | // Dynamic linking uses different default copy paths
9 | LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) '@executable_path/../Frameworks' '@loader_path/../Frameworks'
10 | LD_RUNPATH_SEARCH_PATHS[sdk=iphoneos*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
11 | LD_RUNPATH_SEARCH_PATHS[sdk=iphonesimulator*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
12 | LD_RUNPATH_SEARCH_PATHS[sdk=appletvos*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
13 | LD_RUNPATH_SEARCH_PATHS[sdk=appletvsimulator*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
14 |
--------------------------------------------------------------------------------
/Supporting Files/Down-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSHumanReadableCopyright
22 | Copyright © 2016-2019 Down. All rights reserved.
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Supporting Files/DownTests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSHumanReadableCopyright
22 | Copyright © 2016-2019 Down. All rights reserved.
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/ListItemPrefixGeneratorTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ListItemPrefixGeneratorTests.swift
3 | // DownTests
4 | //
5 | // Created by John Nguyen on 13.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Down
11 |
12 | class ListItemPrefixGeneratorTests: XCTestCase {
13 |
14 | func testNumberStaticPrefixGeneration() {
15 | // Given
16 | let sut = StaticListItemPrefixGenerator(listType: .ordered(start: 3), numberOfItems: 3, nestDepth: 1)
17 |
18 | // Then
19 | XCTAssertEqual("3.", sut.next())
20 | XCTAssertEqual("4.", sut.next())
21 | XCTAssertEqual("5.", sut.next())
22 | XCTAssertNil(sut.next())
23 | }
24 |
25 | func testBulletStaticPrefixGeneration() {
26 | // Given
27 | let sut = StaticListItemPrefixGenerator(listType: .bullet, numberOfItems: 3, nestDepth: 1)
28 |
29 | // Then
30 | XCTAssertEqual("•", sut.next())
31 | XCTAssertEqual("•", sut.next())
32 | XCTAssertEqual("•", sut.next())
33 | XCTAssertNil(sut.next())
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/NodeTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NodeTests.swift
3 | // DownTests
4 | //
5 | // Created by John Nguyen on 23.06.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Down
11 |
12 | class NodeTests: XCTestCase {
13 |
14 | func testListDepth() throws {
15 | // Given
16 | let sut = NodeVisitor()
17 | let markdown = """
18 | 1. A1
19 | 2. B1
20 | * A2
21 | 1. A3
22 | 2. B3
23 | * B2
24 | 3. C1
25 | """
26 |
27 | // When
28 | parse(markdown, andVisitWith: sut)
29 |
30 | // Then
31 | XCTAssertEqual(sut.listNestDepthResults, [0, 1, 2])
32 | }
33 |
34 | }
35 |
36 | // MARK: - Helpers
37 |
38 | extension NodeTests {
39 |
40 | private func parse(_ markdown: String, andVisitWith visitor: NodeVisitor) {
41 | do {
42 | let document = try Down(markdownString: markdown).toDocument()
43 | document.accept(visitor)
44 | } catch {
45 | XCTFail("Failed to generate document.")
46 | }
47 | }
48 |
49 | }
50 |
51 | private class NodeVisitor: DebugVisitor {
52 |
53 | var listNestDepthResults = [Int]()
54 |
55 | override func visit(list node: List) -> String {
56 | listNestDepthResults.append(node.nestDepth)
57 | return super.visit(list: node)
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testAttributedStringVisitor.1.txt:
--------------------------------------------------------------------------------
1 | Heading
This is a paragraph with inline elements
This is followed by a hard linebreak
This is after the linebreak
2 | this is a link this is an image
this is a quote
code block
code block
block
1. first item
2. second item
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testBlockQuote.1.txt:
--------------------------------------------------------------------------------
1 | Text text.
Quote Quote
Quote Quote
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testBlockQuote.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text text.
4 | ↳ Block Quote
5 | ↳ Paragraph
6 | ↳ Text - Quote
7 | ↳ Soft Break
8 | ↳ Text - Quote
9 | ↳ Block Quote
10 | ↳ Paragraph
11 | ↳ Text - Quote
12 | ↳ Soft Break
13 | ↳ Text - Quote
14 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testCodeBlock.1.txt:
--------------------------------------------------------------------------------
1 | Text text.
Code block
Code block
Text text.
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testCodeBlock.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text text.
4 | ↳ Code Block - fenceInfo: nil, content: Code block\nCode block\n
5 | ↳ Paragraph
6 | ↳ Text - Text text.
7 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testHeading.1.txt:
--------------------------------------------------------------------------------
1 | Text text.
Heading
Text text.
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testHeading.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text text.
4 | ↳ Heading - L1
5 | ↳ Text - Heading
6 | ↳ Paragraph
7 | ↳ Text - Text text.
8 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testHtmlBlock.1.txt:
--------------------------------------------------------------------------------
1 | Text text.
Text text.
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testHtmlBlock.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text text.
4 | ↳ Html Block - content: \n \n\n
5 | ↳ Paragraph
6 | ↳ Text - Text text.
7 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testInline.1.txt:
--------------------------------------------------------------------------------
1 | Text strong emphasis code
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testInline.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text
4 | ↳ Strong
5 | ↳ Text - strong
6 | ↳ Emphasis
7 | ↳ Text - emphasis
8 | ↳ Code - code
9 | ↳ Text -
10 | ↳ Html Inline -
11 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testLineBreak.1.txt:
--------------------------------------------------------------------------------
1 | Text text.
Text text.
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testLineBreak.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text text.
4 | ↳ Line Break
5 | ↳ Text - Text text.
6 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testLink.1.txt:
--------------------------------------------------------------------------------
1 | Text link text image
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testLink.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text
4 | ↳ Link - title: nil, url: www.example.com)
5 | ↳ Text - link
6 | ↳ Text - text
7 | ↳ Image - title: nil, url: www.example.com)
8 | ↳ Text - image
9 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testList.1.txt:
--------------------------------------------------------------------------------
1 | Text text.
3. One
4. Two
• Three
• Four
5. Five
Text text.
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testList.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text text.
4 | ↳ List - type: Ordered (start: 3), isTight: true, delimiter: period
5 | ↳ Item
6 | ↳ Paragraph
7 | ↳ Text - One
8 | ↳ Item
9 | ↳ Paragraph
10 | ↳ Text - Two
11 | ↳ List - type: Bullet, isTight: true
12 | ↳ Item
13 | ↳ Paragraph
14 | ↳ Text - Three
15 | ↳ Item
16 | ↳ Paragraph
17 | ↳ Text - Four
18 | ↳ Item
19 | ↳ Paragraph
20 | ↳ Text - Five
21 | ↳ Paragraph
22 | ↳ Text - Text text.
23 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testParagraph.1.txt:
--------------------------------------------------------------------------------
1 | Text text.
Text text.
Text text.
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testParagraph.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text text.
4 | ↳ Paragraph
5 | ↳ Text - Text text.
6 | ↳ Paragraph
7 | ↳ Text - Text text.
8 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testSimpleMarkdown.1.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Heading - L1
3 | ↳ Text - Hello
4 | ↳ Paragraph
5 | ↳ Text - This is a
6 | ↳ Strong
7 | ↳ Text - test!
8 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testSoftBreak.1.txt:
--------------------------------------------------------------------------------
1 | Text text text text
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testSoftBreak.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text text
4 | ↳ Soft Break
5 | ↳ Text - text text
6 |
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testThematicBreak.1.txt:
--------------------------------------------------------------------------------
1 | Text text.
2 | Text text.
--------------------------------------------------------------------------------
/Tests/DownTests/AST/__Snapshots__/VisitorTests/testThematicBreak.2.txt:
--------------------------------------------------------------------------------
1 | Document
2 | ↳ Paragraph
3 | ↳ Text - Text text.
4 | ↳ Thematic Break
5 | ↳ Paragraph
6 | ↳ Text - Text text.
7 |
--------------------------------------------------------------------------------
/Tests/DownTests/BindingTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BindingTests.swift
3 | // DownTests
4 | //
5 | // Created by Rob Phillips on 5/28/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import SnapshotTesting
11 | @testable import Down
12 |
13 | class BindingTests: XCTestCase {
14 |
15 | let down = Down(markdownString: "## [Down](https://github.com/johnxnnguyen/Down)")
16 |
17 | func testASTBindingsWork() throws {
18 | _ = try down.toAST()
19 | }
20 |
21 | func testHTMLBindingsWork() throws {
22 | let html = try down.toHTML()
23 | assertSnapshot(matching: html, as: .lines)
24 | }
25 |
26 | func testXMLBindingsWork() throws {
27 | let xml = try down.toXML()
28 | assertSnapshot(matching: xml, as: .lines)
29 | }
30 |
31 | func testGroffBindingsWork() throws {
32 | let man = try down.toGroff()
33 | assertSnapshot(matching: man, as: .lines)
34 | }
35 |
36 | func testLaTeXBindngsWork() throws {
37 | let latex = try down.toLaTeX()
38 | assertSnapshot(matching: latex, as: .lines)
39 | }
40 |
41 | func testCommonMarkBindngsWork() throws {
42 | let commonMark = try down.toCommonMark()
43 | assertSnapshot(matching: commonMark, as: .lines)
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/Tests/DownTests/Fixtures/TestDownView.bundle/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | DOWN_HTML
13 | But also, custom HTML!
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Tests/DownTests/Fixtures/TestDownView.bundle/js/down.js:
--------------------------------------------------------------------------------
1 | hljs.initHighlightingOnLoad();
--------------------------------------------------------------------------------
/Tests/DownTests/NSAttributedStringTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSAttributedStringTests.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 6/2/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Down
11 |
12 | class NSAttributedStringTests: XCTestCase {
13 |
14 | func testAttributedStringBindingsWork() {
15 | let markdown = "## [Down](https://github.com/johnxnguyen/Down)"
16 | let attributedString = try? Down(markdownString: markdown).toAttributedString()
17 | XCTAssertNotNil(attributedString)
18 | XCTAssertTrue(attributedString!.string == "Down\n")
19 | }
20 |
21 | func testInstantiation() {
22 | let attributedString = try? NSAttributedString(htmlString: "Oh Hai
")
23 | XCTAssertNotNil(attributedString)
24 | XCTAssertTrue(attributedString!.string == "Oh Hai\n")
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/Tests/DownTests/StringTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringTests.swift
3 | // Down
4 | //
5 | // Created by Rob Phillips on 6/2/16.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Down
11 |
12 | class StringTests: XCTestCase {
13 |
14 | func testStringToHTML() {
15 | // String is assumed to contain valid Markdown
16 | let string = "## [Down](https://github.com/iwasrobbed/Down)"
17 | let down = try? string.toHTML()
18 | XCTAssertNotNil(down)
19 | XCTAssertTrue(down == "\n")
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/CodeBlockStyleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodeBlockStyleTests.swift
3 | // DownTests
4 | //
5 | // Created by John Nguyen on 05.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | class CodeBlockStyleTests: StylerTestSuite {
12 |
13 | /// # Important
14 | ///
15 | /// Snapshot tests must be run on the same simulator used to record the reference snapshots, otherwise
16 | /// the comparison may fail. These tests were recorded on the **iPhone 12** simulator.
17 | ///
18 |
19 | func testThat_CodeBlock_IsStyled() {
20 | // Given
21 | let markdown = """
22 | Etiam vel dui id purus finibus auctor. Donec in semper lectus. Vestibulum vel eleifend justo.
23 |
24 | ```
25 | func greet(person: Person) {
26 | print("Hello, \\(person.name)"
27 | }
28 | ```
29 |
30 | Duis ultrices dapibus diam nec mollis. Mauris scelerisque massa nec tristique dapibus. Mauris sed tempor lorem.
31 |
32 | Duis ultrices dapibus diam nec mollis. Mauris scelerisque massa nec tristique dapibus. Mauris sed tempor lorem.
33 | """
34 |
35 | // Then
36 | assertStyle(for: markdown, width: .wide)
37 | }
38 |
39 | func testThat_HtmlBlock_IsStyled() {
40 | // Given
41 | let markdown = """
42 | Etiam vel dui id purus finibus auctor. Donec in semper lectus. Vestibulum vel eleifend justo.
43 |
44 |
45 |
46 |
47 |
48 |
49 | Duis ultrices dapibus diam nec mollis. Mauris scelerisque massa nec tristique dapibus. Mauris sed tempor lorem.
50 | """
51 |
52 | // Then
53 | assertStyle(for: markdown, width: .wide)
54 | }
55 |
56 | }
57 |
58 | #endif
59 |
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/DownDebugLayoutManagerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DownDebugLayoutManagerTests.swift
3 | // DownTests
4 | //
5 | // Created by John Nguyen on 08.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | class DownDebugLayoutManagerTests: StylerTestSuite {
12 |
13 | /// # Important
14 | ///
15 | /// Snapshot tests must be run on the same simulator used to record the reference snapshots, otherwise
16 | /// the comparison may fail. These tests were recorded on the **iPhone 12** simulator.
17 | ///
18 |
19 | func testThat_LineFragments_AreDrawn() {
20 | // Given
21 | let markdown = """
22 | # Curabitur fringilla lacus eget nunc dictum dignissim.
23 | ---
24 |
25 | Etiam vel dui id purus finibus auctor. Donec in semper lectus. Vestibulum vel eleifend justo.
26 |
27 | > Curabitur fringilla lacus eget nunc dictum dignissim. Donec cursus magna a libero vulputate maximus.
28 | > Donec dignissim iaculis orci et pharetra. Curabitur ac viverra augue, id placerat sapien.
29 |
30 | Duis ultrices dapibus diam nec mollis. Mauris scelerisque massa nec tristique dapibus. Mauris sed tempor lorem.
31 |
32 | 1. Etiam vel dui id purus finibus auctor.
33 | 2. Donec in semper lectus. Vestibulum vel eleifend justo.
34 | > Curabitur fringilla lacus eget nunc dictum dignissim.
35 | > Donec cursus magna a libero vulputate maximus.
36 | 3. Nunc vitae tellus eget purus sagittis aliquet.
37 |
38 | Suspendisse egestas ex at bibendum tempor. Integer tellus tortor.
39 | """
40 |
41 | // Then
42 | assertStyle(for: markdown, width: .wide, showLineFragments: true)
43 | }
44 | }
45 |
46 | #endif
47 |
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/Helpers/CGPointTranslateTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint_TranslateTests.swift
3 | // DownTests
4 | //
5 | // Created by John Nguyen on 13.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Down
11 |
12 | class CGPointTranslateTests: XCTestCase {
13 |
14 | func testPointTranslation() {
15 | // Given
16 | let sut = CGPoint(x: 1, y: 2)
17 |
18 | // When
19 | let result = sut.translated(by: CGPoint(x: 3, y: 4))
20 |
21 | // Then
22 | XCTAssertEqual(CGPoint(x: 4, y: 6), result)
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/Helpers/CGRectHelpersTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGRect_HelpersTests.swift
3 | // DownTests
4 | //
5 | // Created by John Nguyen on 13.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Down
11 |
12 | class CGRectHelpersTests: XCTestCase {
13 |
14 | func testRectInitializationWithBoundaries() {
15 | // When
16 | let result = CGRect(minX: 1, minY: 2, maxX: 3, maxY: 4)
17 |
18 | // Then
19 | XCTAssertEqual(CGRect(x: 1, y: 2, width: 2, height: 2), result)
20 | }
21 |
22 | func testRectTranslation() {
23 | // Given
24 | let sut = CGRect(x: 1, y: 2, width: 3, height: 4)
25 |
26 | // When
27 | let result = sut.translated(by: CGPoint(x: 5, y: 6))
28 |
29 | // Then
30 | XCTAssertEqual(CGRect(x: 6, y: 8, width: 3, height: 4), result)
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/InlineStyleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InlineStyleTests.swift
3 | // DownTests
4 | //
5 | // Created by John Nguyen on 29.07.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | class InlineStyleTests: StylerTestSuite {
12 |
13 | // # Important
14 | //
15 | // Snapshot tests must be run on the same simulator used to record the reference snapshots, otherwise
16 | // the comparison may fail. These tests were recorded on the **iPhone 12** simulator.
17 |
18 | // MARK: - Simple
19 |
20 | func testThat_StrongText_IsStyled() {
21 | // Given
22 | let markdown = "Text **strong** text."
23 |
24 | // Then
25 | assertStyle(for: markdown, width: .wide)
26 | }
27 |
28 | func testThat_EmphasizedText_IsStyled() {
29 | // Given
30 | let markdown = "Text _emphasized_ text."
31 |
32 | // Then
33 | assertStyle(for: markdown, width: .wide)
34 | }
35 |
36 | func testThat_CodeText_IsStyled() {
37 | // Given
38 | let markdown = "Text `code` text text."
39 |
40 | // Then
41 | assertStyle(for: markdown, width: .wide)
42 | }
43 |
44 | // MARK: - Double Combinations
45 |
46 | func testThat_StrongEmphasizedText_IsStyled() {
47 | // Given
48 | let markdown = "Text **strong _emphasized_ strong** text."
49 |
50 | // Then
51 | assertStyle(for: markdown, width: .wide)
52 | }
53 |
54 | func testThat_EmphasizedStrongText_IsStyled() {
55 | // Given
56 | let markdown = "Text _emphasized **strong** emphasized_ text."
57 |
58 | // Then
59 | assertStyle(for: markdown, width: .wide)
60 | }
61 |
62 | func testThat_StrongCode_IsStyled() {
63 | // Given
64 | let markdown = "Text **strong `code` strong strong** text."
65 |
66 | // Then
67 | assertStyle(for: markdown, width: .wide)
68 | }
69 |
70 | func testThat_EmphasizedCode_IsStyled() {
71 | // Given
72 | let markdown = "Text _emphasized `code` emphasized emphasized_ text."
73 |
74 | // Then
75 | assertStyle(for: markdown, width: .wide)
76 | }
77 |
78 | // MARK: - Triple Combinations
79 |
80 | func testThat_StrongEmphasizedCode_IsStyled() {
81 | // Given
82 | let markdown = "Text **strong _emphasized `code` emphasized emphasized_ strong** text."
83 |
84 | // Then
85 | assertStyle(for: markdown, width: .wide)
86 | }
87 |
88 | func testThat_EmphasizedStrongCode_IsStyled() {
89 | // Given
90 | let markdown = "Text _emphasized **strong `code` strong strong** emphasized_ text."
91 |
92 | // Then
93 | assertStyle(for: markdown, width: .wide)
94 | }
95 |
96 | }
97 |
98 | #endif
99 |
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/LinkStyleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LinkStyleTests.swift
3 | // DownTests
4 | //
5 | // Created by John Nguyen on 08.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | class LinkStyleTests: StylerTestSuite {
12 |
13 | /// # Important
14 | ///
15 | /// Snapshot tests must be run on the same simulator used to record the reference snapshots, otherwise
16 | /// the comparison may fail. These tests were recorded on the **iPhone 12** simulator.
17 | ///
18 |
19 | func testThat_Link_IsStyled() {
20 | // Given
21 | let markdown = """
22 | Praesent facilisis [pellentesque](www.example.com) ipsum at pulvinar. Sed consectetur augue.
23 | """
24 |
25 | // Then
26 | assertStyle(for: markdown, width: .narrow)
27 | }
28 |
29 | func testThat_Link_Preserves_InlineStyles() {
30 | // Given
31 | let markdown = """
32 | Praesent facilisis [**pellentesque _ipsum `at` _**](www.example.com). Sed consectetur augue.
33 | """
34 |
35 | // Then
36 | assertStyle(for: markdown, width: .narrow)
37 | }
38 |
39 | }
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/ThematicBreakSyleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThematicBreakSyleTests.swift
3 | // DownTests
4 | //
5 | // Created by John Nguyen on 03.08.19.
6 | // Copyright © 2016-2019 Down. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | class ThematicBreakSyleTests: StylerTestSuite {
12 |
13 | /// # Important
14 | ///
15 | /// Snapshot tests must be run on the same simulator used to record the reference snapshots, otherwise
16 | /// the comparison may fail. These tests were recorded on the **iPhone 12** simulator.
17 | ///
18 |
19 | func testThat_ThematicBreak_IsStyled() {
20 | // Given
21 | let markdown = """
22 | # Praesent facilisis pellentesque ipsum at pulvinar.
23 |
24 | ---
25 |
26 | Quisque molestie auctor neque. Donec vitae risus non odio viverra hendrerit. Pellentesque non vulputate felis.
27 | Curabitur aliquam, nisl vitae vulputate eleifend, metus sapien eleifend.
28 | """
29 |
30 | // Then
31 | assertStyle(for: markdown, width: .wide)
32 | }
33 |
34 | func testThat_ThematicBreak_InOffsetTextContainer_IsStyled() {
35 | // Given
36 | let markdown = """
37 | # Praesent facilisis pellentesque ipsum at pulvinar.
38 |
39 | ---
40 |
41 | Quisque molestie auctor neque. Donec vitae risus non odio viverra hendrerit. Pellentesque non vulputate felis.
42 | Curabitur aliquam, nisl vitae vulputate eleifend, metus sapien eleifend. Quisque molestie auctor neque.
43 | Donec vitae risus non odio viverra hendrerit.
44 | """
45 |
46 | textContainerInset = .init(top: 30, left: 60, bottom: 30, right: 20)
47 |
48 | // Then
49 | assertStyle(for: markdown, width: .wide)
50 | }
51 |
52 | func testThat_ThematicBreak_CanBe_Indented() {
53 | // Given
54 | let markdown = """
55 | # Praesent facilisis pellentesque ipsum at pulvinar.
56 |
57 | ---
58 |
59 | Quisque molestie auctor neque. Donec vitae risus non odio viverra hendrerit. Pellentesque non vulputate felis.
60 | Curabitur aliquam, nisl vitae vulputate eleifend, metus sapien eleifend.
61 | """
62 |
63 | var configuration = self.configuration
64 | configuration.thematicBreakOptions.indentation = 30
65 |
66 | // Then
67 | assertStyle(for: markdown, width: .wide, configuration: configuration)
68 | }
69 |
70 | }
71 |
72 | #endif
73 |
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_NestedQuotes_Have_TheirOwnStripes.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_NestedQuotes_Have_TheirOwnStripes.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteAlignment_Obeys_TextContainerOffset.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteAlignment_Obeys_TextContainerOffset.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteContent_Aligns.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteContent_Aligns.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteContent_Preserves_BlockElements.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteContent_Preserves_BlockElements.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteContent_Preserves_InlineElements.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteContent_Preserves_InlineElements.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteContent_Preserves_ListFormatting.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteContent_Preserves_ListFormatting.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteContent_Preserves_ThematicBreak.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteContent_Preserves_ThematicBreak.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteStripe_AlignsTo_Margin.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuoteStripe_AlignsTo_Margin.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuotedList_WithinA_ListItem_AlignsCorrectly.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_QuotedList_WithinA_ListItem_AlignsCorrectly.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_Quotes_WithinA_ListItem_AlignsTo_ListItemContent.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/BlockQuoteStyleTests/testThat_Quotes_WithinA_ListItem_AlignsTo_ListItemContent.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/CodeBlockStyleTests/testThat_CodeBlock_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/CodeBlockStyleTests/testThat_CodeBlock_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/CodeBlockStyleTests/testThat_HtmlBlock_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/CodeBlockStyleTests/testThat_HtmlBlock_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/DownDebugLayoutManagerTests/testThat_LineFragments_AreDrawn.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/DownDebugLayoutManagerTests/testThat_LineFragments_AreDrawn.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/HeadingStyleTests/testThat_HeadingStyle_Preserves_StrongEmphasisAndMonospaceTraits.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/HeadingStyleTests/testThat_HeadingStyle_Preserves_StrongEmphasisAndMonospaceTraits.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/HeadingStyleTests/testThat_Heading_LevelOne_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/HeadingStyleTests/testThat_Heading_LevelOne_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/HeadingStyleTests/testThat_Heading_LevelThree_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/HeadingStyleTests/testThat_Heading_LevelThree_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/HeadingStyleTests/testThat_Heading_LevelTwo_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/HeadingStyleTests/testThat_Heading_LevelTwo_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/HeadingStyleTests/testThat_Heading_LevelsThreeToSix_AreStyledEqually.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/HeadingStyleTests/testThat_Heading_LevelsThreeToSix_AreStyledEqually.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_CodeText_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_CodeText_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_EmphasizedCode_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_EmphasizedCode_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_EmphasizedStrongCode_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_EmphasizedStrongCode_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_EmphasizedStrongText_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_EmphasizedStrongText_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_EmphasizedText_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_EmphasizedText_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_StrongCode_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_StrongCode_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_StrongEmphasizedCode_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_StrongEmphasizedCode_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_StrongEmphasizedText_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_StrongEmphasizedText_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_StrongText_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/InlineStyleTests/testThat_StrongText_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/LinkStyleTests/testThat_Link_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/LinkStyleTests/testThat_Link_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/LinkStyleTests/testThat_Link_Preserves_InlineStyles.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/LinkStyleTests/testThat_Link_Preserves_InlineStyles.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_DigitAndBulletPrefixes_Align.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_DigitAndBulletPrefixes_Align.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_DigitPrefixes_ExceedingMaxPrefixLength_DontPush_WrappedLines.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_DigitPrefixes_ExceedingMaxPrefixLength_DontPush_WrappedLines.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_DigitPrefixes_ExceedingMaxPrefixLength_Push_FirstLine.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_DigitPrefixes_ExceedingMaxPrefixLength_Push_FirstLine.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_DigitPrefixes_UpToMaxPrefixLength_Align.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_DigitPrefixes_UpToMaxPrefixLength_Align.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_FirstParagraph_WithLineBreaks_AlignTo_FirstLine.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_FirstParagraph_WithLineBreaks_AlignTo_FirstLine.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_FirstParagraph_WrappedLines_AlignTo_FirstLine.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_FirstParagraph_WrappedLines_AlignTo_FirstLine.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_ListItems_Preseve_InlineElements.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_ListItems_Preseve_InlineElements.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_NestedList_AlignsTo_OuterList.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_NestedList_AlignsTo_OuterList.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_NestedList_InMiddleParagraph_AlignsTo_OuterList.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_NestedList_InMiddleParagraph_AlignsTo_OuterList.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_NestedList_InTrailingParagraph_AlignsTo_OuterList.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_NestedList_InTrailingParagraph_AlignsTo_OuterList.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_NestedList_With_MultipleParagraphs_Align.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_NestedList_With_MultipleParagraphs_Align.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_NestedLists_AlignTo_ParentLists.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_NestedLists_AlignTo_ParentLists.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_TrailingParagraphs_FirstLines_AlignTo_FirstParagraph.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_TrailingParagraphs_FirstLines_AlignTo_FirstParagraph.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_TrailingParagraphs_WrappedLines_AlignTo_FirstLines.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ListItemStyleTests/testThat_TrailingParagraphs_WrappedLines_AlignTo_FirstLines.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ThematicBreakSyleTests/testThat_ThematicBreak_CanBe_Indented.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ThematicBreakSyleTests/testThat_ThematicBreak_CanBe_Indented.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ThematicBreakSyleTests/testThat_ThematicBreak_InOffsetTextContainer_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ThematicBreakSyleTests/testThat_ThematicBreak_InOffsetTextContainer_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/Styler/__Snapshots__/ThematicBreakSyleTests/testThat_ThematicBreak_IsStyled.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnxnguyen/Down/e754ab1c80920dd51a8e08290c912ac1c2ac8b58/Tests/DownTests/Styler/__Snapshots__/ThematicBreakSyleTests/testThat_ThematicBreak_IsStyled.1.png
--------------------------------------------------------------------------------
/Tests/DownTests/__Snapshots__/BindingTests/testCommonMarkBindngsWork.1.txt:
--------------------------------------------------------------------------------
1 | ## [Down](https://github.com/johnxnnguyen/Down)
2 |
--------------------------------------------------------------------------------
/Tests/DownTests/__Snapshots__/BindingTests/testGroffBindingsWork.1.txt:
--------------------------------------------------------------------------------
1 | .SS
2 | Down (https://github.com/johnxnnguyen/Down)
3 |
--------------------------------------------------------------------------------
/Tests/DownTests/__Snapshots__/BindingTests/testHTMLBindingsWork.1.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Tests/DownTests/__Snapshots__/BindingTests/testLaTeXBindngsWork.1.txt:
--------------------------------------------------------------------------------
1 | \subsection{\href{https://github.com/johnxnnguyen/Down}{Down}}
2 |
--------------------------------------------------------------------------------
/Tests/DownTests/__Snapshots__/BindingTests/testXMLBindingsWork.1.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Down
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | - "Sources/cmark/"
3 | - "Down-Example/"
4 | - "Tests/"
5 |
6 | coverage:
7 | status:
8 | project:
9 | default:
10 | target: auto
11 | threshold: 3%
12 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | # You can set the Swift version to what you need for your app. Versions can be found here: https://hub.docker.com/_/swift
2 | FROM swift:5.1
3 |
4 | RUN apt-get -qq update \
5 | && apt-get -q -y install libssl-dev zlib1g-dev \
6 | && rm -r /var/lib/apt/lists/*
7 |
--------------------------------------------------------------------------------
/docker/README.md:
--------------------------------------------------------------------------------
1 | # Building on Linux (Docker)
2 |
3 | - Go to "docker" directory: `cd $REPO/docker`
4 | - Compose docket image: `docker-compose build`.
5 | - Build sources: `docker-compose run --rm down`.
6 | - Login docker image (i.e. to troubleshoot build failures): `docker-compose run --rm down bash`. After that you can run `swift build --package-path /app` or `cd /app && swift build`.
7 |
--------------------------------------------------------------------------------
/docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.3"
2 | services:
3 | down:
4 | build:
5 | context: .
6 | dockerfile: Dockerfile
7 | volumes:
8 | - ../:/app
9 | working_dir: /app
10 | stdin_open: true
11 | tty: true
12 | command: /app/docker/down-rebuild.sh
13 |
--------------------------------------------------------------------------------
/docker/down-rebuild.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "- Cleaning App"
4 | swift package clean
5 | if [ $? != 0 ]; then
6 | echo "❌ Linux build failed."
7 | exit 1
8 | fi
9 |
10 | echo "- Building App"
11 | swift build --configuration release
12 | if [ $? != 0 ]; then
13 | echo "❌ Linux build failed."
14 | exit 1
15 | fi
16 |
17 | echo "✅ Linux build completed!"
18 |
--------------------------------------------------------------------------------