├── .gitignore ├── .gitmodules ├── .swift-version ├── .travis.yml ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── LICENSE ├── Package.swift ├── README.org ├── Sources ├── Block.swift ├── Comment.swift ├── Constants.swift ├── ConvertToJSON.swift ├── Data.swift ├── Drawer.swift ├── Footnote.swift ├── HorizontalRule.swift ├── Info-iOS.plist ├── Info-macOS.plist ├── Inline.swift ├── Lexer.swift ├── List.swift ├── Node.swift ├── OrgDocument.swift ├── OrgFileWriter.swift ├── OrgParser.swift ├── Paragraph.swift ├── Queue.swift ├── Regex.swift ├── Section.swift ├── String.swift ├── SwiftOrg.h ├── Table.swift ├── Timestamp.swift └── Tokens.swift ├── SwiftOrg.podspec ├── SwiftOrg.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── SwiftOrg-iOS.xcscheme │ └── SwiftOrg-macOS.xcscheme ├── Tests ├── Info-iOS.plist ├── Info-macOS.plist ├── LinuxMain.swift └── SwiftOrgTests │ ├── Asserts.swift │ ├── ConvertToJSONTests.swift │ ├── IndexingTests.swift │ ├── InlineParsingTests.swift │ ├── LexerTests.swift │ ├── OrgFileWriterTests.swift │ ├── ParserTests.swift │ ├── PerformanceTests.swift │ ├── Template.swift │ ├── TimestampTests.swift │ ├── TokenExtensions.swift │ ├── TokenizerTests.swift │ ├── Utils.swift │ └── XCTestManifests.swift └── scripts ├── publish ├── setup └── test /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## OS X Finder 6 | .DS_Store 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xcuserstate 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots 68 | fastlane/test_output -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgapp/swift-org/bbc54e747ac8b6b15d651becdd33d0e5f07d6e5b/.gitmodules -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0.2 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode8.2 2 | language: objective-c 3 | xcode_project: SwiftOrg.xcodeproj 4 | script: 5 | - swift build 6 | - swift test 7 | deploy: 8 | - provider: script 9 | script: ./scripts/publish 10 | on: 11 | branch: master 12 | tags: true 13 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgapp/swift-org/bbc54e747ac8b6b15d651becdd33d0e5f07d6e5b/Cartfile -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgapp/swift-org/bbc54e747ac8b6b15d651becdd33d0e5f07d6e5b/Cartfile.private -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orgapp/swift-org/bbc54e747ac8b6b15d651becdd33d0e5f07d6e5b/Cartfile.resolved -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Xiaoxing Hu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "SwiftOrg" 5 | ) 6 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: SwiftOrg 2 | 3 | * org-mode Parser for Swift 4 | 5 | [[https://travis-ci.org/xiaoxinghu/swift-org.svg?branch=master]] 6 | [[https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000]] 7 | [[https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat]] 8 | [[https://img.shields.io/github/release/xiaoxinghu/swift-org.svg?maxAge=2592000]] 9 | 10 | [[http://orgmode.org/][org-mode]] is awesome. This is the first step to bring it to iOS, (arguably) the 11 | most popular platform on the planet. 12 | 13 | * Usage 14 | An simple example will explain everything. 15 | 16 | #+BEGIN_SRC swift 17 | import SwiftOrg 18 | 19 | let lines = [ 20 | "* TODO head line", 21 | " A normal line here.", 22 | ] 23 | let parser = OrgParser() 24 | let doc = try parser.parse(lines: lines) 25 | #+END_SRC 26 | 27 | * Supported Syntax (so far) [17/19] 28 | ** DONE Affiliated Keywords (aka In Buffer Settings) 29 | CLOSED: [2016-09-03 Sat 12:47] 30 | 31 | #+BEGIN_SRC org 32 | #+TITLE: Hello World 33 | #+OPTIONS: Hello World 34 | #+END_SRC 35 | 36 | ** DONE Headlines 37 | CLOSED: [2016-09-03 Sat 12:47] 38 | #+BEGIN_SRC org 39 | * Head Line 1 40 | * Head Line 2 41 | ** Head Line 2.1 42 | *** Head Line 2.1.1 43 | #+END_SRC 44 | 45 | ** DONE TODO Keywords 46 | CLOSED: [2016-12-31 Sat 14:12] 47 | #+BEGIN_SRC org 48 | ,#+TODO: TODO NEXT | DONE 49 | 50 | ,* TODO Head Line 1 51 | ,* NEXT Head Line 2 52 | ,** DONE Head Line 2.1 53 | CLOSED: [2016-12-31 Sat 14:12] 54 | #+END_SRC 55 | 56 | ** DONE [#A] Priority 57 | CLOSED: [2016-09-30 Fri 12:17] 58 | #+BEGIN_SRC org 59 | ,* TODO [#A] Top Priority Task 60 | ,* [#B] Medium Priority Item 61 | ,* TODO [#c] Low Priority Task 62 | ,* TODO [#D] No Priority Task 63 | #+END_SRC 64 | 65 | ** DONE Tags 66 | CLOSED: [2016-09-30 Fri 14:52] 67 | 68 | #+BEGIN_SRC org 69 | ,* Section with One Tag :tag1: 70 | ,* Section with multiple tags :tag1:tag2:tag3: 71 | #+END_SRC 72 | 73 | ** DONE Planning 74 | CLOSED: [2017-01-09 Mon 16:09] 75 | 76 | #+BEGIN_SRC org 77 | ,* DONE Closed task 78 | CLOSED: [2017-01-09 Mon 15:58] 79 | 80 | ,* Scheduled task 81 | SCHEDULED: <2017-01-09 Mon> 82 | 83 | ,* TODO task that has a deadline 84 | DEADLINE: <2017-01-16 Mon +1w> 85 | #+END_SRC 86 | 87 | ** DONE Paragraph 88 | CLOSED: [2016-09-03 Sat 12:47] 89 | Lines without *line breaker* becomes a *paragraph*. 90 | 91 | ** DONE Emphasis 92 | CLOSED: [2016-09-03 Sat 12:47] 93 | #+BEGIN_SRC org 94 | *bold* 95 | /italic/ 96 | _underlined_ 97 | =verbatim= 98 | ~code~ 99 | +strike-through+ 100 | #+END_SRC 101 | 102 | ** DONE Link 103 | CLOSED: [2016-09-03 Sat 12:47] 104 | #+BEGIN_SRC org 105 | [[google][https://www.google.com]] 106 | #+END_SRC 107 | 108 | ** DONE List 109 | CLOSED: [2016-09-03 Sat 12:47] 110 | #+BEGIN_SRC org 111 | # ordered list 112 | 1. first 113 | 2) second 114 | 3. 3rd 115 | 116 | # unordered list 117 | - item 118 | + item 119 | * item 120 | 121 | # nested list 122 | - item 123 | 1. sub item 124 | 1) sub item 125 | - item 126 | #+END_SRC 127 | 128 | ** DONE Horizontal rules 129 | CLOSED: [2016-09-03 Sat 12:47] 130 | #+BEGIN_SRC org 131 | Above. 132 | ----- 133 | Below 134 | #+END_SRC 135 | 136 | ** DONE Comment 137 | CLOSED: [2016-09-03 Sat 12:47] 138 | #+BEGIN_SRC org 139 | # This is a comment. 140 | #This is a regular line. 141 | #+END_SRC 142 | 143 | ** DONE Blocks 144 | CLOSED: [2016-09-03 Sat 12:47] 145 | #+BEGIN_SRC org 146 | ,#+BEGIN_SRC javascript 147 | Console.log("Hello Org.") 148 | ,#+END_SRC 149 | 150 | ,#+BEGIN_QUOTE 151 | Everything should be made as simple as possible, 152 | but not any simpler -- Albert Einstein 153 | ,#+END_QUOTE 154 | #+END_SRC 155 | 156 | ** DONE Drawer 157 | CLOSED: [2016-09-20 Tue 22:38] 158 | :PROPERTIES: 159 | :END: 160 | 161 | Drawer for headlines. 162 | 163 | #+BEGIN_SRC org 164 | ,* WAITING Talk to Jake 165 | :PROPERTIES: 166 | :CATEGORY: personal 167 | :END: 168 | :LOGBOOK: 169 | - State "WAITING" from "TODO" [2016-09-20 Tue 22:41] \\ 170 | waiting for call from Jake 171 | :END: 172 | #+END_SRC 173 | 174 | ** DONE Footnote 175 | CLOSED: [2016-09-27 Tue 21:24] 176 | #+BEGIN_SRC org 177 | This is a footnote right here[fn:1]. And this is the rest. 178 | 179 | [fn:1] The content of the footnote here. 180 | #+END_SRC 181 | 182 | ** DONE Checkbox 183 | CLOSED: [2016-09-27 Tue 21:28] 184 | Checkboxes in list items. 185 | #+BEGIN_SRC org 186 | - [X] item one checked 187 | - [-] item two not checked 188 | - [ ] item three not checked 189 | #+END_SRC 190 | 191 | ** DONE Table 192 | CLOSED: [2017-01-31 Tue 20:15] 193 | #+BEGIN_SRC org 194 | | Name | Species | Gender | Role | 195 | |--------------+------------+--------+--------------| 196 | | Bruce Wayne | Human | M | Batman | 197 | | Clark Kent | Kryptonian | M | Superman | 198 | | Diana Prince | Amazonian | F | Wonder Woman | 199 | #+END_SRC 200 | 201 | ** TODO Clock 202 | 203 | ** TODO Attachments 204 | 205 | * [#c] Maybe? [0/2] 206 | - [ ] Latex Support 207 | - [ ] Macros 208 | 209 | * TODO Performance Test 210 | 211 | * Contribute 212 | SwiftOrg is written in Swift 3. So you need xcode 8 to be able to build it. 213 | ** Setup 214 | #+BEGIN_SRC bash 215 | ./bin/setup 216 | ./bin/test 217 | #+END_SRC 218 | ** TODO Release 219 | To bump up version number. 220 | #+BEGIN_SRC sh :results silent 221 | agvtool new-marketing-version 0.7.9 222 | sed -i.bak "s/s\.version = .*/s\.version = '0\.7\.9'/" SwiftOrg.podspec 223 | #+END_SRC 224 | 225 | * License 226 | Carthage is released under the [[https://github.com/xiaoxinghu/swift-org/blob/master/LICENSE][MIT LIcense]]. 227 | -------------------------------------------------------------------------------- /Sources/Block.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Block.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 21/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Block: Node { 12 | public let name: String 13 | public let params: [String]? 14 | public var content: [String] = [] 15 | 16 | public init(name n: String, params p: [String]? = nil) { 17 | name = n 18 | params = p 19 | } 20 | 21 | public var description: String { 22 | return "Block(name: \(name), params: \(params), content: \(content))" 23 | } 24 | } 25 | 26 | extension OrgParser { 27 | func parseBlock() throws -> Node { 28 | guard case let (meta, Token.blockBegin(name, params)) = tokens.dequeue()! else { 29 | throw Errors.unexpectedToken("BlockBegin expected") 30 | } 31 | 32 | tokens.takeSnapshot() 33 | var block = Block(name: name, params: params) 34 | var result: Node! 35 | try self.lookAhead(match: { token in 36 | if case .blockEnd(let blockEndName) = token { 37 | return blockEndName.lowercased() == name.lowercased() 38 | } 39 | return false 40 | }, found: { token in 41 | result = block 42 | }, notYet: { tokenMeta in 43 | block.content.append(tokenMeta.raw!) 44 | }, failed: { 45 | tokens.restore() 46 | result = try parseParagraph(meta.raw!) 47 | }) 48 | return result 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Comment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Comment.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 21/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Comment: Node { 12 | public let text: String? 13 | 14 | public var description: String { 15 | return "Comment(text: \(text))" 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Sources/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 9/01/17. 6 | // Copyright © 2017 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum PlanningKeyword: String { 12 | case deadline = "DEADLINE" 13 | case scheduled = "SCHEDULED" 14 | case closed = "CLOSED" 15 | 16 | static let all = [deadline.rawValue, scheduled.rawValue, closed.rawValue] 17 | } 18 | 19 | public enum Priority: String { 20 | case A = "A" 21 | case B = "B" 22 | case C = "C" 23 | } 24 | -------------------------------------------------------------------------------- /Sources/ConvertToJSON.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol JSONable { 4 | func toJSON() -> [String: Any] 5 | } 6 | 7 | private extension Array { 8 | func toJSON() -> [[String: Any]] { 9 | return self 10 | .filter { $0 is JSONable } 11 | .map { ( $0 as! JSONable ).toJSON() } 12 | } 13 | } 14 | 15 | extension OrgDocument: JSONable { 16 | public func toJSON() -> [String: Any] { 17 | return [ 18 | "type": "document", 19 | "title": title ?? "", 20 | "settings": settings, 21 | "todos": todos, 22 | "content": content.toJSON() 23 | ] 24 | } 25 | } 26 | 27 | extension Section: JSONable { 28 | public func toJSON() -> [String: Any] { 29 | var dict: [String: Any] = [ 30 | "type": "section", 31 | "title": title ?? "", 32 | "stars": stars, 33 | "content": content.toJSON(), 34 | ] 35 | 36 | if let drawers = drawers { dict["drawers"] = drawers.toJSON() } 37 | if let keyword = keyword { dict["keyword"] = keyword } 38 | if let tags = tags { dict["tags"] = tags } 39 | if let planning = planning { dict["planning"] = planning.toJSON() } 40 | return dict 41 | } 42 | } 43 | 44 | extension Paragraph: JSONable { 45 | public func toJSON() -> [String: Any] { 46 | return [ 47 | "type": "paragraph", 48 | "text": text, 49 | ] 50 | } 51 | } 52 | 53 | extension Block: JSONable { 54 | public func toJSON() -> [String: Any] { 55 | return [ 56 | "type": "block", 57 | "name": name, 58 | "params": params ?? [], 59 | "content": content, 60 | ] 61 | } 62 | } 63 | 64 | extension List: JSONable { 65 | public func toJSON() -> [String: Any] { 66 | return [ 67 | "type": "list", 68 | "ordered": ordered, 69 | "items": items.toJSON(), 70 | ] 71 | } 72 | } 73 | 74 | extension ListItem: JSONable { 75 | public func toJSON() -> [String: Any] { 76 | 77 | var dict: [String: Any] = [ 78 | "type": "list_item", 79 | "text": text ?? "", 80 | ] 81 | 82 | if let subList = subList { dict["sub_list"] = subList.toJSON() } 83 | return dict 84 | } 85 | } 86 | 87 | extension Comment: JSONable { 88 | public func toJSON() -> [String: Any] { 89 | return [ 90 | "type": "comment", 91 | "text": text ?? "", 92 | ] 93 | } 94 | } 95 | 96 | extension Drawer: JSONable { 97 | public func toJSON() -> [String: Any] { 98 | return [ 99 | "type": "drawer", 100 | "name": name, 101 | "content": content, 102 | ] 103 | } 104 | } 105 | 106 | extension Footnote: JSONable { 107 | public func toJSON() -> [String: Any] { 108 | return [ 109 | "type": "footnote", 110 | "label": label, 111 | "content": content.toJSON(), 112 | ] 113 | } 114 | } 115 | 116 | extension Planning: JSONable { 117 | public func toJSON() -> [String: Any] { 118 | var dict: [String: Any] = [ 119 | "type": "planning", 120 | "keyword": keyword.rawValue, 121 | ] 122 | if let timestamp = timestamp { dict["timestamp"] = timestamp.toJSON() } 123 | return dict 124 | } 125 | } 126 | 127 | extension Timestamp: JSONable { 128 | public func toJSON() -> [String : Any] { 129 | var dict: [String: Any] = [ 130 | "active": active, 131 | "date": date.description, 132 | ] 133 | if let repeater = repeater { dict["repeater"] = repeater } 134 | return dict 135 | } 136 | } 137 | 138 | extension HorizontalRule: JSONable { 139 | public func toJSON() -> [String : Any] { 140 | return [ "type": "horizontal_rule" ] 141 | } 142 | } 143 | 144 | extension Table.Row: JSONable { 145 | public func toJSON() -> [String : Any] { 146 | return [ 147 | "has_seperator": hasSeparator, 148 | "cells": cells, 149 | ] 150 | } 151 | } 152 | 153 | extension Table: JSONable { 154 | 155 | public func toJSON() -> [String : Any] { 156 | return [ 157 | "rows": rows.toJSON() 158 | ] 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /Sources/Data.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 31/08/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | internal extension Array { 12 | func toQueue() -> Queue { 13 | return Queue(data: self) 14 | } 15 | } 16 | 17 | open class TreeNode { 18 | open var value: T 19 | open var parent: TreeNode? 20 | open var children = [TreeNode]() 21 | public init(value v: T) { 22 | value = v 23 | } 24 | 25 | func add(_ child: TreeNode) -> TreeNode { 26 | children.append(child) 27 | child.parent = self 28 | return child 29 | } 30 | 31 | func add(_ child: T) -> TreeNode { 32 | let c = TreeNode(value: child) 33 | c.parent = self 34 | children.append(c) 35 | return c 36 | } 37 | 38 | var isLeaf: Bool { 39 | return children.count == 0 40 | } 41 | 42 | var isRoot: Bool { 43 | return parent == nil 44 | } 45 | 46 | var depth: Int { 47 | if let p = parent { 48 | return p.depth + 1 49 | } 50 | return 0 51 | } 52 | 53 | open func lookUp(_ type: Type.Type) -> Type? { 54 | if let v = value as? Type { 55 | return v 56 | } 57 | if let p = parent { 58 | return p.lookUp(type) 59 | } 60 | return nil 61 | } 62 | } 63 | 64 | extension TreeNode: CustomStringConvertible { 65 | public var description: String { 66 | let prefix = String(repeating: "-", count: depth) 67 | var lines = ["\(prefix) \(value)"] 68 | if !children.isEmpty { 69 | lines += children.map { $0.description } 70 | } 71 | return lines.joined(separator: "\n") 72 | } 73 | } 74 | 75 | public struct Progress { 76 | public var total: Int = 0 77 | public var done: Int = 0 78 | 79 | public init(_ d: Int = 0, outof t: Int = 0) { 80 | total = t 81 | done = d 82 | } 83 | } 84 | 85 | extension Progress: Equatable { 86 | public static func ==(lhs: Progress, rhs: Progress) -> Bool { 87 | return lhs.total == rhs.total && lhs.done == rhs.done 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Sources/Drawer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Drawer.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 29/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Drawer: Node { 12 | public let name: String 13 | public var content: [String] 14 | 15 | public init(_ name: String, content: [String] = []) { 16 | self.name = name 17 | self.content = content 18 | } 19 | 20 | public var description: String { 21 | return "Drawer(name: \(name), content: \(content))" 22 | } 23 | } 24 | 25 | extension OrgParser { 26 | func parseDrawer() throws -> Node? { 27 | guard case let (meta, Token.drawerBegin(name)) = tokens.dequeue()! else { 28 | throw Errors.unexpectedToken("drawerBegin expected") 29 | } 30 | tokens.takeSnapshot() 31 | var drawer = Drawer(name) 32 | var result: Node! 33 | try lookAhead(match: { token in 34 | if case .drawerEnd = token { return true } 35 | return false 36 | }, found: { token in 37 | result = drawer 38 | }, notYet: { tokenMeta in 39 | drawer.content.append(tokenMeta.raw!) 40 | }, failed: { 41 | tokens.restore() 42 | result = try parseParagraph(meta.raw!) 43 | }) 44 | return result 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Footnote.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Footnote.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 27/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Footnote: Node { 12 | public var label: String 13 | public var content: [Node] = [] 14 | 15 | public var description: String { 16 | return "Footnote(content: \(content))" 17 | } 18 | } 19 | 20 | extension OrgParser { 21 | func parseFootnote() throws -> Footnote { 22 | guard case let(_, .footnote(label, content)) = tokens.dequeue()! else { 23 | throw Errors.unexpectedToken("footnote expected") 24 | } 25 | 26 | var footnote = Footnote(label: label, content: [try parseParagraph(content)!]) 27 | while let (_, token) = tokens.peek() { 28 | switch token { 29 | case .headline, .footnote: 30 | return footnote 31 | default: 32 | if let n = try parseTheRest() { 33 | footnote.content.append(n) 34 | } 35 | } 36 | } 37 | return footnote 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/HorizontalRule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HorizontalRule.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 21/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct HorizontalRule: Node { 12 | public var description: String { 13 | return "HorizontalRule" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Info-iOS.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 | 0.7.9 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Info-macOS.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 | 0.7.9 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2016 Xiaoxing Hu. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Inline.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InlineParser.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 29/08/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum InlineToken { 12 | case bold(String) 13 | case italic(String) 14 | case underlined(String) 15 | case strikeThrough(String) 16 | case verbatim(String) 17 | case code(String) 18 | case link(text: String, url: String) 19 | case plain(String) 20 | case footnote(String) 21 | } 22 | 23 | typealias InlineTokenGenerator = (String) -> InlineToken 24 | let markerList: [(String, InlineTokenGenerator)] = [ 25 | ("~", { text in .code(text) }), 26 | ("=", { text in .verbatim(text) }), 27 | ("*", { text in .bold(text) }), 28 | ("/", { text in .italic(text) }), 29 | ("_", { text in .underlined(text) }), 30 | ("+", { text in .strikeThrough(text) }), 31 | ] 32 | 33 | open class InlineLexer { 34 | 35 | var text: String 36 | var tokens: [InlineToken] 37 | var attr: [String : AnyObject] = [String : AnyObject]() 38 | public init(text t: String) { 39 | text = t 40 | tokens = [InlineToken.plain(text)] 41 | } 42 | 43 | fileprivate func tokenizeEmphasis() { 44 | for (marker, generator) in markerList { 45 | var newTokens = [InlineToken]() 46 | for token in tokens { 47 | if case let InlineToken.plain(text) = token { 48 | text.tryMatch("([\(marker)]+)([\\s\\S]+?)\\1", match: { m in 49 | newTokens.append(generator(m[2]!)) 50 | }, or: { text in newTokens.append(.plain(text)) }) 51 | } else { 52 | newTokens.append(token) 53 | } 54 | } 55 | tokens = newTokens 56 | } 57 | } 58 | 59 | fileprivate func tokenizeLink() { 60 | var newTokens = [InlineToken]() 61 | for token in tokens { 62 | if case let InlineToken.plain(text) = token { 63 | text.tryMatch("\\[\\[([^\\]]*)\\](?:\\[([^\\]]*)\\])?\\]", match: { m in 64 | newTokens.append(.link(text: m[2]!, url: m[1]!)) 65 | }, or: { text in newTokens.append(.plain(text)) }) 66 | } else { 67 | newTokens.append(token) 68 | } 69 | } 70 | tokens = newTokens 71 | } 72 | 73 | fileprivate func tokenizeFootnote() { 74 | var newTokens = [InlineToken]() 75 | for token in tokens { 76 | if case let InlineToken.plain(text) = token { 77 | text.tryMatch("\\[fn:(\\d+)\\]", match: { m in 78 | newTokens += [.footnote(m[1]!)] 79 | }, or: { text in newTokens.append(.plain(text)) }) 80 | } else { 81 | newTokens += [token] 82 | } 83 | } 84 | tokens = newTokens 85 | } 86 | 87 | open func tokenize() -> [InlineToken] { 88 | tokens = [InlineToken.plain(text)] 89 | tokenizeLink() 90 | tokenizeEmphasis() 91 | tokenizeFootnote() 92 | return tokens 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Sources/Lexer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Lexer.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 14/08/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum LexerErrors: Error { 12 | case tokenizeFailed(Int, String) 13 | } 14 | 15 | open class Lexer { 16 | let lines: [String] 17 | public init(lines theLines: [String]) { 18 | lines = theLines 19 | } 20 | 21 | 22 | /// Tokenize one line, without considering the context 23 | /// 24 | /// - Parameter line: the target line 25 | /// - Returns: the token 26 | class func tokenize(line: String) -> Token? { 27 | defineTokens() 28 | for td in tokenDescriptors { 29 | guard let m = line.match(td.pattern, options: td.options) else { continue } 30 | return td.generator(m) 31 | } 32 | return nil 33 | } 34 | 35 | func tokenize(cursor: Int = 0, tokens: [TokenInfo] = []) throws -> [TokenInfo] { 36 | 37 | if lines.count == cursor { return tokens } 38 | let line = lines[cursor] 39 | 40 | guard let token = Lexer.tokenize(line: line) else { 41 | throw LexerErrors.tokenizeFailed(cursor, line) 42 | } 43 | 44 | return try tokenize( 45 | cursor: cursor + 1, 46 | tokens: tokens + [(TokenMeta(raw: line, lineNumber: cursor), token)]) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/List.swift: -------------------------------------------------------------------------------- 1 | // 2 | // List.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 21/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct ListItem: Node { 12 | public let text: String? 13 | public var checked: Bool? 14 | public var subList: List? 15 | 16 | public init(text t: String? = nil, checked c: Bool? = nil, list l: List? = nil) { 17 | text = t 18 | subList = l 19 | checked = c 20 | } 21 | public var description: String { 22 | return "ListItem(text: \(text), checked: \(checked), subList: \(subList))" 23 | } 24 | } 25 | 26 | public struct List: Node { 27 | public var items: [ListItem] 28 | public var ordered: Bool 29 | public var progress: Progress { 30 | var progress = Progress() 31 | for item in items { 32 | if let checked = item.checked { 33 | progress.total += 1 34 | if checked { 35 | progress.done += 1 36 | } 37 | } 38 | } 39 | return progress 40 | } 41 | 42 | public init(ordered o: Bool, items i: [ListItem] = []) { 43 | ordered = o 44 | items = i 45 | } 46 | 47 | public var description: String { 48 | return "List(ordered: \(ordered), items: \(items))" 49 | } 50 | } 51 | 52 | extension OrgParser { 53 | func parseList() throws -> List { 54 | guard case let (_, Token.listItem(indent, text, ordered, checked)) = tokens.dequeue()! else { 55 | throw Errors.unexpectedToken("ListItem expected") 56 | } 57 | var list = List(ordered: ordered) 58 | list.items = [ListItem(text: text, checked: checked)] 59 | while let (_, token) = tokens.peek() { 60 | if case let .listItem(i, t, _, c) = token { 61 | if i > indent { 62 | var lastItem = list.items.removeLast() 63 | lastItem.subList = try parseList() 64 | list.items += [lastItem] 65 | } else if i == indent { 66 | _ = tokens.dequeue() 67 | list.items += [ListItem(text: t, checked: c)] 68 | } else { 69 | break 70 | } 71 | } else { 72 | break 73 | } 74 | } 75 | 76 | return list 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/Node.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Nodes.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 22/08/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Index of elements within org file. 12 | public struct OrgIndex: CustomStringConvertible, Hashable { 13 | var indexes: [Int] 14 | 15 | public init(_ theIndexes: [Int] = [0]) { 16 | indexes = theIndexes 17 | } 18 | 19 | public var out: OrgIndex { 20 | var newIndex = indexes 21 | newIndex.removeLast() 22 | return OrgIndex(newIndex) 23 | } 24 | 25 | public var `in`: OrgIndex { 26 | var newIndex = indexes 27 | newIndex.append(0) 28 | return OrgIndex(newIndex) 29 | } 30 | 31 | public var next: OrgIndex { 32 | var newIndex = indexes 33 | newIndex[newIndex.endIndex - 1] = newIndex.last! + 1 34 | return OrgIndex(newIndex) 35 | } 36 | 37 | public var prev: OrgIndex { 38 | var newIndex = indexes 39 | newIndex[newIndex.endIndex - 1] = newIndex.last! - 1 40 | return OrgIndex(newIndex) 41 | } 42 | 43 | public var description: String { 44 | return indexes.map { n in 45 | return "\(n)" 46 | }.joined(separator: ".") 47 | } 48 | 49 | public var hashValue: Int { 50 | return description.hashValue 51 | } 52 | 53 | public static func == (lhs: OrgIndex, rhs: OrgIndex) -> Bool { 54 | return lhs.hashValue == rhs.hashValue 55 | } 56 | } 57 | 58 | public protocol Node: CustomStringConvertible { 59 | var index: OrgIndex? { get } 60 | } 61 | 62 | extension Node { 63 | public var index: OrgIndex? { return nil } 64 | } 65 | 66 | extension OrgParser { 67 | func parseTheRest() throws -> Node? { 68 | guard let (_, token) = tokens.peek() else { 69 | return nil 70 | } 71 | switch token { 72 | case .blank: 73 | _ = tokens.dequeue() 74 | return nil 75 | case .line: 76 | return try parseParagraph() 77 | case let .comment(t): 78 | _ = tokens.dequeue() 79 | return Comment(text: t) 80 | case .blockBegin: 81 | return try parseBlock() 82 | case .drawerBegin: 83 | return try parseDrawer() 84 | case .listItem: 85 | return try parseList() 86 | case .planning(let keyword, let timestamp): 87 | _ = tokens.dequeue() 88 | return Planning(keyword: keyword, timestamp: timestamp) 89 | case .tableRow, .horizontalSeparator: 90 | return try parseTable() 91 | case .horizontalRule: 92 | _ = tokens.dequeue() 93 | return HorizontalRule() 94 | default: 95 | throw Errors.unexpectedToken("\(token) is not expected") 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Sources/OrgDocument.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OrgDocument.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 21/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct OrgDocument: Node { 12 | public var settings = [String: String]() 13 | public var content = [Node]() 14 | 15 | public var title: String? { 16 | return settings["TITLE"] 17 | } 18 | 19 | public let defaultTodos: [[String]] 20 | 21 | public var todos: [[String]] { 22 | if let todo = settings["TODO"] { 23 | let keywords = todo.components(separatedBy: .whitespaces) 24 | var result: [[String]] = [[]] 25 | for keyword in keywords { 26 | if keyword == "|" { 27 | result.append([]) 28 | } else { 29 | result[result.endIndex - 1].append(keyword) 30 | } 31 | } 32 | return result 33 | } 34 | return defaultTodos 35 | } 36 | 37 | public init(todos: [[String]]) { 38 | defaultTodos = todos 39 | } 40 | 41 | public var description: String { 42 | return "OrgDocument(settings: \(settings))\n - \(content)" 43 | } 44 | } 45 | 46 | extension OrgParser { 47 | func parseDocument() throws -> OrgDocument { 48 | var index = OrgIndex([0]) 49 | while let (_, token) = tokens.peek() { 50 | switch token { 51 | case let .setting(key, value): 52 | _ = tokens.dequeue() 53 | document.settings[key] = value 54 | default: 55 | if let node = try parseSection(index) { 56 | document.content.append(node) 57 | index = index.next 58 | } 59 | } 60 | } 61 | return document 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/OrgFileWriter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OrgFileWriter.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 29/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol Textifiable { 12 | func textify(indent: Int) -> [String] 13 | } 14 | 15 | extension Section: Textifiable { 16 | func textify(indent: Int) -> [String] { 17 | var lines = [String]() 18 | var headlineComponents = [String]() 19 | headlineComponents.append(String(repeating: "*", count: stars)) 20 | if let k = keyword { 21 | headlineComponents.append(k) 22 | } 23 | if let p = priority { 24 | headlineComponents.append("[#\(p)]") 25 | } 26 | if let t = title { 27 | headlineComponents.append(t) 28 | } 29 | lines += [headlineComponents.joined(separator: " ")] 30 | 31 | // write drawers 32 | 33 | for d in drawers ?? [] { 34 | lines += d.textify(indent: stars + 1) 35 | } 36 | 37 | for n in content { 38 | if let tn = n as? Textifiable { 39 | lines += tn.textify(indent: stars + 1) 40 | } 41 | } 42 | lines += ["\n"] 43 | return lines 44 | } 45 | } 46 | 47 | extension Paragraph: Textifiable { 48 | func textify(indent: Int) -> [String] { 49 | return lines.map { line in line.indent(indent) } 50 | } 51 | } 52 | 53 | extension Block: Textifiable { 54 | func textify(indent: Int) -> [String] { 55 | var begin = ["#+BEGIN_\(name.uppercased())"] 56 | if let p = params { 57 | begin += p 58 | } 59 | var lines = [begin.joined(separator: " ").indent(indent)] 60 | lines += content 61 | lines += ["#+END_\(name.uppercased())".indent(indent)] 62 | return lines 63 | } 64 | } 65 | 66 | extension List: Textifiable { 67 | func textify(indent: Int) -> [String] { 68 | // TODO impl sublist for Textifiable 69 | 70 | return items.enumerated().map { index, item in 71 | var parts = [ordered ? "\(index)." : "-"] 72 | if let c = item.checked { 73 | parts += [c ? "[X]" : "[ ]"] 74 | } 75 | parts += [item.text ?? ""] 76 | return parts.joined(separator: " ").indent(indent) 77 | } 78 | } 79 | } 80 | 81 | extension HorizontalRule: Textifiable { 82 | func textify(indent: Int) -> [String] { 83 | return ["-----"] 84 | } 85 | } 86 | 87 | extension Comment: Textifiable { 88 | func textify(indent: Int) -> [String] { 89 | return ["# \(text ?? "")"] 90 | } 91 | } 92 | 93 | extension Footnote: Textifiable { 94 | func textify(indent: Int) -> [String] { 95 | // TODO impl footnote for Textifiable 96 | return [] 97 | } 98 | } 99 | 100 | extension Drawer: Textifiable { 101 | func textify(indent: Int) -> [String] { 102 | var lines = [":\(name.uppercased()):"] 103 | lines += content 104 | lines += [":END:"] 105 | return lines.map { line in line.indent(indent) } 106 | } 107 | } 108 | 109 | extension OrgDocument: Textifiable { 110 | 111 | func textify(indent: Int = 0) -> [String] { 112 | var lines = [String]() 113 | let settingLines = settings.map { k, v in "#+\(k): \(v)" } 114 | lines.append(contentsOf: settingLines) 115 | 116 | for node in content { 117 | if let n = node as? Textifiable { 118 | lines += n.textify(indent: indent) 119 | } 120 | } 121 | return lines 122 | } 123 | 124 | public func toText() -> String { 125 | return self.textify().joined(separator: "\n") 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Sources/OrgParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 22/08/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Errors: Error { 12 | case unexpectedToken(String) 13 | case cannotFindToken(String) 14 | } 15 | 16 | public class OrgParser { 17 | // MARK: properties 18 | var tokens: Queue! 19 | var document: OrgDocument! 20 | public var defaultTodos: [[String]] 21 | 22 | // MARK: init 23 | public init(defaultTodos: [[String]] = [["TODO"], ["DONE"]]) { 24 | self.defaultTodos = defaultTodos 25 | } 26 | 27 | func skipBlanks() { 28 | while let (_, token) = tokens.peek(), case .blank = token { 29 | _ = tokens.dequeue() 30 | } 31 | } 32 | 33 | /// look ahead for matching tokens 34 | /// 35 | /// - Parameters: 36 | /// - match: matcher 37 | /// - found: callback when found the target 38 | /// - notYet: callback for not yet 39 | /// - Failed: failed to find the match 40 | /// - Throws: Lexer Error 41 | func lookAhead( 42 | match: (Token) -> Bool, 43 | found: (Token) -> Void, 44 | notYet: (TokenMeta) -> Void, 45 | failed: () throws -> Void) throws { 46 | 47 | guard let (meta, token) = tokens.dequeue() else { 48 | try failed() 49 | return 50 | } 51 | 52 | if match(token) { 53 | found(token) 54 | } else { 55 | notYet(meta) 56 | try lookAhead(match: match, found: found, notYet: notYet, failed: failed) 57 | } 58 | } 59 | 60 | // MARK: parse document 61 | func parse(tokens: [TokenInfo]) throws -> OrgDocument { 62 | self.tokens = Queue(data: tokens) 63 | document = OrgDocument(todos: defaultTodos) 64 | return try parseDocument() 65 | } 66 | 67 | public func parse(lines: [String]) throws -> OrgDocument { 68 | let lexer = Lexer(lines: lines) 69 | return try parse(tokens: lexer.tokenize()) 70 | } 71 | 72 | public func parse(content: String) throws -> OrgDocument { 73 | return try parse(lines: content.lines) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Sources/Paragraph.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Paragraph.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 21/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Paragraph: Node { 12 | public var lines: [String] 13 | public var text: String { 14 | return lines.joined(separator: " ") 15 | } 16 | public var parsed: [InlineToken] { 17 | return InlineLexer(text: text).tokenize() 18 | } 19 | 20 | public var description: String { 21 | return "Paragraph(text: \(text))" 22 | } 23 | } 24 | 25 | extension OrgParser { 26 | func parseParagraph(_ startWith: String? = nil) throws -> Paragraph? { 27 | var paragraph: Paragraph? = nil 28 | if let firstLine = startWith { 29 | paragraph = Paragraph(lines: [firstLine]) 30 | } 31 | while let (_, token) = tokens.peek() { 32 | if case .line(let t) = token { 33 | paragraph = paragraph ?? Paragraph(lines: []) 34 | paragraph?.lines.append(t) 35 | _ = tokens.dequeue() 36 | } else { 37 | break 38 | } 39 | } 40 | return paragraph 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Queue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Queue.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 30/01/17. 6 | // Copyright © 2017 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | internal struct Queue { 12 | var array = [T]() 13 | var snapshot: [T]? 14 | 15 | init(data: [T]) { 16 | array = data 17 | } 18 | 19 | var isEmpty: Bool { 20 | return array.isEmpty 21 | } 22 | 23 | mutating func dequeue() -> T? { 24 | if isEmpty { 25 | return nil 26 | } 27 | return array.removeFirst() 28 | } 29 | 30 | mutating func swapNext(with element: T) { 31 | _ = dequeue() 32 | array.insert(element, at: 0) 33 | } 34 | 35 | func peek() -> T? { 36 | return array.first 37 | } 38 | 39 | mutating func takeSnapshot() { 40 | snapshot = array 41 | } 42 | 43 | mutating func restore() { 44 | if let s = snapshot { 45 | array = s 46 | } 47 | } 48 | } 49 | 50 | extension Queue : CustomStringConvertible { 51 | internal var description: String { 52 | return array.map{i in "\(i)"}.joined(separator: "\n") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/Regex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Regex.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 14/08/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | #if !os(Linux) 12 | typealias RegularExpression = NSRegularExpression 13 | typealias TextCheckingResult = NSTextCheckingResult 14 | #else 15 | extension TextCheckingResult { 16 | func rangeAt(_ idx: Int) -> NSRange { 17 | return range(at: idx) 18 | } 19 | } 20 | #endif 21 | 22 | var expressions = [String: RegularExpression]() 23 | 24 | public extension String { 25 | 26 | /// Cache regex (for performance and resource sake) 27 | /// 28 | /// - Parameters: 29 | /// - regex: regex pattern 30 | /// - options: options 31 | /// - Returns: the regex 32 | private func getExpression(_ regex: String, options: RegularExpression.Options) -> RegularExpression { 33 | let expression: RegularExpression 34 | if let exists = expressions[regex] { 35 | expression = exists 36 | } else { 37 | expression = try! RegularExpression(pattern: regex, options: options) 38 | expressions[regex] = expression 39 | } 40 | return expression 41 | } 42 | 43 | private func getMatches(_ match: TextCheckingResult) -> [String?] { 44 | var matches = [String?]() 45 | switch match.numberOfRanges { 46 | case 0: 47 | return [] 48 | case let n where n > 0: 49 | for i in 0.. 0 ? NSString(string: self).substring(with: r) : nil) 52 | } 53 | default: 54 | return [] 55 | } 56 | return matches 57 | } 58 | 59 | public func match(_ regex: String, options: RegularExpression.Options = []) -> [String?]? { 60 | let expression = self.getExpression(regex, options: options) 61 | 62 | if let match = expression.firstMatch(in: self, options: [], range: NSMakeRange(0, self.utf16.count)) { 63 | return getMatches(match) 64 | } 65 | return nil 66 | } 67 | 68 | public func matchSplit(_ regex: String, options: RegularExpression.Options) -> [String] { 69 | let expression = self.getExpression(regex, options: options) 70 | let matches = expression.matches(in: self, options: [], range: NSMakeRange(0, self.utf16.count)) 71 | var splitted = [String]() 72 | var cursor = 0 73 | for m in matches { 74 | if m.range.location > cursor { 75 | splitted.append(self.substring(with: self.characters.index(self.startIndex, offsetBy: cursor).. Void, 89 | or: (String) -> Void) { 90 | let expression = self.getExpression(regex, options: options) 91 | let matches = expression.matches(in: self, options: [], range: NSMakeRange(0, self.utf16.count)) 92 | var cursor = 0 93 | for m in matches { 94 | if m.range.location > cursor { 95 | or(self.substring(with: self.characters.index(self.startIndex, offsetBy: cursor).. Node? { 71 | skipBlanks() // in a section, you don't care about blanks 72 | 73 | guard let (_, token) = tokens.peek() else { 74 | return nil 75 | } 76 | switch token { 77 | case let .headline(l, t): 78 | if l < index.indexes.count { 79 | return nil 80 | } 81 | _ = tokens.dequeue() 82 | var section = Section(stars: l, title: t, todos: document.todos.flatMap{ $0 }) 83 | section.index = index 84 | var subIndex = index.in 85 | while let subSection = try parseSection(subIndex) { 86 | section.content.append(subSection) 87 | subIndex = subIndex.next 88 | } 89 | return section 90 | case .footnote: 91 | return try parseFootnote() 92 | default: 93 | return try parseTheRest() 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Sources/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 15/08/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public func multiline(_ x: String...) -> String { 12 | return x.joined(separator: "\n") 13 | } 14 | 15 | extension String { 16 | var lines: [String] { return self.components(separatedBy: CharacterSet.newlines) } 17 | var trimmed: String { 18 | return self.trimmingCharacters(in: CharacterSet.whitespaces) 19 | } 20 | 21 | func indent(_ n: Int) -> String { 22 | return "\(String(repeating: " ", count: n))\(self)" 23 | } 24 | } 25 | 26 | func length(_ text: String?) -> Int { 27 | return (text ?? "").characters.count 28 | } 29 | -------------------------------------------------------------------------------- /Sources/SwiftOrg.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOrg.h 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 7/12/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for SwiftOrg. 12 | FOUNDATION_EXPORT double SwiftOrgVersionNumber; 13 | 14 | //! Project version string for SwiftOrg. 15 | FOUNDATION_EXPORT const unsigned char SwiftOrgVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sources/Table.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Table.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 30/01/17. 6 | // Copyright © 2017 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Table: Node { 12 | 13 | public struct Row { 14 | public var cells: [String] = [] 15 | public var hasSeparator: Bool = false 16 | 17 | init(_ theCells: [String], hasSeparator itHasSeparator: Bool = false) { 18 | cells = theCells 19 | hasSeparator = itHasSeparator 20 | } 21 | } 22 | 23 | public var rows: [Row] = [] 24 | 25 | public var description: String { 26 | let data = rows.map { row in 27 | return "\(row)" 28 | }.joined(separator: "\n") 29 | return "Table(rows:\n\(data))" 30 | } 31 | } 32 | 33 | extension OrgParser { 34 | func parseTable() throws -> Table { 35 | var table = Table() 36 | while let (_, token) = tokens.peek() { 37 | switch token { 38 | case .tableRow(let cells): 39 | _ = tokens.dequeue() 40 | table.rows.append(Table.Row(cells)) 41 | case .horizontalSeparator: 42 | _ = tokens.dequeue() 43 | if !table.rows.isEmpty { 44 | table.rows[table.rows.count - 1] 45 | = Table.Row(table.rows.last!.cells, hasSeparator: true) 46 | } 47 | default: 48 | return table 49 | } 50 | } 51 | return table 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Timestamp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timestamp.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 8/01/17. 6 | // Copyright © 2017 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Timestamp { 12 | public let active: Bool 13 | public let date: Date 14 | public let repeater: String? 15 | 16 | static func from(string: String) -> Timestamp? { 17 | let markPattern = "\\+|\\+\\+|\\.\\+|-|--" 18 | let pattern = "^(<|\\[)(.+?)(?: (\(markPattern))(\\d+)([hdwmy])\\s*)?(>|])$" 19 | guard let m = string.trimmed.match(pattern) else { 20 | return nil 21 | } 22 | 23 | let formater = DateFormatter() 24 | let formats = [ 25 | "yyyy-MM-dd EEE H:mm", 26 | "yyyy-MM-dd EEE", 27 | ] 28 | 29 | for format in formats { 30 | formater.dateFormat = format 31 | // It acts differently in Linux and !Linux env 32 | guard let date = formater.date(from: m[2]!) else { continue } 33 | let active = m[1]! == "<" 34 | 35 | var repeater: String? = nil 36 | if let mark = m[3], let value = m[4], let unit = m[5] { 37 | repeater = "\(mark)\(value)\(unit)" 38 | } 39 | let timestamp = Timestamp(active: active, date: date, repeater: repeater) 40 | return timestamp 41 | } 42 | return nil 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Tokens.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tokens.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 13/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | public struct TokenMeta { 13 | public let raw: String? 14 | public let lineNumber: Int 15 | } 16 | 17 | typealias TokenInfo = (TokenMeta, Token) 18 | 19 | public enum Token { 20 | case setting(key: String, value: String?) 21 | case headline(stars: Int, text: String?) 22 | case planning(keyword: PlanningKeyword, timestamp: Timestamp?) 23 | case blank 24 | case horizontalRule 25 | case blockBegin(name: String, params: [String]?) 26 | case blockEnd(name: String) 27 | case drawerBegin(name: String) 28 | case drawerEnd 29 | case listItem(indent: Int, text: String?, ordered: Bool, checked: Bool?) 30 | case comment(String?) 31 | case line(text: String) 32 | case footnote(label: String, content: String?) 33 | case tableRow(cells: [String]) 34 | case horizontalSeparator 35 | case raw(String) 36 | } 37 | 38 | typealias TokenGenerator = ([String?]) -> Token 39 | 40 | struct TokenDescriptor { 41 | let pattern: String 42 | let options: RegularExpression.Options 43 | let generator: TokenGenerator 44 | 45 | 46 | init(_ thePattern: String, 47 | options theOptions: RegularExpression.Options = [], 48 | generator theGenerator: @escaping TokenGenerator = { matches in .raw(matches[0]!) }) { 49 | pattern = thePattern 50 | options = theOptions 51 | generator = theGenerator 52 | } 53 | } 54 | 55 | func define(_ pattern: String, 56 | options: RegularExpression.Options = [], 57 | generator: @escaping TokenGenerator) { 58 | tokenDescriptors.append( 59 | TokenDescriptor(pattern, 60 | options: options, 61 | generator: generator)) 62 | } 63 | 64 | var tokenDescriptors: [TokenDescriptor] = [] 65 | 66 | func defineTokens() { 67 | if tokenDescriptors.count > 0 {return} 68 | 69 | define("^\\s*$") { _ in .blank } 70 | 71 | define("^#\\+([a-zA-Z_]+):\\s*(.*)$") { matches in 72 | .setting(key: matches[1]!, value: matches[2]) } 73 | 74 | define("^(\\*+)\\s+(.*)$") { matches in 75 | .headline(stars: matches[1]!.characters.count, text: matches[2]) } 76 | 77 | define("^\\s*(\(PlanningKeyword.all.joined(separator: "|"))):\\s+(.+)$") { matches in 78 | let timestamp = Timestamp.from(string: matches[2]!) 79 | return .planning(keyword: PlanningKeyword(rawValue: matches[1]!)!, timestamp: timestamp) 80 | } 81 | // Block 82 | define("^(\\s*)#\\+begin_([a-z]+)(?:\\s+(.*))?$", 83 | options: [.caseInsensitive]) 84 | { matches in 85 | var params: [String]? 86 | if let m3 = matches[3] { 87 | params = m3.characters.split{$0 == " "}.map(String.init) 88 | } 89 | return .blockBegin(name: matches[2]!, params: params) 90 | } 91 | 92 | define("^(\\s*)#\\+end_([a-z]+)$", options: [.caseInsensitive]) { matches in 93 | .blockEnd(name: matches[2]!) } 94 | 95 | // Drawer 96 | 97 | define("^(\\s*):end:\\s*$", options: [.caseInsensitive]) { _ in .drawerEnd } 98 | 99 | define("^(\\s*):([a-z]+):\\s*$", 100 | options: [.caseInsensitive]) { matches in 101 | .drawerBegin(name: matches[2]!) } 102 | 103 | define("^(\\s*)([-+*]|\\d+(?:\\.|\\)))\\s+(?:\\[([ X-])\\]\\s+)?(.*)$") { matches in 104 | var ordered = true 105 | if let m = matches[2] { 106 | ordered = !["-", "+", "*"].contains(m) 107 | } 108 | var checked: Bool? = nil 109 | if let m = matches[3] { 110 | checked = m == "X" 111 | } 112 | return .listItem(indent: length(matches[1]), text: matches[4], ordered: ordered, checked: checked) } 113 | 114 | define("^\\s*-{5,}$") { _ in .horizontalRule } 115 | 116 | define("^\\s*#\\s+(.*)$") { matches in 117 | .comment(matches[1]) } 118 | 119 | define("^\\[fn:(\\d+)\\](?:\\s+(.*))?$") { matches in 120 | .footnote(label: matches[1]!, content: matches[2]) 121 | } 122 | 123 | // Table 124 | 125 | define("\\s*\\|-.*$") { matches in 126 | return .horizontalSeparator 127 | } 128 | 129 | define("^\\s*\\|(?:[^\\r\\n\\|]*\\|?)+$") { matches in 130 | let cells = matches[0]! 131 | .components(separatedBy: "|") 132 | .map { $0.trimmed } 133 | .filter { !$0.isEmpty } 134 | return .tableRow(cells: cells) 135 | } 136 | 137 | define("^(\\s*)(.*)$") { matches in 138 | .line(text: matches[2]!) } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /SwiftOrg.podspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | Pod::Spec.new do |s| 3 | s.name = 'SwiftOrg' 4 | s.version = '0.7.9' 5 | s.summary = 'org-mode with swift' 6 | s.homepage = 'https://github.com/xiaoxinghu/swift-org' 7 | s.license = { type: 'MIT', file: 'LICENSE' } 8 | s.author = { 'Xiaoxing Hu' => 'dawnstar.hu@gmail.com' } 9 | s.social_media_url = 'http://twitter.com/xiaoxinghu' 10 | s.ios.deployment_target = '8.0' 11 | s.osx.deployment_target = '10.10' 12 | s.source = { :git => 'https://github.com/xiaoxinghu/swift-org.git', :tag => "#{s.version}" } 13 | s.source_files = 'Sources/**/*.{swift,h}' 14 | end 15 | -------------------------------------------------------------------------------- /SwiftOrg.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 03520B5A1E4D9209003345D5 /* Cartfile in Resources */ = {isa = PBXBuildFile; fileRef = 03520B571E4D9209003345D5 /* Cartfile */; }; 11 | 03520B5C1E4D9209003345D5 /* SwiftOrg.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 03520B591E4D9209003345D5 /* SwiftOrg.podspec */; }; 12 | 03520BE81E5C3785003345D5 /* ParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03520BE71E5C3785003345D5 /* ParserTests.swift */; }; 13 | 03520BE91E5C3785003345D5 /* ParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03520BE71E5C3785003345D5 /* ParserTests.swift */; }; 14 | 035A617E1E3F11C7001CE563 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035A617D1E3F11C7001CE563 /* Queue.swift */; }; 15 | 035A617F1E3F11C7001CE563 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035A617D1E3F11C7001CE563 /* Queue.swift */; }; 16 | 035A61811E3F3BBA001CE563 /* Table.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035A61801E3F3BBA001CE563 /* Table.swift */; }; 17 | 035A61821E3F3BBA001CE563 /* Table.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035A61801E3F3BBA001CE563 /* Table.swift */; }; 18 | 035EEA1F1D8D504A00A38F7E /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035EEA1E1D8D504A00A38F7E /* Utils.swift */; }; 19 | 035EEA271D8D526F00A38F7E /* InlineParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035EEA261D8D526F00A38F7E /* InlineParsingTests.swift */; }; 20 | 036C5E821D928A85007484CF /* README.org in Resources */ = {isa = PBXBuildFile; fileRef = 036C5E811D928A85007484CF /* README.org */; }; 21 | 037BFFB81E6406A3005EB02F /* ConvertToJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037BFFB71E6406A3005EB02F /* ConvertToJSON.swift */; }; 22 | 037BFFBA1E640710005EB02F /* ConvertToJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037BFFB91E640710005EB02F /* ConvertToJSONTests.swift */; }; 23 | 037BFFBB1E640710005EB02F /* ConvertToJSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037BFFB91E640710005EB02F /* ConvertToJSONTests.swift */; }; 24 | 037BFFBC1E640714005EB02F /* ConvertToJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037BFFB71E6406A3005EB02F /* ConvertToJSON.swift */; }; 25 | 0382A1881DF826730015B475 /* SwiftOrg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03BBC3BD1DF81104003FCB3C /* SwiftOrg.framework */; }; 26 | 0382A1901DF827080015B475 /* Asserts.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70C22501D8A1D6A007796E2 /* Asserts.swift */; }; 27 | 0382A1911DF827080015B475 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035EEA1E1D8D504A00A38F7E /* Utils.swift */; }; 28 | 0382A1921DF827080015B475 /* TokenExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70C22521D8A204F007796E2 /* TokenExtensions.swift */; }; 29 | 0382A1931DF827080015B475 /* TokenizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F9E6AB1D60411A0080E872 /* TokenizerTests.swift */; }; 30 | 0382A19A1DF827080015B475 /* InlineParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035EEA261D8D526F00A38F7E /* InlineParsingTests.swift */; }; 31 | 0382A19B1DF827080015B475 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7B688451D8B74FA009E2FB1 /* PerformanceTests.swift */; }; 32 | 0382A19D1DF827080015B475 /* OrgFileWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A77DDD591D9C975D00120BA7 /* OrgFileWriterTests.swift */; }; 33 | 0382A19E1DF827080015B475 /* IndexingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A78044F31DDD06D000C8CE46 /* IndexingTests.swift */; }; 34 | 0382A19F1DF828700015B475 /* README.org in Resources */ = {isa = PBXBuildFile; fileRef = 036C5E811D928A85007484CF /* README.org */; }; 35 | 0382A1A11DF9713B0015B475 /* LexerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0382A1A01DF9713B0015B475 /* LexerTests.swift */; }; 36 | 03AF26331DF97342002E71A7 /* LexerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0382A1A01DF9713B0015B475 /* LexerTests.swift */; }; 37 | 03BBC3DD1DF817E0003FCB3C /* SwiftOrg.h in Headers */ = {isa = PBXBuildFile; fileRef = 03BBC3D91DF817DB003FCB3C /* SwiftOrg.h */; settings = {ATTRIBUTES = (Public, ); }; }; 38 | 03BBC3DE1DF817E1003FCB3C /* SwiftOrg.h in Headers */ = {isa = PBXBuildFile; fileRef = 03BBC3D91DF817DB003FCB3C /* SwiftOrg.h */; settings = {ATTRIBUTES = (Public, ); }; }; 39 | 03BBC4091DF818E6003FCB3C /* Block.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E11DF81863003FCB3C /* Block.swift */; }; 40 | 03BBC40A1DF818E6003FCB3C /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E21DF81863003FCB3C /* Comment.swift */; }; 41 | 03BBC40B1DF818E6003FCB3C /* Drawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E41DF81863003FCB3C /* Drawer.swift */; }; 42 | 03BBC40C1DF818E6003FCB3C /* Footnote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E51DF81863003FCB3C /* Footnote.swift */; }; 43 | 03BBC40D1DF818E6003FCB3C /* HorizontalRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E61DF81863003FCB3C /* HorizontalRule.swift */; }; 44 | 03BBC40E1DF818E6003FCB3C /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3EA1DF81863003FCB3C /* List.swift */; }; 45 | 03BBC40F1DF818E6003FCB3C /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3EB1DF81863003FCB3C /* Node.swift */; }; 46 | 03BBC4101DF818E6003FCB3C /* OrgDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3EC1DF81863003FCB3C /* OrgDocument.swift */; }; 47 | 03BBC4111DF818E6003FCB3C /* Paragraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3EF1DF81863003FCB3C /* Paragraph.swift */; }; 48 | 03BBC4121DF818E6003FCB3C /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3F11DF81863003FCB3C /* Section.swift */; }; 49 | 03BBC4131DF818E7003FCB3C /* Block.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E11DF81863003FCB3C /* Block.swift */; }; 50 | 03BBC4141DF818E7003FCB3C /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E21DF81863003FCB3C /* Comment.swift */; }; 51 | 03BBC4151DF818E7003FCB3C /* Drawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E41DF81863003FCB3C /* Drawer.swift */; }; 52 | 03BBC4161DF818E7003FCB3C /* Footnote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E51DF81863003FCB3C /* Footnote.swift */; }; 53 | 03BBC4171DF818E7003FCB3C /* HorizontalRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E61DF81863003FCB3C /* HorizontalRule.swift */; }; 54 | 03BBC4181DF818E7003FCB3C /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3EA1DF81863003FCB3C /* List.swift */; }; 55 | 03BBC4191DF818E7003FCB3C /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3EB1DF81863003FCB3C /* Node.swift */; }; 56 | 03BBC41A1DF818E7003FCB3C /* OrgDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3EC1DF81863003FCB3C /* OrgDocument.swift */; }; 57 | 03BBC41B1DF818E7003FCB3C /* Paragraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3EF1DF81863003FCB3C /* Paragraph.swift */; }; 58 | 03BBC41C1DF818E7003FCB3C /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3F11DF81863003FCB3C /* Section.swift */; }; 59 | 03BBC41D1DF818F1003FCB3C /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E31DF81863003FCB3C /* Data.swift */; }; 60 | 03BBC41F1DF818F1003FCB3C /* OrgFileWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3ED1DF81863003FCB3C /* OrgFileWriter.swift */; }; 61 | 03BBC4201DF818F1003FCB3C /* Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3F01DF81863003FCB3C /* Regex.swift */; }; 62 | 03BBC4211DF818F1003FCB3C /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3F21DF81863003FCB3C /* String.swift */; }; 63 | 03BBC4221DF818F1003FCB3C /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E31DF81863003FCB3C /* Data.swift */; }; 64 | 03BBC4241DF818F1003FCB3C /* OrgFileWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3ED1DF81863003FCB3C /* OrgFileWriter.swift */; }; 65 | 03BBC4251DF818F1003FCB3C /* Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3F01DF81863003FCB3C /* Regex.swift */; }; 66 | 03BBC4261DF818F1003FCB3C /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3F21DF81863003FCB3C /* String.swift */; }; 67 | 03BBC4271DF8191E003FCB3C /* Inline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E71DF81863003FCB3C /* Inline.swift */; }; 68 | 03BBC4281DF8191E003FCB3C /* Lexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E91DF81863003FCB3C /* Lexer.swift */; }; 69 | 03BBC4291DF8191E003FCB3C /* OrgParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3EE1DF81863003FCB3C /* OrgParser.swift */; }; 70 | 03BBC42A1DF8191E003FCB3C /* Tokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3F31DF81863003FCB3C /* Tokens.swift */; }; 71 | 03BBC42B1DF8191E003FCB3C /* Inline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E71DF81863003FCB3C /* Inline.swift */; }; 72 | 03BBC42C1DF8191E003FCB3C /* Lexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3E91DF81863003FCB3C /* Lexer.swift */; }; 73 | 03BBC42D1DF8191E003FCB3C /* OrgParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3EE1DF81863003FCB3C /* OrgParser.swift */; }; 74 | 03BBC42E1DF8191E003FCB3C /* Tokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BBC3F31DF81863003FCB3C /* Tokens.swift */; }; 75 | 03C091121E21A4D100002E2F /* Timestamp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C091111E21A4D100002E2F /* Timestamp.swift */; }; 76 | 03C091141E220DF500002E2F /* TimestampTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C091131E220DF500002E2F /* TimestampTests.swift */; }; 77 | 03C091151E220ECD00002E2F /* TimestampTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C091131E220DF500002E2F /* TimestampTests.swift */; }; 78 | 03C091161E220F2700002E2F /* Timestamp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C091111E21A4D100002E2F /* Timestamp.swift */; }; 79 | 03C091181E2321FB00002E2F /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C091171E2321FB00002E2F /* Constants.swift */; }; 80 | 03C091191E23222500002E2F /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C091171E2321FB00002E2F /* Constants.swift */; }; 81 | 03F9E6AC1D60411A0080E872 /* TokenizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F9E6AB1D60411A0080E872 /* TokenizerTests.swift */; }; 82 | A70C22511D8A1D6A007796E2 /* Asserts.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70C22501D8A1D6A007796E2 /* Asserts.swift */; }; 83 | A70C22531D8A204F007796E2 /* TokenExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70C22521D8A204F007796E2 /* TokenExtensions.swift */; }; 84 | A77DDD5A1D9C975D00120BA7 /* OrgFileWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A77DDD591D9C975D00120BA7 /* OrgFileWriterTests.swift */; }; 85 | A78044F41DDD06D000C8CE46 /* IndexingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A78044F31DDD06D000C8CE46 /* IndexingTests.swift */; }; 86 | A7B688461D8B74FA009E2FB1 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7B688451D8B74FA009E2FB1 /* PerformanceTests.swift */; }; 87 | /* End PBXBuildFile section */ 88 | 89 | /* Begin PBXContainerItemProxy section */ 90 | 0382A1891DF826730015B475 /* PBXContainerItemProxy */ = { 91 | isa = PBXContainerItemProxy; 92 | containerPortal = 03F9E6931D6041190080E872 /* Project object */; 93 | proxyType = 1; 94 | remoteGlobalIDString = 03BBC3BC1DF81104003FCB3C; 95 | remoteInfo = "SwiftOrg-macOS"; 96 | }; 97 | /* End PBXContainerItemProxy section */ 98 | 99 | /* Begin PBXFileReference section */ 100 | 03520B571E4D9209003345D5 /* Cartfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; 101 | 03520B581E4D9209003345D5 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 102 | 03520B591E4D9209003345D5 /* SwiftOrg.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SwiftOrg.podspec; sourceTree = ""; }; 103 | 03520BE71E5C3785003345D5 /* ParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ParserTests.swift; path = SwiftOrgTests/ParserTests.swift; sourceTree = ""; }; 104 | 03520BEA1E5C39A2003345D5 /* LinuxMain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinuxMain.swift; sourceTree = ""; }; 105 | 03520BEC1E5C39CB003345D5 /* XCTestManifests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = XCTestManifests.swift; path = SwiftOrgTests/XCTestManifests.swift; sourceTree = ""; }; 106 | 035A617D1E3F11C7001CE563 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; 107 | 035A61801E3F3BBA001CE563 /* Table.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Table.swift; sourceTree = ""; }; 108 | 035EEA1E1D8D504A00A38F7E /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = SwiftOrgTests/Utils.swift; sourceTree = ""; }; 109 | 035EEA261D8D526F00A38F7E /* InlineParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InlineParsingTests.swift; path = SwiftOrgTests/InlineParsingTests.swift; sourceTree = ""; }; 110 | 036C5E811D928A85007484CF /* README.org */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.org; sourceTree = SOURCE_ROOT; }; 111 | 037BFFB71E6406A3005EB02F /* ConvertToJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConvertToJSON.swift; sourceTree = ""; }; 112 | 037BFFB91E640710005EB02F /* ConvertToJSONTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConvertToJSONTests.swift; path = SwiftOrgTests/ConvertToJSONTests.swift; sourceTree = ""; }; 113 | 0382A1831DF826730015B475 /* SwiftOrgTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SwiftOrgTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 114 | 0382A18E1DF826A10015B475 /* Info-macOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-macOS.plist"; sourceTree = ""; }; 115 | 0382A1A01DF9713B0015B475 /* LexerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LexerTests.swift; path = SwiftOrgTests/LexerTests.swift; sourceTree = ""; }; 116 | 03BBC3BD1DF81104003FCB3C /* SwiftOrg.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftOrg.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 117 | 03BBC3CA1DF8127C003FCB3C /* SwiftOrg.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftOrg.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 118 | 03BBC3D71DF817DB003FCB3C /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; 119 | 03BBC3D81DF817DB003FCB3C /* Info-macOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-macOS.plist"; sourceTree = ""; }; 120 | 03BBC3D91DF817DB003FCB3C /* SwiftOrg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwiftOrg.h; sourceTree = ""; }; 121 | 03BBC3E11DF81863003FCB3C /* Block.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Block.swift; sourceTree = ""; }; 122 | 03BBC3E21DF81863003FCB3C /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; 123 | 03BBC3E31DF81863003FCB3C /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; }; 124 | 03BBC3E41DF81863003FCB3C /* Drawer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Drawer.swift; sourceTree = ""; }; 125 | 03BBC3E51DF81863003FCB3C /* Footnote.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Footnote.swift; sourceTree = ""; }; 126 | 03BBC3E61DF81863003FCB3C /* HorizontalRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalRule.swift; sourceTree = ""; }; 127 | 03BBC3E71DF81863003FCB3C /* Inline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Inline.swift; sourceTree = ""; }; 128 | 03BBC3E91DF81863003FCB3C /* Lexer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lexer.swift; sourceTree = ""; }; 129 | 03BBC3EA1DF81863003FCB3C /* List.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = List.swift; sourceTree = ""; }; 130 | 03BBC3EB1DF81863003FCB3C /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; 131 | 03BBC3EC1DF81863003FCB3C /* OrgDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrgDocument.swift; sourceTree = ""; }; 132 | 03BBC3ED1DF81863003FCB3C /* OrgFileWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrgFileWriter.swift; sourceTree = ""; }; 133 | 03BBC3EE1DF81863003FCB3C /* OrgParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrgParser.swift; sourceTree = ""; }; 134 | 03BBC3EF1DF81863003FCB3C /* Paragraph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Paragraph.swift; sourceTree = ""; }; 135 | 03BBC3F01DF81863003FCB3C /* Regex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Regex.swift; sourceTree = ""; }; 136 | 03BBC3F11DF81863003FCB3C /* Section.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = ""; }; 137 | 03BBC3F21DF81863003FCB3C /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 138 | 03BBC3F31DF81863003FCB3C /* Tokens.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tokens.swift; sourceTree = ""; }; 139 | 03C091111E21A4D100002E2F /* Timestamp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timestamp.swift; sourceTree = ""; }; 140 | 03C091131E220DF500002E2F /* TimestampTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TimestampTests.swift; path = SwiftOrgTests/TimestampTests.swift; sourceTree = ""; }; 141 | 03C091171E2321FB00002E2F /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 142 | 03F9E6A61D60411A0080E872 /* SwiftOrgTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SwiftOrgTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 143 | 03F9E6AB1D60411A0080E872 /* TokenizerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TokenizerTests.swift; path = SwiftOrgTests/TokenizerTests.swift; sourceTree = ""; }; 144 | 03F9E6AD1D60411A0080E872 /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; 145 | A70C22501D8A1D6A007796E2 /* Asserts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Asserts.swift; path = SwiftOrgTests/Asserts.swift; sourceTree = ""; }; 146 | A70C22521D8A204F007796E2 /* TokenExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TokenExtensions.swift; path = SwiftOrgTests/TokenExtensions.swift; sourceTree = ""; }; 147 | A77DDD591D9C975D00120BA7 /* OrgFileWriterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OrgFileWriterTests.swift; path = SwiftOrgTests/OrgFileWriterTests.swift; sourceTree = ""; }; 148 | A78044F31DDD06D000C8CE46 /* IndexingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = IndexingTests.swift; path = SwiftOrgTests/IndexingTests.swift; sourceTree = ""; }; 149 | A7B688451D8B74FA009E2FB1 /* PerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PerformanceTests.swift; path = SwiftOrgTests/PerformanceTests.swift; sourceTree = ""; }; 150 | /* End PBXFileReference section */ 151 | 152 | /* Begin PBXFrameworksBuildPhase section */ 153 | 0382A1801DF826730015B475 /* Frameworks */ = { 154 | isa = PBXFrameworksBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | 0382A1881DF826730015B475 /* SwiftOrg.framework in Frameworks */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | 03BBC3B91DF81104003FCB3C /* Frameworks */ = { 162 | isa = PBXFrameworksBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | ); 166 | runOnlyForDeploymentPostprocessing = 0; 167 | }; 168 | 03BBC3C61DF8127C003FCB3C /* Frameworks */ = { 169 | isa = PBXFrameworksBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | 03F9E6A31D60411A0080E872 /* Frameworks */ = { 176 | isa = PBXFrameworksBuildPhase; 177 | buildActionMask = 2147483647; 178 | files = ( 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | }; 182 | /* End PBXFrameworksBuildPhase section */ 183 | 184 | /* Begin PBXGroup section */ 185 | 03520B561E4D918F003345D5 /* Meta */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | 03520B571E4D9209003345D5 /* Cartfile */, 189 | 03520B581E4D9209003345D5 /* Package.swift */, 190 | 03520B591E4D9209003345D5 /* SwiftOrg.podspec */, 191 | ); 192 | name = Meta; 193 | sourceTree = ""; 194 | }; 195 | 03520BEE1E5C39F3003345D5 /* Linux */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | 03520BEC1E5C39CB003345D5 /* XCTestManifests.swift */, 199 | 03520BEA1E5C39A2003345D5 /* LinuxMain.swift */, 200 | ); 201 | name = Linux; 202 | sourceTree = ""; 203 | }; 204 | 03BBC3D61DF817A2003FCB3C /* Sources */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | 03BBC4071DF818A9003FCB3C /* Nodes */, 208 | 03BBC4081DF818D2003FCB3C /* Utils */, 209 | 03BBC3E71DF81863003FCB3C /* Inline.swift */, 210 | 03BBC3E91DF81863003FCB3C /* Lexer.swift */, 211 | 03BBC3EE1DF81863003FCB3C /* OrgParser.swift */, 212 | 03BBC3F31DF81863003FCB3C /* Tokens.swift */, 213 | 03BBC3D71DF817DB003FCB3C /* Info-iOS.plist */, 214 | 03BBC3D81DF817DB003FCB3C /* Info-macOS.plist */, 215 | 03BBC3D91DF817DB003FCB3C /* SwiftOrg.h */, 216 | ); 217 | path = Sources; 218 | sourceTree = ""; 219 | }; 220 | 03BBC4071DF818A9003FCB3C /* Nodes */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | 03BBC3EB1DF81863003FCB3C /* Node.swift */, 224 | 03BBC3EC1DF81863003FCB3C /* OrgDocument.swift */, 225 | 03BBC3F11DF81863003FCB3C /* Section.swift */, 226 | 03BBC3E41DF81863003FCB3C /* Drawer.swift */, 227 | 03BBC3EF1DF81863003FCB3C /* Paragraph.swift */, 228 | 03BBC3E11DF81863003FCB3C /* Block.swift */, 229 | 03BBC3E21DF81863003FCB3C /* Comment.swift */, 230 | 03BBC3E51DF81863003FCB3C /* Footnote.swift */, 231 | 03BBC3E61DF81863003FCB3C /* HorizontalRule.swift */, 232 | 03BBC3EA1DF81863003FCB3C /* List.swift */, 233 | 035A61801E3F3BBA001CE563 /* Table.swift */, 234 | ); 235 | name = Nodes; 236 | sourceTree = ""; 237 | }; 238 | 03BBC4081DF818D2003FCB3C /* Utils */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | 037BFFB71E6406A3005EB02F /* ConvertToJSON.swift */, 242 | 03BBC3E31DF81863003FCB3C /* Data.swift */, 243 | 03C091171E2321FB00002E2F /* Constants.swift */, 244 | 03BBC3ED1DF81863003FCB3C /* OrgFileWriter.swift */, 245 | 03BBC3F01DF81863003FCB3C /* Regex.swift */, 246 | 03BBC3F21DF81863003FCB3C /* String.swift */, 247 | 03C091111E21A4D100002E2F /* Timestamp.swift */, 248 | 035A617D1E3F11C7001CE563 /* Queue.swift */, 249 | ); 250 | name = Utils; 251 | sourceTree = ""; 252 | }; 253 | 03F9E6921D6041190080E872 = { 254 | isa = PBXGroup; 255 | children = ( 256 | 03520B561E4D918F003345D5 /* Meta */, 257 | 03BBC3D61DF817A2003FCB3C /* Sources */, 258 | 03F9E6AA1D60411A0080E872 /* Tests */, 259 | 03F9E69D1D6041190080E872 /* Products */, 260 | ); 261 | sourceTree = ""; 262 | }; 263 | 03F9E69D1D6041190080E872 /* Products */ = { 264 | isa = PBXGroup; 265 | children = ( 266 | 03F9E6A61D60411A0080E872 /* SwiftOrgTests-iOS.xctest */, 267 | 03BBC3BD1DF81104003FCB3C /* SwiftOrg.framework */, 268 | 03BBC3CA1DF8127C003FCB3C /* SwiftOrg.framework */, 269 | 0382A1831DF826730015B475 /* SwiftOrgTests-macOS.xctest */, 270 | ); 271 | name = Products; 272 | sourceTree = ""; 273 | }; 274 | 03F9E6AA1D60411A0080E872 /* Tests */ = { 275 | isa = PBXGroup; 276 | children = ( 277 | 03520BEE1E5C39F3003345D5 /* Linux */, 278 | A779680E1D8B999200A8C19C /* Resources */, 279 | 0382A18E1DF826A10015B475 /* Info-macOS.plist */, 280 | 03F9E6AD1D60411A0080E872 /* Info-iOS.plist */, 281 | A70C22501D8A1D6A007796E2 /* Asserts.swift */, 282 | 035EEA1E1D8D504A00A38F7E /* Utils.swift */, 283 | A70C22521D8A204F007796E2 /* TokenExtensions.swift */, 284 | 0382A1A01DF9713B0015B475 /* LexerTests.swift */, 285 | 03F9E6AB1D60411A0080E872 /* TokenizerTests.swift */, 286 | 03520BE71E5C3785003345D5 /* ParserTests.swift */, 287 | 035EEA261D8D526F00A38F7E /* InlineParsingTests.swift */, 288 | A78044F31DDD06D000C8CE46 /* IndexingTests.swift */, 289 | 03C091131E220DF500002E2F /* TimestampTests.swift */, 290 | A7B688451D8B74FA009E2FB1 /* PerformanceTests.swift */, 291 | A77DDD591D9C975D00120BA7 /* OrgFileWriterTests.swift */, 292 | 037BFFB91E640710005EB02F /* ConvertToJSONTests.swift */, 293 | ); 294 | path = Tests; 295 | sourceTree = ""; 296 | }; 297 | A779680E1D8B999200A8C19C /* Resources */ = { 298 | isa = PBXGroup; 299 | children = ( 300 | 036C5E811D928A85007484CF /* README.org */, 301 | ); 302 | name = Resources; 303 | sourceTree = ""; 304 | }; 305 | /* End PBXGroup section */ 306 | 307 | /* Begin PBXHeadersBuildPhase section */ 308 | 03BBC3BA1DF81104003FCB3C /* Headers */ = { 309 | isa = PBXHeadersBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | 03BBC3DD1DF817E0003FCB3C /* SwiftOrg.h in Headers */, 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | }; 316 | 03BBC3C71DF8127C003FCB3C /* Headers */ = { 317 | isa = PBXHeadersBuildPhase; 318 | buildActionMask = 2147483647; 319 | files = ( 320 | 03BBC3DE1DF817E1003FCB3C /* SwiftOrg.h in Headers */, 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | /* End PBXHeadersBuildPhase section */ 325 | 326 | /* Begin PBXNativeTarget section */ 327 | 0382A1821DF826730015B475 /* SwiftOrgTests-macOS */ = { 328 | isa = PBXNativeTarget; 329 | buildConfigurationList = 0382A18D1DF826730015B475 /* Build configuration list for PBXNativeTarget "SwiftOrgTests-macOS" */; 330 | buildPhases = ( 331 | 0382A17F1DF826730015B475 /* Sources */, 332 | 0382A1801DF826730015B475 /* Frameworks */, 333 | 0382A1811DF826730015B475 /* Resources */, 334 | ); 335 | buildRules = ( 336 | ); 337 | dependencies = ( 338 | 0382A18A1DF826730015B475 /* PBXTargetDependency */, 339 | ); 340 | name = "SwiftOrgTests-macOS"; 341 | productName = "SwiftOrgTests-macOS"; 342 | productReference = 0382A1831DF826730015B475 /* SwiftOrgTests-macOS.xctest */; 343 | productType = "com.apple.product-type.bundle.unit-test"; 344 | }; 345 | 03BBC3BC1DF81104003FCB3C /* SwiftOrg-macOS */ = { 346 | isa = PBXNativeTarget; 347 | buildConfigurationList = 03BBC3C41DF81104003FCB3C /* Build configuration list for PBXNativeTarget "SwiftOrg-macOS" */; 348 | buildPhases = ( 349 | 03BBC3B81DF81104003FCB3C /* Sources */, 350 | 03BBC3B91DF81104003FCB3C /* Frameworks */, 351 | 03BBC3BA1DF81104003FCB3C /* Headers */, 352 | 03BBC3BB1DF81104003FCB3C /* Resources */, 353 | 03BBC4301DF819FD003FCB3C /* ShellScript */, 354 | ); 355 | buildRules = ( 356 | ); 357 | dependencies = ( 358 | ); 359 | name = "SwiftOrg-macOS"; 360 | productName = "SwiftOrg-macOS"; 361 | productReference = 03BBC3BD1DF81104003FCB3C /* SwiftOrg.framework */; 362 | productType = "com.apple.product-type.framework"; 363 | }; 364 | 03BBC3C91DF8127C003FCB3C /* SwiftOrg-iOS */ = { 365 | isa = PBXNativeTarget; 366 | buildConfigurationList = 03BBC3CF1DF8127C003FCB3C /* Build configuration list for PBXNativeTarget "SwiftOrg-iOS" */; 367 | buildPhases = ( 368 | 03BBC3C51DF8127C003FCB3C /* Sources */, 369 | 03BBC3C61DF8127C003FCB3C /* Frameworks */, 370 | 03BBC3C71DF8127C003FCB3C /* Headers */, 371 | 03BBC3C81DF8127C003FCB3C /* Resources */, 372 | 03BBC42F1DF819ED003FCB3C /* ShellScript */, 373 | ); 374 | buildRules = ( 375 | ); 376 | dependencies = ( 377 | ); 378 | name = "SwiftOrg-iOS"; 379 | productName = "SwiftOrg-iOS"; 380 | productReference = 03BBC3CA1DF8127C003FCB3C /* SwiftOrg.framework */; 381 | productType = "com.apple.product-type.framework"; 382 | }; 383 | 03F9E6A51D60411A0080E872 /* SwiftOrgTests-iOS */ = { 384 | isa = PBXNativeTarget; 385 | buildConfigurationList = 03F9E6B31D60411A0080E872 /* Build configuration list for PBXNativeTarget "SwiftOrgTests-iOS" */; 386 | buildPhases = ( 387 | 03F9E6A21D60411A0080E872 /* Sources */, 388 | 03F9E6A31D60411A0080E872 /* Frameworks */, 389 | 03F9E6A41D60411A0080E872 /* Resources */, 390 | ); 391 | buildRules = ( 392 | ); 393 | dependencies = ( 394 | ); 395 | name = "SwiftOrgTests-iOS"; 396 | productName = CocoaOrgTests; 397 | productReference = 03F9E6A61D60411A0080E872 /* SwiftOrgTests-iOS.xctest */; 398 | productType = "com.apple.product-type.bundle.unit-test"; 399 | }; 400 | /* End PBXNativeTarget section */ 401 | 402 | /* Begin PBXProject section */ 403 | 03F9E6931D6041190080E872 /* Project object */ = { 404 | isa = PBXProject; 405 | attributes = { 406 | LastSwiftUpdateCheck = 0810; 407 | LastUpgradeCheck = 0800; 408 | ORGANIZATIONNAME = "Xiaoxing Hu"; 409 | TargetAttributes = { 410 | 0382A1821DF826730015B475 = { 411 | CreatedOnToolsVersion = 8.1; 412 | ProvisioningStyle = Automatic; 413 | }; 414 | 03BBC3BC1DF81104003FCB3C = { 415 | CreatedOnToolsVersion = 8.1; 416 | ProvisioningStyle = Automatic; 417 | }; 418 | 03BBC3C91DF8127C003FCB3C = { 419 | CreatedOnToolsVersion = 8.1; 420 | ProvisioningStyle = Automatic; 421 | }; 422 | 03F9E6A51D60411A0080E872 = { 423 | CreatedOnToolsVersion = 7.3.1; 424 | DevelopmentTeam = H5G26MVS9D; 425 | LastSwiftMigration = 0800; 426 | }; 427 | }; 428 | }; 429 | buildConfigurationList = 03F9E6961D6041190080E872 /* Build configuration list for PBXProject "SwiftOrg" */; 430 | compatibilityVersion = "Xcode 3.2"; 431 | developmentRegion = English; 432 | hasScannedForEncodings = 0; 433 | knownRegions = ( 434 | en, 435 | ); 436 | mainGroup = 03F9E6921D6041190080E872; 437 | productRefGroup = 03F9E69D1D6041190080E872 /* Products */; 438 | projectDirPath = ""; 439 | projectRoot = ""; 440 | targets = ( 441 | 03BBC3C91DF8127C003FCB3C /* SwiftOrg-iOS */, 442 | 03BBC3BC1DF81104003FCB3C /* SwiftOrg-macOS */, 443 | 03F9E6A51D60411A0080E872 /* SwiftOrgTests-iOS */, 444 | 0382A1821DF826730015B475 /* SwiftOrgTests-macOS */, 445 | ); 446 | }; 447 | /* End PBXProject section */ 448 | 449 | /* Begin PBXResourcesBuildPhase section */ 450 | 0382A1811DF826730015B475 /* Resources */ = { 451 | isa = PBXResourcesBuildPhase; 452 | buildActionMask = 2147483647; 453 | files = ( 454 | 0382A19F1DF828700015B475 /* README.org in Resources */, 455 | ); 456 | runOnlyForDeploymentPostprocessing = 0; 457 | }; 458 | 03BBC3BB1DF81104003FCB3C /* Resources */ = { 459 | isa = PBXResourcesBuildPhase; 460 | buildActionMask = 2147483647; 461 | files = ( 462 | ); 463 | runOnlyForDeploymentPostprocessing = 0; 464 | }; 465 | 03BBC3C81DF8127C003FCB3C /* Resources */ = { 466 | isa = PBXResourcesBuildPhase; 467 | buildActionMask = 2147483647; 468 | files = ( 469 | 03520B5A1E4D9209003345D5 /* Cartfile in Resources */, 470 | 03520B5C1E4D9209003345D5 /* SwiftOrg.podspec in Resources */, 471 | ); 472 | runOnlyForDeploymentPostprocessing = 0; 473 | }; 474 | 03F9E6A41D60411A0080E872 /* Resources */ = { 475 | isa = PBXResourcesBuildPhase; 476 | buildActionMask = 2147483647; 477 | files = ( 478 | 036C5E821D928A85007484CF /* README.org in Resources */, 479 | ); 480 | runOnlyForDeploymentPostprocessing = 0; 481 | }; 482 | /* End PBXResourcesBuildPhase section */ 483 | 484 | /* Begin PBXShellScriptBuildPhase section */ 485 | 03BBC42F1DF819ED003FCB3C /* ShellScript */ = { 486 | isa = PBXShellScriptBuildPhase; 487 | buildActionMask = 2147483647; 488 | files = ( 489 | ); 490 | inputPaths = ( 491 | ); 492 | outputPaths = ( 493 | ); 494 | runOnlyForDeploymentPostprocessing = 0; 495 | shellPath = /bin/sh; 496 | shellScript = "TAGS=\"\\/\\/(\\s*)TODO\"\nfind \"${SRCROOT}\" \\( -name \"*.h\" -or -name \"*.m\" -or -name \"*.swift\" \\) -print0 | xargs -0 egrep --with-filename --line-number --only-matching \"($TAGS).*\\$\" | perl -p -e \"s/($TAGS)/ warning: \\$1/\""; 497 | }; 498 | 03BBC4301DF819FD003FCB3C /* ShellScript */ = { 499 | isa = PBXShellScriptBuildPhase; 500 | buildActionMask = 2147483647; 501 | files = ( 502 | ); 503 | inputPaths = ( 504 | ); 505 | outputPaths = ( 506 | ); 507 | runOnlyForDeploymentPostprocessing = 0; 508 | shellPath = /bin/sh; 509 | shellScript = "TAGS=\"\\/\\/(\\s*)TODO\"\nfind \"${SRCROOT}\" \\( -name \"*.h\" -or -name \"*.m\" -or -name \"*.swift\" \\) -print0 | xargs -0 egrep --with-filename --line-number --only-matching \"($TAGS).*\\$\" | perl -p -e \"s/($TAGS)/ warning: \\$1/\""; 510 | }; 511 | /* End PBXShellScriptBuildPhase section */ 512 | 513 | /* Begin PBXSourcesBuildPhase section */ 514 | 0382A17F1DF826730015B475 /* Sources */ = { 515 | isa = PBXSourcesBuildPhase; 516 | buildActionMask = 2147483647; 517 | files = ( 518 | 0382A19B1DF827080015B475 /* PerformanceTests.swift in Sources */, 519 | 0382A19D1DF827080015B475 /* OrgFileWriterTests.swift in Sources */, 520 | 0382A1901DF827080015B475 /* Asserts.swift in Sources */, 521 | 0382A19A1DF827080015B475 /* InlineParsingTests.swift in Sources */, 522 | 037BFFBB1E640710005EB02F /* ConvertToJSONTests.swift in Sources */, 523 | 03520BE91E5C3785003345D5 /* ParserTests.swift in Sources */, 524 | 0382A1911DF827080015B475 /* Utils.swift in Sources */, 525 | 0382A1931DF827080015B475 /* TokenizerTests.swift in Sources */, 526 | 0382A1921DF827080015B475 /* TokenExtensions.swift in Sources */, 527 | 03AF26331DF97342002E71A7 /* LexerTests.swift in Sources */, 528 | 03C091151E220ECD00002E2F /* TimestampTests.swift in Sources */, 529 | 0382A19E1DF827080015B475 /* IndexingTests.swift in Sources */, 530 | ); 531 | runOnlyForDeploymentPostprocessing = 0; 532 | }; 533 | 03BBC3B81DF81104003FCB3C /* Sources */ = { 534 | isa = PBXSourcesBuildPhase; 535 | buildActionMask = 2147483647; 536 | files = ( 537 | 03BBC4091DF818E6003FCB3C /* Block.swift in Sources */, 538 | 03BBC42A1DF8191E003FCB3C /* Tokens.swift in Sources */, 539 | 03BBC40B1DF818E6003FCB3C /* Drawer.swift in Sources */, 540 | 035A617F1E3F11C7001CE563 /* Queue.swift in Sources */, 541 | 03BBC40D1DF818E6003FCB3C /* HorizontalRule.swift in Sources */, 542 | 03BBC4201DF818F1003FCB3C /* Regex.swift in Sources */, 543 | 03BBC4101DF818E6003FCB3C /* OrgDocument.swift in Sources */, 544 | 037BFFBC1E640714005EB02F /* ConvertToJSON.swift in Sources */, 545 | 03C091161E220F2700002E2F /* Timestamp.swift in Sources */, 546 | 03BBC4271DF8191E003FCB3C /* Inline.swift in Sources */, 547 | 03BBC41D1DF818F1003FCB3C /* Data.swift in Sources */, 548 | 03BBC40A1DF818E6003FCB3C /* Comment.swift in Sources */, 549 | 03C091191E23222500002E2F /* Constants.swift in Sources */, 550 | 03BBC4121DF818E6003FCB3C /* Section.swift in Sources */, 551 | 03BBC4281DF8191E003FCB3C /* Lexer.swift in Sources */, 552 | 03BBC40C1DF818E6003FCB3C /* Footnote.swift in Sources */, 553 | 035A61821E3F3BBA001CE563 /* Table.swift in Sources */, 554 | 03BBC4111DF818E6003FCB3C /* Paragraph.swift in Sources */, 555 | 03BBC40E1DF818E6003FCB3C /* List.swift in Sources */, 556 | 03BBC4211DF818F1003FCB3C /* String.swift in Sources */, 557 | 03BBC4291DF8191E003FCB3C /* OrgParser.swift in Sources */, 558 | 03BBC41F1DF818F1003FCB3C /* OrgFileWriter.swift in Sources */, 559 | 03BBC40F1DF818E6003FCB3C /* Node.swift in Sources */, 560 | ); 561 | runOnlyForDeploymentPostprocessing = 0; 562 | }; 563 | 03BBC3C51DF8127C003FCB3C /* Sources */ = { 564 | isa = PBXSourcesBuildPhase; 565 | buildActionMask = 2147483647; 566 | files = ( 567 | 03BBC4131DF818E7003FCB3C /* Block.swift in Sources */, 568 | 03BBC42E1DF8191E003FCB3C /* Tokens.swift in Sources */, 569 | 03BBC4151DF818E7003FCB3C /* Drawer.swift in Sources */, 570 | 035A617E1E3F11C7001CE563 /* Queue.swift in Sources */, 571 | 03BBC4171DF818E7003FCB3C /* HorizontalRule.swift in Sources */, 572 | 03BBC4251DF818F1003FCB3C /* Regex.swift in Sources */, 573 | 03BBC41A1DF818E7003FCB3C /* OrgDocument.swift in Sources */, 574 | 03C091121E21A4D100002E2F /* Timestamp.swift in Sources */, 575 | 03BBC42B1DF8191E003FCB3C /* Inline.swift in Sources */, 576 | 03BBC4221DF818F1003FCB3C /* Data.swift in Sources */, 577 | 03BBC4141DF818E7003FCB3C /* Comment.swift in Sources */, 578 | 037BFFB81E6406A3005EB02F /* ConvertToJSON.swift in Sources */, 579 | 03C091181E2321FB00002E2F /* Constants.swift in Sources */, 580 | 03BBC41C1DF818E7003FCB3C /* Section.swift in Sources */, 581 | 03BBC42C1DF8191E003FCB3C /* Lexer.swift in Sources */, 582 | 03BBC4161DF818E7003FCB3C /* Footnote.swift in Sources */, 583 | 035A61811E3F3BBA001CE563 /* Table.swift in Sources */, 584 | 03BBC41B1DF818E7003FCB3C /* Paragraph.swift in Sources */, 585 | 03BBC4181DF818E7003FCB3C /* List.swift in Sources */, 586 | 03BBC4261DF818F1003FCB3C /* String.swift in Sources */, 587 | 03BBC42D1DF8191E003FCB3C /* OrgParser.swift in Sources */, 588 | 03BBC4241DF818F1003FCB3C /* OrgFileWriter.swift in Sources */, 589 | 03BBC4191DF818E7003FCB3C /* Node.swift in Sources */, 590 | ); 591 | runOnlyForDeploymentPostprocessing = 0; 592 | }; 593 | 03F9E6A21D60411A0080E872 /* Sources */ = { 594 | isa = PBXSourcesBuildPhase; 595 | buildActionMask = 2147483647; 596 | files = ( 597 | A70C22511D8A1D6A007796E2 /* Asserts.swift in Sources */, 598 | A7B688461D8B74FA009E2FB1 /* PerformanceTests.swift in Sources */, 599 | A77DDD5A1D9C975D00120BA7 /* OrgFileWriterTests.swift in Sources */, 600 | 03F9E6AC1D60411A0080E872 /* TokenizerTests.swift in Sources */, 601 | 037BFFBA1E640710005EB02F /* ConvertToJSONTests.swift in Sources */, 602 | 035EEA271D8D526F00A38F7E /* InlineParsingTests.swift in Sources */, 603 | 035EEA1F1D8D504A00A38F7E /* Utils.swift in Sources */, 604 | 03520BE81E5C3785003345D5 /* ParserTests.swift in Sources */, 605 | A70C22531D8A204F007796E2 /* TokenExtensions.swift in Sources */, 606 | 0382A1A11DF9713B0015B475 /* LexerTests.swift in Sources */, 607 | 03C091141E220DF500002E2F /* TimestampTests.swift in Sources */, 608 | A78044F41DDD06D000C8CE46 /* IndexingTests.swift in Sources */, 609 | ); 610 | runOnlyForDeploymentPostprocessing = 0; 611 | }; 612 | /* End PBXSourcesBuildPhase section */ 613 | 614 | /* Begin PBXTargetDependency section */ 615 | 0382A18A1DF826730015B475 /* PBXTargetDependency */ = { 616 | isa = PBXTargetDependency; 617 | target = 03BBC3BC1DF81104003FCB3C /* SwiftOrg-macOS */; 618 | targetProxy = 0382A1891DF826730015B475 /* PBXContainerItemProxy */; 619 | }; 620 | /* End PBXTargetDependency section */ 621 | 622 | /* Begin XCBuildConfiguration section */ 623 | 0382A18B1DF826730015B475 /* Debug */ = { 624 | isa = XCBuildConfiguration; 625 | buildSettings = { 626 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 627 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 628 | CODE_SIGN_IDENTITY = "-"; 629 | COMBINE_HIDPI_IMAGES = YES; 630 | INFOPLIST_FILE = "Tests/Info-macOS.plist"; 631 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 632 | PRODUCT_BUNDLE_IDENTIFIER = "com.huxiaoxing.SwiftOrgTests-macOS"; 633 | PRODUCT_NAME = "$(TARGET_NAME)"; 634 | SDKROOT = macosx; 635 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 636 | SWIFT_VERSION = 3.0; 637 | }; 638 | name = Debug; 639 | }; 640 | 0382A18C1DF826730015B475 /* Release */ = { 641 | isa = XCBuildConfiguration; 642 | buildSettings = { 643 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 644 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 645 | CODE_SIGN_IDENTITY = "-"; 646 | COMBINE_HIDPI_IMAGES = YES; 647 | INFOPLIST_FILE = "Tests/Info-macOS.plist"; 648 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 649 | PRODUCT_BUNDLE_IDENTIFIER = "com.huxiaoxing.SwiftOrgTests-macOS"; 650 | PRODUCT_NAME = "$(TARGET_NAME)"; 651 | SDKROOT = macosx; 652 | SWIFT_VERSION = 3.0; 653 | }; 654 | name = Release; 655 | }; 656 | 03BBC3C21DF81104003FCB3C /* Debug */ = { 657 | isa = XCBuildConfiguration; 658 | buildSettings = { 659 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 660 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 661 | CODE_SIGN_IDENTITY = "-"; 662 | COMBINE_HIDPI_IMAGES = YES; 663 | DEFINES_MODULE = YES; 664 | DYLIB_COMPATIBILITY_VERSION = 1; 665 | DYLIB_CURRENT_VERSION = 1; 666 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 667 | FRAMEWORK_VERSION = A; 668 | INFOPLIST_FILE = "Sources/Info-macOS.plist"; 669 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 670 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 671 | MACOSX_DEPLOYMENT_TARGET = 10.10; 672 | PRODUCT_BUNDLE_IDENTIFIER = "com.huxiaoxing.SwiftOrg-macOS"; 673 | PRODUCT_NAME = SwiftOrg; 674 | SDKROOT = macosx; 675 | SKIP_INSTALL = YES; 676 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 677 | SWIFT_VERSION = 3.0; 678 | }; 679 | name = Debug; 680 | }; 681 | 03BBC3C31DF81104003FCB3C /* Release */ = { 682 | isa = XCBuildConfiguration; 683 | buildSettings = { 684 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 685 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 686 | CODE_SIGN_IDENTITY = "-"; 687 | COMBINE_HIDPI_IMAGES = YES; 688 | DEFINES_MODULE = YES; 689 | DYLIB_COMPATIBILITY_VERSION = 1; 690 | DYLIB_CURRENT_VERSION = 1; 691 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 692 | FRAMEWORK_VERSION = A; 693 | INFOPLIST_FILE = "Sources/Info-macOS.plist"; 694 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 695 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 696 | MACOSX_DEPLOYMENT_TARGET = 10.10; 697 | PRODUCT_BUNDLE_IDENTIFIER = "com.huxiaoxing.SwiftOrg-macOS"; 698 | PRODUCT_NAME = SwiftOrg; 699 | SDKROOT = macosx; 700 | SKIP_INSTALL = YES; 701 | SWIFT_VERSION = 3.0; 702 | }; 703 | name = Release; 704 | }; 705 | 03BBC3D01DF8127C003FCB3C /* Debug */ = { 706 | isa = XCBuildConfiguration; 707 | buildSettings = { 708 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 709 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 710 | CODE_SIGN_IDENTITY = ""; 711 | DEFINES_MODULE = YES; 712 | DYLIB_COMPATIBILITY_VERSION = 1; 713 | DYLIB_CURRENT_VERSION = 1; 714 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 715 | INFOPLIST_FILE = "Sources/Info-iOS.plist"; 716 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 717 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 718 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 719 | PRODUCT_BUNDLE_IDENTIFIER = "com.huxiaoxing.SwiftOrg-iOS"; 720 | PRODUCT_NAME = SwiftOrg; 721 | SKIP_INSTALL = YES; 722 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 723 | SWIFT_VERSION = 3.0; 724 | }; 725 | name = Debug; 726 | }; 727 | 03BBC3D11DF8127C003FCB3C /* Release */ = { 728 | isa = XCBuildConfiguration; 729 | buildSettings = { 730 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 731 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 732 | CODE_SIGN_IDENTITY = ""; 733 | DEFINES_MODULE = YES; 734 | DYLIB_COMPATIBILITY_VERSION = 1; 735 | DYLIB_CURRENT_VERSION = 1; 736 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 737 | INFOPLIST_FILE = "Sources/Info-iOS.plist"; 738 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 739 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 740 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 741 | PRODUCT_BUNDLE_IDENTIFIER = "com.huxiaoxing.SwiftOrg-iOS"; 742 | PRODUCT_NAME = SwiftOrg; 743 | SKIP_INSTALL = YES; 744 | SWIFT_VERSION = 3.0; 745 | }; 746 | name = Release; 747 | }; 748 | 03F9E6AE1D60411A0080E872 /* Debug */ = { 749 | isa = XCBuildConfiguration; 750 | buildSettings = { 751 | ALWAYS_SEARCH_USER_PATHS = NO; 752 | CLANG_ANALYZER_NONNULL = YES; 753 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 754 | CLANG_CXX_LIBRARY = "libc++"; 755 | CLANG_ENABLE_MODULES = YES; 756 | CLANG_ENABLE_OBJC_ARC = YES; 757 | CLANG_WARN_BOOL_CONVERSION = YES; 758 | CLANG_WARN_CONSTANT_CONVERSION = YES; 759 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 760 | CLANG_WARN_EMPTY_BODY = YES; 761 | CLANG_WARN_ENUM_CONVERSION = YES; 762 | CLANG_WARN_INFINITE_RECURSION = YES; 763 | CLANG_WARN_INT_CONVERSION = YES; 764 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 765 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 766 | CLANG_WARN_UNREACHABLE_CODE = YES; 767 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 768 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 769 | COPY_PHASE_STRIP = NO; 770 | CURRENT_PROJECT_VERSION = 1; 771 | DEBUG_INFORMATION_FORMAT = dwarf; 772 | ENABLE_STRICT_OBJC_MSGSEND = YES; 773 | ENABLE_TESTABILITY = YES; 774 | GCC_C_LANGUAGE_STANDARD = gnu99; 775 | GCC_DYNAMIC_NO_PIC = NO; 776 | GCC_NO_COMMON_BLOCKS = YES; 777 | GCC_OPTIMIZATION_LEVEL = 0; 778 | GCC_PREPROCESSOR_DEFINITIONS = ( 779 | "DEBUG=1", 780 | "$(inherited)", 781 | ); 782 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 783 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 784 | GCC_WARN_UNDECLARED_SELECTOR = YES; 785 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 786 | GCC_WARN_UNUSED_FUNCTION = YES; 787 | GCC_WARN_UNUSED_VARIABLE = YES; 788 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 789 | MACOSX_DEPLOYMENT_TARGET = 10.10; 790 | MTL_ENABLE_DEBUG_INFO = YES; 791 | ONLY_ACTIVE_ARCH = YES; 792 | SDKROOT = iphoneos; 793 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 794 | TARGETED_DEVICE_FAMILY = "1,2"; 795 | VERSIONING_SYSTEM = "apple-generic"; 796 | VERSION_INFO_PREFIX = ""; 797 | }; 798 | name = Debug; 799 | }; 800 | 03F9E6AF1D60411A0080E872 /* Release */ = { 801 | isa = XCBuildConfiguration; 802 | buildSettings = { 803 | ALWAYS_SEARCH_USER_PATHS = NO; 804 | CLANG_ANALYZER_NONNULL = YES; 805 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 806 | CLANG_CXX_LIBRARY = "libc++"; 807 | CLANG_ENABLE_MODULES = YES; 808 | CLANG_ENABLE_OBJC_ARC = YES; 809 | CLANG_WARN_BOOL_CONVERSION = YES; 810 | CLANG_WARN_CONSTANT_CONVERSION = YES; 811 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 812 | CLANG_WARN_EMPTY_BODY = YES; 813 | CLANG_WARN_ENUM_CONVERSION = YES; 814 | CLANG_WARN_INFINITE_RECURSION = YES; 815 | CLANG_WARN_INT_CONVERSION = YES; 816 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 817 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 818 | CLANG_WARN_UNREACHABLE_CODE = YES; 819 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 820 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 821 | COPY_PHASE_STRIP = NO; 822 | CURRENT_PROJECT_VERSION = 1; 823 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 824 | ENABLE_NS_ASSERTIONS = NO; 825 | ENABLE_STRICT_OBJC_MSGSEND = YES; 826 | GCC_C_LANGUAGE_STANDARD = gnu99; 827 | GCC_NO_COMMON_BLOCKS = YES; 828 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 829 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 830 | GCC_WARN_UNDECLARED_SELECTOR = YES; 831 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 832 | GCC_WARN_UNUSED_FUNCTION = YES; 833 | GCC_WARN_UNUSED_VARIABLE = YES; 834 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 835 | MACOSX_DEPLOYMENT_TARGET = 10.10; 836 | MTL_ENABLE_DEBUG_INFO = NO; 837 | SDKROOT = iphoneos; 838 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 839 | TARGETED_DEVICE_FAMILY = "1,2"; 840 | VALIDATE_PRODUCT = YES; 841 | VERSIONING_SYSTEM = "apple-generic"; 842 | VERSION_INFO_PREFIX = ""; 843 | }; 844 | name = Release; 845 | }; 846 | 03F9E6B41D60411A0080E872 /* Debug */ = { 847 | isa = XCBuildConfiguration; 848 | buildSettings = { 849 | DEVELOPMENT_TEAM = H5G26MVS9D; 850 | INFOPLIST_FILE = "Tests/Info-iOS.plist"; 851 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 852 | PRODUCT_BUNDLE_IDENTIFIER = "com.huxiaoxing.SwiftOrgTests-iOS"; 853 | PRODUCT_NAME = "$(TARGET_NAME)"; 854 | SWIFT_VERSION = 3.0; 855 | }; 856 | name = Debug; 857 | }; 858 | 03F9E6B51D60411A0080E872 /* Release */ = { 859 | isa = XCBuildConfiguration; 860 | buildSettings = { 861 | DEVELOPMENT_TEAM = H5G26MVS9D; 862 | INFOPLIST_FILE = "Tests/Info-iOS.plist"; 863 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 864 | PRODUCT_BUNDLE_IDENTIFIER = "com.huxiaoxing.SwiftOrgTests-iOS"; 865 | PRODUCT_NAME = "$(TARGET_NAME)"; 866 | SWIFT_VERSION = 3.0; 867 | }; 868 | name = Release; 869 | }; 870 | /* End XCBuildConfiguration section */ 871 | 872 | /* Begin XCConfigurationList section */ 873 | 0382A18D1DF826730015B475 /* Build configuration list for PBXNativeTarget "SwiftOrgTests-macOS" */ = { 874 | isa = XCConfigurationList; 875 | buildConfigurations = ( 876 | 0382A18B1DF826730015B475 /* Debug */, 877 | 0382A18C1DF826730015B475 /* Release */, 878 | ); 879 | defaultConfigurationIsVisible = 0; 880 | defaultConfigurationName = Release; 881 | }; 882 | 03BBC3C41DF81104003FCB3C /* Build configuration list for PBXNativeTarget "SwiftOrg-macOS" */ = { 883 | isa = XCConfigurationList; 884 | buildConfigurations = ( 885 | 03BBC3C21DF81104003FCB3C /* Debug */, 886 | 03BBC3C31DF81104003FCB3C /* Release */, 887 | ); 888 | defaultConfigurationIsVisible = 0; 889 | defaultConfigurationName = Release; 890 | }; 891 | 03BBC3CF1DF8127C003FCB3C /* Build configuration list for PBXNativeTarget "SwiftOrg-iOS" */ = { 892 | isa = XCConfigurationList; 893 | buildConfigurations = ( 894 | 03BBC3D01DF8127C003FCB3C /* Debug */, 895 | 03BBC3D11DF8127C003FCB3C /* Release */, 896 | ); 897 | defaultConfigurationIsVisible = 0; 898 | defaultConfigurationName = Release; 899 | }; 900 | 03F9E6961D6041190080E872 /* Build configuration list for PBXProject "SwiftOrg" */ = { 901 | isa = XCConfigurationList; 902 | buildConfigurations = ( 903 | 03F9E6AE1D60411A0080E872 /* Debug */, 904 | 03F9E6AF1D60411A0080E872 /* Release */, 905 | ); 906 | defaultConfigurationIsVisible = 0; 907 | defaultConfigurationName = Release; 908 | }; 909 | 03F9E6B31D60411A0080E872 /* Build configuration list for PBXNativeTarget "SwiftOrgTests-iOS" */ = { 910 | isa = XCConfigurationList; 911 | buildConfigurations = ( 912 | 03F9E6B41D60411A0080E872 /* Debug */, 913 | 03F9E6B51D60411A0080E872 /* Release */, 914 | ); 915 | defaultConfigurationIsVisible = 0; 916 | defaultConfigurationName = Release; 917 | }; 918 | /* End XCConfigurationList section */ 919 | }; 920 | rootObject = 03F9E6931D6041190080E872 /* Project object */; 921 | } 922 | -------------------------------------------------------------------------------- /SwiftOrg.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftOrg.xcodeproj/xcshareddata/xcschemes/SwiftOrg-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /SwiftOrg.xcodeproj/xcshareddata/xcschemes/SwiftOrg-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Tests/Info-iOS.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 | BNDL 17 | CFBundleShortVersionString 18 | 0.7.9 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/Info-macOS.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 | BNDL 17 | CFBundleShortVersionString 18 | 0.7.9 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | @testable import SwiftOrgTests 2 | import XCTest 3 | 4 | XCTMain([testCase(TokenizerTests.allTests), 5 | testCase(IndexingTests.allTests), 6 | testCase(InlineParsingTests.allTests), 7 | testCase(ParserTests.allTests), 8 | testCase(TimestampTests.allTests), 9 | testCase(ConvertToJSONTests.allTests), 10 | ]) 11 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/Asserts.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Asserts.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 15/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftOrg 11 | 12 | func expect(_ actual: Token, toBe expected: Token, 13 | file: StaticString = #file, line: UInt = #line) { 14 | XCTAssertEqual(expected, actual, 15 | file: file, line: line) 16 | } 17 | 18 | 19 | func evalListItem(_ str: String, indent: Int, text: String?, ordered: Bool, checked: Bool? = nil, 20 | file: StaticString = #file, line: UInt = #line) { 21 | 22 | let token = Lexer.tokenize(line: str) 23 | expect(token!, toBe: .listItem(indent: indent, text: text, ordered: ordered, checked: checked), 24 | file: file, line: line) 25 | } 26 | 27 | func evalHorizontalRule(_ str: String, 28 | file: StaticString = #file, line: UInt = #line) { 29 | let token = Lexer.tokenize(line: str) 30 | expect(token!, toBe: .horizontalRule, 31 | file: file, line: line) 32 | } 33 | 34 | func evalComment(_ str: String, text: String, 35 | file: StaticString = #file, line: UInt = #line) { 36 | let token = Lexer.tokenize(line: str) 37 | expect(token!, toBe: .comment(text), 38 | file: file, line: line) 39 | } 40 | 41 | func evalBlockEnd(_ str: String, type: String, 42 | file: StaticString = #file, line: UInt = #line) { 43 | let token = Lexer.tokenize(line: str) 44 | expect(token!, toBe: .blockEnd(name: type), 45 | file: file, line: line) 46 | } 47 | 48 | func evalBlockBegin(_ str: String, type: String, params: [String]?, 49 | file: StaticString = #file, line: UInt = #line) { 50 | let token = Lexer.tokenize(line: str) 51 | expect(token!, toBe: .blockBegin(name: type, params: params), 52 | file: file, line: line) 53 | } 54 | 55 | func evalHeadline(_ str: String, stars: Int, text: String?, 56 | file: StaticString = #file, line: UInt = #line) { 57 | let token = Lexer.tokenize(line: str) 58 | expect(token!, toBe: .headline(stars: stars, text: text), 59 | file: file, line: line) 60 | } 61 | 62 | func evalPlanning(_ str: String, keyword: String, timestamp: Timestamp?, 63 | file: StaticString = #file, line: UInt = #line) { 64 | let token = Lexer.tokenize(line: str) 65 | expect(token!, toBe: .planning(keyword: PlanningKeyword(rawValue: keyword)!, timestamp: timestamp), 66 | file: file, line: line) 67 | } 68 | 69 | func evalSetting(_ str: String, key: String, value: String?, 70 | file: StaticString = #file, line: UInt = #line) { 71 | let token = Lexer.tokenize(line: str) 72 | expect(token!, toBe: .setting(key: key, value: value), 73 | file: file, line: line) 74 | } 75 | 76 | func evalBlank(_ str: String, rawIsNil: Bool = false, 77 | file: StaticString = #file, line: UInt = #line) { 78 | let token = Lexer.tokenize(line: str) 79 | expect(token!, toBe: .blank, 80 | file: file, line: line) 81 | } 82 | 83 | func evalLine(_ str: String, text: String, 84 | file: StaticString = #file, line: UInt = #line) { 85 | let token = Lexer.tokenize(line: str) 86 | expect(token!, toBe: .line(text: text), 87 | file: file, line: line) 88 | } 89 | 90 | func evalDrawerBegin(_ str: String, name: String, 91 | file: StaticString = #file, line: UInt = #line) { 92 | let token = Lexer.tokenize(line: str) 93 | expect(token!, toBe: .drawerBegin(name: name), 94 | file: file, line: line) 95 | } 96 | 97 | func evalDrawerEnd(_ str: String, 98 | file: StaticString = #file, line: UInt = #line) { 99 | let token = Lexer.tokenize(line: str) 100 | expect(token!, toBe: .drawerEnd, 101 | file: file, line: line) 102 | } 103 | 104 | func evalFootnote(_ str: String, label: String, content: String?, 105 | file: StaticString = #file, line: UInt = #line) { 106 | let token = Lexer.tokenize(line: str) 107 | expect(token!, toBe: .footnote(label: label, content: content), 108 | file: file, line: line) 109 | } 110 | 111 | func evalTableRow(_ str: String, cells: [String], 112 | file: StaticString = #file, line: UInt = #line) { 113 | let token = Lexer.tokenize(line: str) 114 | expect(token!, toBe: .tableRow(cells: cells), 115 | file: file, line: line) 116 | } 117 | 118 | func evalHorizontalSeparator(_ str: String, 119 | file: StaticString = #file, line: UInt = #line) { 120 | let token = Lexer.tokenize(line: str) 121 | expect(token!, toBe: .horizontalSeparator, 122 | file: file, line: line) 123 | } 124 | 125 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/ConvertToJSONTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConvertToJSONTests.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 27/02/17. 6 | // Copyright © 2017 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import SwiftOrg 11 | 12 | class ConvertToJSONTests: XCTestCase { 13 | func testJSONConvertion() throws { 14 | let lines = [ 15 | "#+TITLE: Org Mode Syntax", 16 | "#+TODO: TODO NEXT | DONE", 17 | "", 18 | "* TODO Section One :tag1:tag2:", 19 | " DEADLINE: <2017-02-28 Tue>", 20 | " :PROPERTIES:", 21 | " :CATEGORY: nice", 22 | " :END:", 23 | "", 24 | " Fist line of a paragraph.", 25 | " Second line of a paragraph.", 26 | "-----", 27 | "| Name | Species | Gender | Role |", 28 | "|--------------+------------+--------+--------------|", 29 | "| Bruce Wayne | Human | M | Batman |", 30 | "| Clark Kent | Kryptonian | M | Superman |", 31 | "| Diana Prince | Amazonian | F | Wonder Woman |", 32 | "-----", 33 | "- list item one", 34 | "- list item tow", 35 | " - list item tow.one", 36 | "-----", 37 | "#+BEGIN_SRC swift", 38 | "print(\"org-mode is awesome.\")", 39 | "#+END_SRC", 40 | "-----", 41 | "# This is a comment.", 42 | "* Section Two", 43 | "** Section Two.One", 44 | ] 45 | 46 | let doc = parse(lines) 47 | let json = doc?.toJSON() 48 | XCTAssertEqual(json?["type"] as? String, "document") 49 | XCTAssertEqual(json?["title"] as? String, "Org Mode Syntax") 50 | 51 | guard let todos = json?["todos"] as? [[String]] else { 52 | XCTFail("should have todos") 53 | return 54 | } 55 | XCTAssertEqual(todos.count, 2) 56 | XCTAssertEqual(todos[0], ["TODO", "NEXT"]) 57 | XCTAssertEqual(todos[1], ["DONE"]) 58 | 59 | guard let settings = json?["settings"] as? [String: String] else { 60 | XCTFail("should have settings") 61 | return 62 | } 63 | 64 | XCTAssertEqual(settings["TITLE"], "Org Mode Syntax") 65 | XCTAssertEqual(settings["TODO"], "TODO NEXT | DONE") 66 | 67 | guard let content = json?["content"] as? [[String: Any?]] else { 68 | XCTFail("should have content") 69 | return 70 | } 71 | 72 | XCTAssertEqual(content.count, 2) 73 | 74 | 75 | let section1 = content[0] 76 | 77 | XCTAssertEqual(section1["title"] as? String, "Section One") 78 | XCTAssertEqual(section1["type"] as? String, "section") 79 | XCTAssertEqual(section1["stars"] as? Int, 1) 80 | XCTAssertEqual(section1["keyword"] as? String, "TODO") 81 | 82 | guard let tags = section1["tags"] as? [String] else { 83 | XCTFail("should have tags") 84 | return 85 | } 86 | XCTAssertEqual(tags, ["tag1", "tag2"]) 87 | 88 | guard let planning = section1["planning"] as? [String: Any?] else { 89 | XCTFail("should have planning") 90 | return 91 | } 92 | 93 | XCTAssertEqual(planning["type"] as? String, "planning") 94 | XCTAssertEqual(planning["keyword"] as? String, "DEADLINE") 95 | 96 | guard let drawers = section1["drawers"] as? [[String: Any?]] else { 97 | XCTFail("should have drawers") 98 | return 99 | } 100 | 101 | XCTAssertEqual(drawers.count, 1) 102 | XCTAssertEqual(drawers[0]["type"] as? String, "drawer") 103 | XCTAssertEqual(drawers[0]["name"] as? String, "PROPERTIES") 104 | XCTAssertEqual((drawers[0]["content"] as? [String])!, [" :CATEGORY: nice"]) // TODO properly parse properties 105 | 106 | // TODO finish the rest 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/IndexingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndexingTests.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 17/11/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import SwiftOrg 11 | 12 | fileprivate func eval(_ node: Node?, makesure: (Section) -> Void) { 13 | if let doc = node as? OrgDocument { 14 | doc.content.forEach { n in 15 | eval(n, makesure: makesure) 16 | } 17 | return 18 | } 19 | guard let section = node as? Section else { 20 | XCTFail("Section is expected") 21 | return 22 | } 23 | XCTAssertNotNil(section.index) 24 | // print(">> eval: \(section.index!)") 25 | // print(" hash: \(section.index!.hashValue)") 26 | makesure(section) 27 | for n in section.content { 28 | if let subSection = n as? Section { 29 | eval(subSection, makesure: makesure) 30 | } 31 | } 32 | } 33 | 34 | class IndexingTests: XCTestCase { 35 | 36 | func testIndexing() { 37 | let index = OrgIndex([0, 1, 2]) 38 | XCTAssertEqual(index.in.description, "0.1.2.0") 39 | XCTAssertEqual(index.out.description, "0.1") 40 | XCTAssertEqual(index.next.description, "0.1.3") 41 | XCTAssertEqual(index.prev.description, "0.1.1") 42 | } 43 | 44 | func testSectionIndexing() { 45 | guard let doc = parse([ 46 | "* Section 0", 47 | "** Section 0.0", 48 | "** Section 0.1", 49 | "*** Section 0.1.0", 50 | "*** Section 0.1.1", 51 | "*** Section 0.1.2", 52 | "**** Section 0.1.2.0", 53 | "* Section 1", 54 | "* Section 2", 55 | ]) else { XCTFail("failed to parse lines."); return } 56 | 57 | eval(doc) { section in 58 | XCTAssertEqual("Section \(section.index!.description)", section.title) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/InlineParsingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InlineParsingTests.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 17/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import SwiftOrg 11 | 12 | class InlineParsingTests: XCTestCase { 13 | 14 | func testInlineParsing() { 15 | let text = "hello *world*, and /Welcome/ to *org* world. and [[http://google.com][this]] is a link. and [[/image/logo.png][this]] is a image." 16 | // let splitted = text.matchSplit("(\\*)([\\s\\S]*?)\\1", options: []) 17 | let lexer = InlineLexer(text: text) 18 | let tokens = lexer.tokenize() 19 | var index = 0 20 | XCTAssertEqual(tokens[index], .plain("hello ")); index += 1 21 | XCTAssertEqual(tokens[index], .bold("world")); index += 1 22 | XCTAssertEqual(tokens[index], .plain(", and ")); index += 1 23 | XCTAssertEqual(tokens[index], .italic("Welcome")); index += 1 24 | XCTAssertEqual(tokens[index], .plain(" to ")); index += 1 25 | XCTAssertEqual(tokens[index], .bold("org")); index += 1 26 | XCTAssertEqual(tokens[index], .plain(" world. and ")); index += 1 27 | XCTAssertEqual(tokens[index], .link(text: "this", url: "http://google.com")); index += 1 28 | XCTAssertEqual(tokens[index], .plain(" is a link. and ")); index += 1 29 | XCTAssertEqual(tokens[index], .link(text: "this", url: "/image/logo.png")); index += 1 30 | XCTAssertEqual(tokens[index], .plain(" is a image.")); index += 1 31 | XCTAssertEqual(tokens.count, index) 32 | } 33 | 34 | func testCornerCases() { 35 | let text = "hello **world** ****world****" 36 | let tokens = InlineLexer(text: text).tokenize() 37 | var index = 0 38 | XCTAssertEqual(tokens[index], .plain("hello ")); index += 1 39 | XCTAssertEqual(tokens[index], .bold("world")); index += 1 40 | XCTAssertEqual(tokens[index], .plain(" ")); index += 1 41 | XCTAssertEqual(tokens[index], .bold("world")); index += 1 42 | XCTAssertEqual(tokens.count, index) 43 | } 44 | 45 | func testInlineFootnote() { 46 | let text = "This is a footnote right here[fn:1]. And this is the rest of the line." 47 | let tokens = InlineLexer(text: text).tokenize() 48 | 49 | var index = 0 50 | XCTAssertEqual(tokens[index], .plain("This is a footnote right here")); index += 1 51 | XCTAssertEqual(tokens[index], .footnote("1")); index += 1 52 | XCTAssertEqual(tokens[index], .plain(". And this is the rest of the line.")); index += 1 53 | XCTAssertEqual(tokens.count, index) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/LexerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LexerTests.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 8/12/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftOrg 11 | 12 | class LexerTests: XCTestCase { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/OrgFileWriterTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OrgFileWriterTests.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 29/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import SwiftOrg 11 | 12 | class OrgFileWriterTests: XCTestCase { 13 | 14 | // func testOrgFileWriter() throws { 15 | 16 | // let path = Bundle(for: type(of: self)).path(forResource: "README", ofType: "org") 17 | // let content = try String(contentsOfFile: path!) 18 | // let parser = OrgParser() 19 | // let doc = try parser.parse(content: content) 20 | 21 | // _ = doc.toText() 22 | // // print(text) 23 | // } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/ParserTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParserTests.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 21/02/17. 6 | // Copyright © 2017 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import SwiftOrg 11 | 12 | fileprivate func eval(_ node: Node?, makesure: (Section) -> Void) { 13 | guard let section = node as? Section else { 14 | XCTFail("Section is expected") 15 | return 16 | } 17 | makesure(section) 18 | } 19 | 20 | class ParserTests: XCTestCase { 21 | 22 | override func setUp() { 23 | super.setUp() 24 | // Put setup code here. This method is called before the invocation of each test method in the class. 25 | } 26 | 27 | // MARK: Settings 28 | func testParseSettings() { 29 | guard let doc = parse([ 30 | "#+options: toc:nil", 31 | "#+TITLE: ", 32 | " ", 33 | "* First Head Line", 34 | ]) else { return } 35 | XCTAssertEqual(doc.settings.count, 1) 36 | XCTAssertEqual(doc.settings["options"], "toc:nil") 37 | XCTAssertNil(doc.settings["TITLE"]) 38 | } 39 | 40 | func testDefaultTodos() { 41 | let parser = OrgParser(defaultTodos: [["TODO", "NEXT"], ["DONE", "CANCELED"]]) 42 | let content = ["Use Default TODOs"] 43 | guard let docWithDefaultParser = parse(content) else { return } 44 | guard let docWithModifiedParser = parse(content, with: parser) else { return } 45 | 46 | XCTAssertEqual(2, docWithDefaultParser.todos.count) 47 | XCTAssertEqual(["TODO"], docWithDefaultParser.todos[0]) 48 | XCTAssertEqual(["DONE"], docWithDefaultParser.todos[1]) 49 | XCTAssertEqual(2, docWithModifiedParser.todos.count) 50 | XCTAssertEqual(["TODO", "NEXT"], docWithModifiedParser.todos[0]) 51 | XCTAssertEqual(["DONE", "CANCELED"], docWithModifiedParser.todos[1]) 52 | } 53 | 54 | func testInBufferTodos() { 55 | guard let doc = parse([ 56 | "#+TODO: HELLO WORLD | ORG MODE", 57 | ]) else { return } 58 | XCTAssertEqual(2, doc.todos.count) 59 | XCTAssertEqual(["HELLO", "WORLD"], doc.todos[0]) 60 | XCTAssertEqual(["ORG", "MODE"], doc.todos[1]) 61 | } 62 | 63 | // MARK: Heading 64 | 65 | func testParseHeadline() { 66 | guard let doc = parse([ 67 | "#+TODO: NEXT TODO", 68 | "* Header 1", 69 | "* TODO Header 2", 70 | " A line of content.", 71 | "** Header 2.1", 72 | "*** Header 2.1.1", 73 | "** Header 2.2", 74 | "* NEXT Customized todo", 75 | "* " 76 | ]) else { return } 77 | XCTAssertEqual(doc.content.count, 4) 78 | 79 | eval(doc.content[0]) { sec in 80 | XCTAssertEqual(sec.stars, 1) 81 | XCTAssertEqual(sec.title, "Header 1") 82 | XCTAssertEqual(sec.content.count, 0) 83 | } 84 | 85 | eval(doc.content[1]) { sec in 86 | XCTAssertEqual(sec.stars, 1) 87 | XCTAssertEqual(sec.title, "Header 2") 88 | XCTAssertEqual(sec.keyword, "TODO") 89 | XCTAssertEqual(sec.content.count, 3) 90 | guard let line = sec.content[0] as? Paragraph else { 91 | XCTFail("Expect h2.nodes[0] to be Line") 92 | return 93 | } 94 | XCTAssertEqual(line.text, "A line of content.") 95 | } 96 | 97 | eval(doc.content[2]) { sec in 98 | XCTAssertEqual(sec.title, "Customized todo") 99 | XCTAssertEqual(sec.keyword, "NEXT") 100 | } 101 | 102 | eval(doc.content[3]) { sec in 103 | XCTAssertNil(sec.title) 104 | XCTAssertNil(sec.keyword) 105 | } 106 | } 107 | 108 | func testParseDrawer() { 109 | let lines = [ 110 | "* the headline", 111 | " :LOGBOOK:", 112 | " - hello world", 113 | " - hello world again", 114 | " :END:", 115 | " :PROPERTIES:", 116 | " - property 1", 117 | " - property 2", 118 | " :END:", 119 | ] 120 | let doc = parse(lines) 121 | 122 | eval(doc?.content[0]) { section in 123 | XCTAssertNotNil(section.drawers) 124 | XCTAssertEqual(section.drawers?.count, 2) 125 | XCTAssertEqual(section.drawers?[0].name, "LOGBOOK") 126 | XCTAssertEqual(section.drawers?[1].name, "PROPERTIES") 127 | } 128 | } 129 | 130 | func testMalfunctionDrawer() { 131 | let lines = [ 132 | "* the headline", 133 | "", 134 | " :LOGBOOK:", 135 | " hello world", 136 | " hello world again", 137 | " :END:", 138 | ] 139 | let doc = parse(lines) 140 | 141 | eval(doc?.content[0]) { section in 142 | XCTAssertEqual(1, section.drawers?.count) 143 | } 144 | // XCTAssertEqual(section.content.count, 2) 145 | } 146 | 147 | func testPriority() { 148 | guard let doc = parse([ 149 | "* TODO [#A] top priority task", 150 | "* [#A] top priority item", 151 | "* [#D] no priority item", 152 | "* DONE [#a] top priority item", 153 | "* BREAKING [#A] no priority item", 154 | ]) else { XCTFail("failed to parse lines."); return } 155 | eval(doc.content[0]) { sec in 156 | XCTAssertEqual(sec.priority, .A) 157 | XCTAssertEqual(sec.keyword, "TODO") 158 | XCTAssertEqual(sec.title, "top priority task") 159 | } 160 | 161 | eval(doc.content[1]) { sec in 162 | XCTAssertEqual(sec.priority, .A) 163 | XCTAssertEqual(sec.keyword, nil) 164 | XCTAssertEqual(sec.title, "top priority item") 165 | } 166 | 167 | eval(doc.content[2]) { sec in 168 | XCTAssertEqual(sec.priority, nil) 169 | XCTAssertEqual(sec.keyword, nil) 170 | XCTAssertEqual(sec.title, "[#D] no priority item") 171 | } 172 | 173 | eval(doc.content[3]) { sec in 174 | XCTAssertEqual(sec.priority, .A) 175 | XCTAssertEqual(sec.keyword, "DONE") 176 | XCTAssertEqual(sec.title, "top priority item") 177 | } 178 | 179 | eval(doc.content[4]) { sec in 180 | XCTAssertEqual(sec.priority, nil) 181 | XCTAssertEqual(sec.keyword, nil) 182 | XCTAssertEqual(sec.title, "BREAKING [#A] no priority item") 183 | } 184 | } 185 | 186 | func testTags() { 187 | guard let doc = parse([ 188 | "* line with one tag. :tag1:", 189 | "* line with multiple tags. :tag1:tag2:tag3:", 190 | "* line with trailing spaces. :tag1:tag2:tag3: ", 191 | ]) else { XCTFail("failed to parse lines."); return } 192 | 193 | eval(doc.content[0]) { sec in 194 | XCTAssertNotNil(sec.tags) 195 | XCTAssertEqual(sec.tags!, ["tag1"]) 196 | } 197 | eval(doc.content[1]) { sec in 198 | XCTAssertEqual(sec.tags!, ["tag1", "tag2", "tag3"]) 199 | } 200 | 201 | eval(doc.content[2]) { sec in 202 | XCTAssertEqual(sec.tags!, ["tag1", "tag2", "tag3"]) 203 | } 204 | } 205 | 206 | func testPlanning() { 207 | let keyword = "CLOSED" 208 | let date = "2017-01-09" 209 | let day = "Tue" 210 | let time = "18:00" 211 | guard let doc = parse([ 212 | "* line with planning", 213 | " \(keyword): [\(date) \(day) \(time)]", 214 | "* line without planning", 215 | ]) else { XCTFail("failed to parse lines."); return } 216 | 217 | eval(doc.content[0]) { sec in 218 | XCTAssertNotNil(sec.planning) 219 | guard let planning = sec.planning else { 220 | XCTFail("Failed to parse planning") 221 | return 222 | } 223 | XCTAssertEqual(planning.keyword.rawValue, keyword) 224 | XCTAssertFalse(planning.timestamp!.active) 225 | XCTAssertEqual(planning.timestamp?.date, quickDate(date: date, time: time)) 226 | } 227 | 228 | eval(doc.content[1]) { sec in 229 | XCTAssertNil(sec.planning) 230 | } 231 | } 232 | 233 | // MARK: Block 234 | 235 | func testParseBlock() { 236 | guard let doc = parse([ 237 | "#+begin_src java", 238 | " class HelloWorld {", 239 | " # print(\"Hell World\");", 240 | " }", 241 | "#+END_SRC", 242 | " #+begin_src", 243 | " #+end_src", 244 | " #+begin_src yaml exports: results :results value html", 245 | "#+END_SRC", 246 | "# +begin_src java", 247 | "#+begin_src no-end", 248 | " This is a normal line", 249 | " # print(\"Hell World\");", 250 | ]) else { return } 251 | 252 | XCTAssertEqual(doc.content.count, 6) 253 | guard let block1 = doc.content[0] as? Block else { 254 | XCTFail("Expect 0 to be Block") 255 | return 256 | } 257 | XCTAssertEqual(block1.name, "src") 258 | XCTAssertEqual(block1.params!, ["java"]) 259 | XCTAssertEqual(block1.content, [" class HelloWorld {", " # print(\"Hell World\");", " }"]) 260 | guard let block2 = doc.content[1] as? Block else { 261 | XCTFail("Expect 1 to be Block") 262 | return 263 | } 264 | XCTAssertEqual(block2.name, "src") 265 | XCTAssertNil(block2.params) 266 | guard let block3 = doc.content[2] as? Block else { 267 | XCTFail("Expect 2 to be Block") 268 | return 269 | } 270 | XCTAssertEqual(block3.name, "src") 271 | XCTAssertEqual(block3.params!, ["yaml", "exports:", "results", ":results", "value", "html"]) 272 | guard let comment = doc.content[3] as? Comment else { 273 | XCTFail("Expect 3 to be Comment") 274 | return 275 | } 276 | XCTAssertEqual(comment.text, "+begin_src java") 277 | 278 | // TODO make these assertion work 279 | // expect(doc.children[4].value).to(beAnInstanceOf(Paragraph)) 280 | // expect(doc.children[5].value).to(beAnInstanceOf(Comment)) 281 | } 282 | 283 | // MARK: List 284 | func testParseList() { 285 | let lines = [ 286 | "- list item", 287 | " 1. sub list item", 288 | " 1. sub list item", 289 | "- list item", 290 | ] 291 | let doc = parse(lines) 292 | guard let list = doc?.content[0] as? List else { 293 | XCTFail("Expect 0 to be List") 294 | return 295 | } 296 | XCTAssertEqual(list.items.count, 2) 297 | XCTAssertFalse(list.ordered) 298 | XCTAssertEqual(list.items[0].text, "list item") 299 | XCTAssertEqual(list.items[1].text, "list item") 300 | XCTAssertNil(list.items[1].subList) 301 | 302 | guard let subList = list.items[0].subList else { 303 | XCTFail("Expecting sublist") 304 | return 305 | } 306 | XCTAssertEqual(subList.items.count, 2) 307 | XCTAssertTrue(subList.ordered) 308 | XCTAssertEqual(subList.items[0].text, "sub list item") 309 | XCTAssertEqual(subList.items[1].text, "sub list item") 310 | } 311 | 312 | func testListItemWithCheckbox() { 313 | let lines = [ 314 | // legal checkboxes 315 | "- [ ] list item", 316 | "- [X] list item", 317 | "1. [-] list item", 318 | // illegal checkboxes 319 | "- [] list item", 320 | "- [Y] list item", 321 | ] 322 | let doc = parse(lines) 323 | guard let list = doc?.content[0] as? List else { 324 | XCTFail("Expect 0 to be List") 325 | return 326 | } 327 | XCTAssertEqual(list.items.count, 5) 328 | XCTAssertFalse(list.ordered) 329 | XCTAssertEqual(list.items[0].text, "list item") 330 | XCTAssertEqual(list.progress, Progress(1, outof: 3)) 331 | } 332 | 333 | // MARK: Paragraph 334 | 335 | func testParseParagraph() { 336 | let lines = [ 337 | "Line one.", 338 | "Line two.", 339 | "Line three.", 340 | "", 341 | "Line four.", 342 | "Line five.", 343 | ] 344 | let doc = parse(lines) 345 | guard let para1 = doc?.content[0] as? Paragraph else { 346 | XCTFail("Expect 0 to be Paragraph") 347 | return 348 | } 349 | XCTAssertEqual(para1.lines.count, 3) 350 | XCTAssertEqual(para1.lines, ["Line one.", "Line two.", "Line three."]) 351 | 352 | guard let para2 = doc?.content[1] as? Paragraph else { 353 | XCTFail("Expect 0 to be Paragraph") 354 | return 355 | } 356 | XCTAssertEqual(para2.lines.count, 2) 357 | XCTAssertEqual(para2.lines, ["Line four.", "Line five."]) 358 | } 359 | 360 | 361 | // MARK: Table 362 | func testParseTable() { 363 | guard let doc = parse([ 364 | "| Name | Species | Gender | Role |", 365 | "|--------------+------------+--------+--------------|", 366 | "| Bruce Wayne | Human | M | Batman |", 367 | "| Clark Kent | Kryptonian | M | Superman |", 368 | "| Diana Prince | Amazonian | F | Wonder Woman |", 369 | ]) else { return } 370 | 371 | XCTAssertEqual(doc.content.count, 1) 372 | guard let table = doc.content[0] as? Table else { 373 | XCTFail("Expect 0 to be Table") 374 | return 375 | } 376 | XCTAssertEqual(table.rows.count, 4) 377 | XCTAssertEqual(table.rows[0].cells, ["Name", "Species", "Gender", "Role"]) 378 | XCTAssertTrue(table.rows[0].hasSeparator) 379 | XCTAssertEqual(table.rows[1].cells, ["Bruce Wayne", "Human", "M", "Batman"]) 380 | XCTAssertFalse(table.rows[1].hasSeparator) 381 | XCTAssertEqual(table.rows[2].cells, ["Clark Kent", "Kryptonian", "M", "Superman"]) 382 | XCTAssertFalse(table.rows[2].hasSeparator) 383 | XCTAssertEqual(table.rows[3].cells, ["Diana Prince", "Amazonian", "F", "Wonder Woman"]) 384 | XCTAssertFalse(table.rows[2].hasSeparator) 385 | } 386 | 387 | // MARK: Footnote 388 | 389 | func testOnelineFootnote() throws { 390 | let lines = [ 391 | "[fn:1] footnote one.", 392 | "", 393 | "[fn:2] footnote two.", 394 | ] 395 | let doc = parse(lines) 396 | guard let foot1 = doc?.content[0] as? Footnote else { 397 | XCTFail("Expect \(doc?.content[0]) to be Footnote") 398 | return 399 | } 400 | XCTAssertEqual(foot1.label, "1") 401 | guard let para1 = foot1.content[0] as? Paragraph else { 402 | XCTFail("Expect [0][0] to be Paragraph") 403 | return 404 | } 405 | XCTAssertEqual(para1.lines[0], "footnote one.") 406 | } 407 | 408 | 409 | } 410 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/PerformanceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Performance.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 16/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import SwiftOrg 11 | 12 | class PerformanceTests: XCTestCase { 13 | 14 | var content: String = "" 15 | // override func setUp() { 16 | // super.setUp() 17 | // do { 18 | // let path = Bundle(for: type(of: self)).path(forResource: "README", ofType: "org") 19 | // content = try String(contentsOfFile: path!) 20 | // } catch { 21 | // XCTFail("ERROR: \(error)") 22 | // } 23 | // } 24 | 25 | // func testPerformanceParseSmallFile() { 26 | // self.measure { 27 | // do { 28 | // let parser = OrgParser() 29 | // _ = try parser.parse(content: self.content) 30 | // } catch { 31 | // XCTFail("ERROR: \(error)") 32 | // } 33 | // } 34 | // } 35 | } 36 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/Template.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Template.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 15/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class Template: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | // Use XCTAssert and related functions to verify your tests produce the correct results. 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measure { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/TimestampTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimestampTests.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 8/01/17. 6 | // Copyright © 2017 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Foundation 11 | @testable import SwiftOrg 12 | 13 | let calendar = Calendar.current 14 | 15 | let _date = "2007-01-09" 16 | let _day = "Tue" 17 | let _time = "18:00" 18 | let _repeater = "+2w" 19 | 20 | class TimestampTests: XCTestCase { 21 | 22 | func testParseTimestamp() { 23 | let activeTimestamp = "<\(_date) \(_day) \(_time)>" 24 | if let timestamp = Timestamp.from(string: activeTimestamp) { 25 | XCTAssertNotNil(timestamp) 26 | let date = quickDate(date: _date, time: _time) 27 | XCTAssertEqual(date, timestamp.date) 28 | XCTAssertTrue(timestamp.active) 29 | } else { 30 | XCTFail("Failed to parse \(activeTimestamp)") 31 | return 32 | } 33 | 34 | let inactiveTimestamp = "[\(_date) \(_day) \(_time)]" 35 | if let timestamp = Timestamp.from(string: inactiveTimestamp) { 36 | XCTAssertNotNil(timestamp) 37 | let date = quickDate(date: _date, time: _time) 38 | XCTAssertEqual(date, timestamp.date) 39 | XCTAssertFalse(timestamp.active) 40 | } else { 41 | XCTFail("Failed to parse \(inactiveTimestamp)") 42 | return 43 | } 44 | 45 | let timstampWithoutTime = "[\(_date) \(_day)]" 46 | if let timestamp = Timestamp.from(string: timstampWithoutTime) { 47 | XCTAssertNotNil(timestamp) 48 | let date = quickDate(date: _date) 49 | XCTAssertEqual(date, timestamp.date) 50 | XCTAssertFalse(timestamp.active) 51 | } else { 52 | XCTFail("Failed to parse \(timstampWithoutTime)") 53 | return 54 | } 55 | } 56 | 57 | func testTimestampWithSpacing() { 58 | let candidates = [ 59 | "<\(_date) \(_day) \(_time) \(_repeater)>", // normal spacing 60 | "<\(_date) \(_day) \(_time) \(_repeater)>", // extra spaces in between 61 | "< \(_date) \(_day) \(_time) \(_repeater)>", // leading spaces 62 | "<\(_date) \(_day) \(_time) \(_repeater) >", // tail spaces 63 | "< \(_date) \(_day) \(_time) \(_repeater) >", // leading & tail spaces 64 | ] 65 | 66 | for str in candidates { 67 | guard let timestamp = Timestamp.from(string: str) else { 68 | XCTFail("Failed to parse \(str)") 69 | return 70 | } 71 | let date = quickDate(date: _date, time: _time) 72 | XCTAssertEqual(date, timestamp.date) 73 | XCTAssertEqual(_repeater, timestamp.repeater) 74 | } 75 | } 76 | 77 | func testTimestampWithRepeater() { 78 | let repeaters = [ 79 | "+2h", 80 | "+2d", 81 | "+2w", 82 | "+2m", 83 | "+2y", 84 | "++2d", 85 | "-2d", 86 | "--2d", 87 | ".+2d", 88 | ] 89 | 90 | for repeater in repeaters { 91 | let str = "[\(_date) \(_day) \(_time) \(repeater)]" 92 | guard let timestamp = Timestamp.from(string: str) else { 93 | XCTFail("Failed to parse \(str)") 94 | return 95 | } 96 | let date = quickDate(date: _date, time: _time) 97 | XCTAssertEqual(date, timestamp.date) 98 | XCTAssertEqual(repeater, timestamp.repeater) 99 | } 100 | } 101 | 102 | func testTimestampWithInvalidRepeater() { 103 | let repeaters = [ 104 | "+2ah", 105 | "+2week", 106 | "2+2d", 107 | "S+2d", 108 | ] 109 | 110 | for repeater in repeaters { 111 | let str = "[\(_date) \(_day) \(_time) \(repeater)]" 112 | let timestamp = Timestamp.from(string: str) 113 | #if os(Linux) 114 | XCTAssertNil(timestamp?.repeater, "\(str) should be invalid") 115 | #else 116 | XCTAssertNil(timestamp, "\(str) should be invalid") 117 | #endif 118 | } 119 | } 120 | 121 | func testInvalidTimestamp() { 122 | let candidates = [ 123 | "[\(_date) \(_day) \(_time) +2a]", // invalid repeater 124 | ] 125 | 126 | for str in candidates { 127 | let timestamp = Timestamp.from(string: str) 128 | #if os(Linux) 129 | XCTAssertNil(timestamp?.repeater, "\(str) should be invalid") 130 | #else 131 | XCTAssertNil(timestamp, "\(str) should be invalid") 132 | #endif 133 | } 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/TokenExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TokenExtensions.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 15/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import SwiftOrg 10 | 11 | //extension TokenMeta: Equatable { 12 | // public static func ==(lhs: TokenMeta, rhs: TokenMeta) -> Bool { 13 | // return lhs.lineNumber == rhs.lineNumber && lhs.raw == rhs.raw 14 | // } 15 | //} 16 | 17 | extension Token: Equatable { 18 | public static func ==(lhs: Token, rhs: Token) -> Bool { 19 | return "\(lhs)" == "\(rhs)" 20 | // switch (lhs, rhs) { 21 | // case (.blank(let lMeta), .blank(let rMeta)) where lMeta == rMeta: return true 22 | // case (let .setting(lMeta, lKey, lValue), let .setting(rMeta, rKey, rValue)) 23 | // where lMeta == rMeta && lKey == rKey && lValue == rValue: return true 24 | // case (let .headline(lMeta, lLevel, lText), let .header(rMeta, rLevel, rText)) 25 | // where lMeta == rMeta && lLevel == rLevel && lText == rText: return true 26 | // default: return "\(lhs)" == "\(rhs)" 27 | // } 28 | } 29 | } 30 | 31 | extension InlineToken: Equatable { 32 | public static func ==(lhs: InlineToken, rhs: InlineToken) -> Bool { 33 | return "\(lhs)" == "\(rhs)" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/TokenizerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOrgTests.swift 3 | // SwiftOrgTests 4 | // 5 | // Created by Xiaoxing Hu on 14/08/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftOrg 11 | 12 | 13 | class TokenizerTests: XCTestCase { 14 | 15 | override func setUp() { 16 | super.setUp() 17 | defineTokens() 18 | } 19 | 20 | func testTokenBlank() { 21 | evalBlank("", rawIsNil: true) 22 | evalBlank(" ") 23 | evalBlank("\t") 24 | evalBlank(" \t ") 25 | } 26 | 27 | func testTokenSetting() { 28 | evalSetting("#+options: toc:nil", key: "options", value: "toc:nil") 29 | evalSetting("#+options: toc:nil", key: "options", value: "toc:nil") 30 | evalSetting("#+TITLE: hello world", key: "TITLE", value: "hello world") 31 | evalSetting("#+TITLE: ", key: "TITLE", value: nil) 32 | evalSetting("#+TITLE:", key: "TITLE", value: nil) 33 | } 34 | 35 | func testTokenHeading() { 36 | evalHeadline("* Level One", stars: 1, text: "Level One") 37 | evalHeadline("** Level Two", stars: 2, text: "Level Two") 38 | evalHeadline("* TODO Level One with todo", stars: 1, text: "TODO Level One with todo") 39 | evalHeadline("* ", stars: 1, text: nil) 40 | evalLine("*", text: "*") 41 | evalListItem(" * ", indent: 1, text: nil, ordered: false) 42 | } 43 | 44 | func testTokenPlanning() { 45 | let date = "2017-01-09" 46 | let time = "18:00" 47 | let day = "Tue" 48 | 49 | let theDate = quickDate(date: date, time: time) 50 | 51 | evalPlanning("CLOSED: [\(date) \(day) \(time)]", 52 | keyword: "CLOSED", 53 | timestamp: Timestamp(active: false, date: theDate, repeater: nil)) 54 | evalPlanning("SCHEDULED: <\(date) \(day) \(time) +2w>", // with repeater 55 | keyword: "SCHEDULED", 56 | timestamp: Timestamp(active: true, date: theDate, repeater: "+2w")) 57 | evalPlanning("SCHEDULED: <\(date) \(day) \(time) +2w>", // with extra spaces before timestamp 58 | keyword: "SCHEDULED", 59 | timestamp: Timestamp(active: true, date: theDate, repeater: "+2w")) 60 | evalPlanning(" SCHEDULED: <\(date) \(day) \(time) +2w>", // with leading space 61 | keyword: "SCHEDULED", 62 | timestamp: Timestamp(active: true, date: theDate, repeater: "+2w")) 63 | evalPlanning("SCHEDULED: <\(date) \(day) \(time) +2w> ", // with trailing space 64 | keyword: "SCHEDULED", 65 | timestamp: Timestamp(active: true, date: theDate, repeater: "+2w")) 66 | evalPlanning(" SCHEDULED: <\(date) \(day) \(time) +2w> ", // with leading & trailing space 67 | keyword: "SCHEDULED", 68 | timestamp: Timestamp(active: true, date: theDate, repeater: "+2w")) 69 | 70 | // illegal ones are considered normal line 71 | evalLine("closed: <\(date) \(day) \(time)>", // case sensitive 72 | text: "closed: <\(date) \(day) \(time)>") 73 | 74 | evalLine("OPEN: <\(date) \(day) \(time)>", // illegal keyword 75 | text: "OPEN: <\(date) \(day) \(time)>") 76 | } 77 | 78 | func testTokenBlockBegin() { 79 | evalBlockBegin("#+begin_src java", type: "src", params: ["java"]) 80 | evalBlockBegin(" #+begin_src", type: "src", params: nil) 81 | evalBlockBegin(" #+begin_src yaml exports: results :results value html", 82 | type: "src", 83 | params: ["yaml", "exports:", "results", ":results", "value", "html"]) 84 | } 85 | 86 | func testTokenBlockEnd() { 87 | evalBlockEnd("#+END_SRC", type: "SRC") 88 | evalBlockEnd(" #+end_src", type: "src") 89 | } 90 | 91 | func testTokenComment() { 92 | evalComment("# a line of comment", text: "a line of comment") 93 | evalComment("# a line of comment", text: "a line of comment") 94 | evalLine("#not comment", text: "#not comment") 95 | } 96 | 97 | func testTokenHorizontalRule() { 98 | evalHorizontalRule("-----") 99 | evalHorizontalRule("----------") 100 | evalHorizontalRule(" -----") 101 | } 102 | 103 | func testTokenListItem() { 104 | evalListItem("- list item", indent: 0, text: "list item", ordered: false) 105 | evalListItem(" + list item", indent: 1, text: "list item", ordered: false) 106 | evalListItem(" * list item", indent: 2, text: "list item", ordered: false) 107 | evalListItem("1. ordered list item", indent: 0, text: "ordered list item", ordered: true) 108 | evalListItem(" 200) ordered list item", indent: 2, text: "ordered list item", ordered: true) 109 | // checkboxes 110 | evalListItem("- [ ] checkbox", indent: 0, text: "checkbox", ordered: false, checked: false) 111 | evalListItem("- [-] checkbox", indent: 0, text: "checkbox", ordered: false, checked: false) 112 | evalListItem("- [X] checkbox", indent: 0, text: "checkbox", ordered: false, checked: true) 113 | // illegal checkboxes 114 | evalListItem("- [] checkbox", indent: 0, text: "[] checkbox", ordered: false, checked: nil) 115 | evalListItem("- [X]checkbox", indent: 0, text: "[X]checkbox", ordered: false, checked: nil) 116 | evalListItem("- [Y] checkbox", indent: 0, text: "[Y] checkbox", ordered: false, checked: nil) 117 | evalLine("-[X] checkbox", text: "-[X] checkbox") 118 | } 119 | 120 | func testDrawer() { 121 | evalDrawerBegin(":PROPERTY:", name: "PROPERTY") 122 | evalDrawerBegin(" :properties:", name: "properties") 123 | evalDrawerBegin(" :properties: ", name: "properties") 124 | evalDrawerEnd(":END:") 125 | evalDrawerEnd(" :end:") 126 | evalDrawerEnd(" :end: ") 127 | } 128 | 129 | func testFootnote() { 130 | evalFootnote("[fn:1] the footnote", label: "1", content: "the footnote") 131 | evalFootnote("[fn:1] \t the footnote", label: "1", content: "the footnote") 132 | evalFootnote("[fn:999] the footnote", label: "999", content: "the footnote") 133 | evalFootnote("[fn:23]", label: "23", content: nil) 134 | evalFootnote("[fn:23] ", label: "23", content: nil) 135 | evalLine(" [fn:1] the footnote", text: "[fn:1] the footnote") 136 | evalLine("a[fn:1] the footnote", text: "a[fn:1] the footnote") 137 | evalLine("[fn:1]the footnote", text: "[fn:1]the footnote") 138 | } 139 | 140 | func testTable() { 141 | // valid table rows 142 | evalTableRow("| hello | world | y'all |", cells: ["hello", "world", "y'all"]) 143 | evalTableRow(" | hello | world | y'all |", cells: ["hello", "world", "y'all"]) 144 | evalTableRow("| hello | world |y'all |", cells: ["hello", "world", "y'all"]) 145 | evalTableRow("| hello | world | y'all", cells: ["hello", "world", "y'all"]) 146 | evalTableRow("|+", cells: ["+"]) 147 | 148 | // invalid table rows 149 | evalLine(" hello | world | y'all |", text: "hello | world | y'all |") 150 | 151 | // horizontal separator 152 | evalHorizontalSeparator("|----+---+----|") 153 | evalHorizontalSeparator("|---=+---+----|") 154 | evalHorizontalSeparator(" |----+---+----|") 155 | evalHorizontalSeparator("|----+---+---") 156 | evalHorizontalSeparator("|-") 157 | evalHorizontalSeparator("|---") 158 | 159 | // invalud horizontal separator 160 | evalLine("----+---+----|", text: "----+---+----|") 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // SwiftOrg 4 | // 5 | // Created by Xiaoxing Hu on 17/09/16. 6 | // Copyright © 2016 Xiaoxing Hu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Foundation 11 | @testable import SwiftOrg 12 | 13 | let parser = OrgParser() 14 | 15 | func parse(_ lines: [String], with parser: OrgParser = parser) -> OrgDocument? { 16 | do { 17 | return try parser.parse(lines: lines) 18 | } catch { 19 | XCTFail("> ERROR: \(error).") 20 | } 21 | return nil 22 | } 23 | 24 | func quickDate(date: String, time: String? = nil) -> Date { 25 | let formater = DateFormatter() 26 | formater.dateFormat = "yyyy-MM-dd" 27 | var dt = date 28 | if let t = time { 29 | dt = "\(date) \(t)" 30 | formater.dateFormat = "yyyy-MM-dd HH:mm" 31 | } 32 | return formater.date(from: dt)! 33 | } 34 | -------------------------------------------------------------------------------- /Tests/SwiftOrgTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | extension TokenizerTests { 2 | static var allTests = [ 3 | ("testTokenBlank", testTokenBlank), 4 | ("testTokenSetting", testTokenSetting), 5 | ("testTokenHeading", testTokenHeading), 6 | ("testTokenPlanning", testTokenPlanning), 7 | ("testTokenBlockBegin", testTokenBlockBegin), 8 | ("testTokenBlockEnd", testTokenBlockEnd), 9 | ("testTokenComment", testTokenComment), 10 | ("testTokenHorizontalRule", testTokenHorizontalRule), 11 | ("testTokenListItem", testTokenListItem), 12 | ("testDrawer", testDrawer), 13 | ("testFootnote", testFootnote), 14 | ("testTable", testTable), 15 | ] 16 | } 17 | 18 | // extension LexerTests { 19 | // static var allTests = [] 20 | // } 21 | 22 | extension ParserTests { 23 | static var allTests = [ 24 | ("testParseSettings", testParseSettings), 25 | ("testDefaultTodos", testDefaultTodos), 26 | ("testInBufferTodos", testInBufferTodos), 27 | ("testParseHeadline", testParseHeadline), 28 | ("testParseDrawer", testParseDrawer), 29 | ("testMalfunctionDrawer", testMalfunctionDrawer), 30 | ("testPriority", testPriority), 31 | ("testTags", testTags), 32 | ("testPlanning", testPlanning), 33 | ("testParseBlock", testParseBlock), 34 | ("testParseList", testParseList), 35 | ("testListItemWithCheckbox", testListItemWithCheckbox), 36 | ("testParseParagraph", testParseParagraph), 37 | ("testParseTable", testParseTable), 38 | ("testOnelineFootnote", testOnelineFootnote), 39 | ] 40 | } 41 | 42 | extension IndexingTests { 43 | static var allTests = [ 44 | ("testIndexing", testIndexing), 45 | ("testSectionIndexing", testSectionIndexing), 46 | ] 47 | } 48 | 49 | extension InlineParsingTests { 50 | static var allTests = [ 51 | ("testInlineParsing", testInlineParsing), 52 | ("testCornerCases", testCornerCases), 53 | ("testInlineFootnote", testInlineFootnote), 54 | ] 55 | } 56 | 57 | extension TimestampTests { 58 | static var allTests = [ 59 | ("testParseTimestamp", testParseTimestamp), 60 | ("testTimestampWithSpacing", testTimestampWithSpacing), 61 | ("testTimestampWithRepeater", testTimestampWithRepeater), 62 | ("testTimestampWithInvalidRepeater", testTimestampWithInvalidRepeater), 63 | ("testInvalidTimestamp", testInvalidTimestamp), 64 | ] 65 | } 66 | 67 | extension ConvertToJSONTests { 68 | static var allTests = [ 69 | ("testJSONConvertion", testJSONConvertion), 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /scripts/publish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ~/.rvm/scripts/rvm 4 | rvm use default 5 | pod trunk push 6 | -------------------------------------------------------------------------------- /scripts/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if ! command -v carthage > /dev/null; then 4 | printf 'Carthage is not installed.\n' 5 | printf 'See https://github.com/Carthage/Carthage for install instructions.\n' 6 | exit 1 7 | fi 8 | 9 | carthage update --platform iOS --use-submodules 10 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_CMD="xcodebuild -scheme SwiftOrg-iOS -project SwiftOrg.xcodeproj -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=10.2' build test" 4 | 5 | which -s xcpretty 6 | XCPRETTY_INSTALLED=$? 7 | 8 | if [[ $TRAVIS || $XCPRETTY_INSTALLED == 0 ]]; then 9 | eval "${TEST_CMD} | xcpretty" 10 | else 11 | eval "$TEST_CMD" 12 | fi 13 | --------------------------------------------------------------------------------