swiftgemtext

[ACTIVE] gemtext parsing in swift
git clone git://git.figbert.com/swiftgemtext.git
Log | Files | Refs | README

SwiftGemtext.swift (3227B)


      1 import SwiftUI
      2 
      3 public struct Gemtext {
      4     public init() {}
      5 
      6     public func parse(_ source: String) -> [LineType] {
      7         return parse(source, url: nil)
      8     }
      9 
     10     public func parse(_ source: String, url: URL?) -> [LineType] {
     11         var lines = [LineType]()
     12         var pre = PreformattedState()
     13         
     14         for line in source.lines() {
     15             if pre.active {
     16                 if line.starts(with: "```") {
     17                     lines.append(pre.getLineType())
     18                     pre = PreformattedState()
     19                     continue
     20                 }
     21 
     22                 pre.lines.append(line)
     23             } else {
     24                 if line.starts(with: "```") {
     25                     let alt = line.dropFirst(3).trimmingCharacters(in: .whitespacesAndNewlines)
     26                     if alt.count > 0 { pre.alt = alt }
     27                     pre.active = true
     28                     continue
     29                 }
     30 
     31                 lines.append(parsePlainLine(line, relativeTo: url))
     32             }
     33         }
     34         return lines
     35     }
     36     
     37     func parsePlainLine(_ line: String, relativeTo rel: URL?) -> LineType {
     38         if line.starts(with: "#") { // Heading Line
     39             var level: Int = 1
     40             if line.starts(with: "###") {
     41                 level = 3
     42             } else if line.starts(with: "##")  {
     43                 level = 2
     44             }
     45             return LineType.Heading(level, line.dropFirst(level).trimmingCharacters(in: .whitespacesAndNewlines))
     46         } else if line.starts(with: "=>") { // Link Line
     47             let base = line.dropFirst(2).trimmingCharacters(in: .whitespacesAndNewlines)
     48             let url = base.components(separatedBy: .whitespaces).first!
     49             let caption = base.dropFirst(url.count).trimmingCharacters(in: .whitespaces)
     50 
     51             return LineType.Link(
     52                 URL(string: url.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed)!, relativeTo: rel)!,
     53                 caption.isEmpty ? nil : caption
     54             )
     55         } else if line.starts(with: "* ") { // Unordered List Line
     56             return LineType.UnorderedList(line.dropFirst(2).trimmingCharacters(in: .whitespacesAndNewlines))
     57         } else if line.starts(with: ">") { // Quote Line
     58             return LineType.Quote(line.dropFirst().trimmingCharacters(in: .whitespacesAndNewlines))
     59         } else if line.trimmingCharacters(in: .whitespacesAndNewlines) == "" { // Empty Line
     60             return LineType.EmptyLine
     61         } else { // Text Line
     62             return LineType.Text(line)
     63         }
     64     }
     65 }
     66 
     67 struct PreformattedState {
     68     var lines = [String]()
     69     var active = false
     70     var alt: String?
     71     
     72     func getLineType() -> LineType {
     73         let str = self.lines.joined(separator: "\r\n")
     74         return LineType.PreformattedText(str, alt)
     75     }
     76 }
     77 
     78 public enum LineType: Hashable {
     79     case Text(String)
     80     case Link(URL, String?)
     81     case PreformattedText(String, String?)
     82     case Heading(Int, String)
     83     case UnorderedList(String)
     84     case Quote(String)
     85     case EmptyLine
     86 }
     87 
     88 extension StringProtocol {
     89     func lines() -> [String] {
     90         var lines = [String]()
     91         self.enumerateLines { line, _ in
     92             lines.append(line)
     93         }
     94         return lines
     95     }
     96 }