[go: up one dir, main page]

Skip to content

Commit

Permalink
Support Links and inline code styles
Browse files Browse the repository at this point in the history
  • Loading branch information
rlaguilar committed Jul 26, 2018
1 parent 7f71588 commit 1a4b220
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 29 deletions.
5 changes: 5 additions & 0 deletions Example/Markdowner/PreviewViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@ class PreviewViewController: UIViewController {

// Do any additional setup after loading the view.
textView.attributedText = markdownContent
if #available(iOS 10.0, *) {
textView.adjustsFontForContentSizeCategory = true
} else {
// Fallback on earlier versions
}
}
}
18 changes: 1 addition & 17 deletions Example/Markdowner/sample.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,6 @@ Lists

[I'm an inline-style link](https://www.google.com)

## Code
## Inline Code

Inline `code` has `back-ticks around` it.

You can also use block of codes

```
No language indicated, so no syntax highlighting.
But let's throw in a <b>tag</b>.
```

## Quotes

> Blockquotes are very handy in email to emulate reply text.
> This line is part of the same quote.
Quote break.

> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
8 changes: 8 additions & 0 deletions Example/Pods/Pods.xcodeproj/project.pbxproj

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 63 additions & 0 deletions Markdowner/Classes/Default Elements/InlineCodeElement.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// InlineCodeElement.swift
// Markdowner
//
// Created by Reynaldo Aguilar on 7/26/18.
//

import Foundation

open class InlineCodeElement: MarkdownElement {
let symbolsColor: UIColor
let font: UIFont

public init(symbolsColor: UIColor, font: UIFont) {
self.symbolsColor = symbolsColor
self.font = font

let pattern = "(?<!`)`(?![ `]).*?(?<![ `])`(?!`)"

guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
fatalError()
}

super.init(regex: regex)
}

open override func styles(forMatch match: String) -> [MarkdownElement.Style] {
let fontStyle = Style(
attributeKey: .font,
value: font.dynamic(),
startIndex: 0,
length: match.count
)

let indicatorRanges = [
NSRange(location: 0, length: 1),
NSRange(location: match.count - 1, length: 1)
]

let foregroundStyles = indicatorRanges.map {
Style(
attributeKey: .foregroundColor,
value: symbolsColor,
startIndex: $0.location,
length: $0.length
)
}

return foregroundStyles + [fontStyle]
}

open override func applying(stylesConfiguration: StylesConfiguration) -> InlineCodeElement {
return InlineCodeElement(
symbolsColor: stylesConfiguration.symbolsColor,
font: font
)
}

open override func replacementRanges(forMatch match: String) -> [ReplacementRange] {
let ranges = [NSRange(location: 0, length: 1), NSRange(location: match.count - 1, length: 1)]
return ranges.map { ReplacementRange(range: $0, replacementValue: NSAttributedString()) }
}
}
105 changes: 105 additions & 0 deletions Markdowner/Classes/Default Elements/LinkElement.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//
// LinkElement.swift
// Markdowner
//
// Created by Reynaldo Aguilar on 7/24/18.
//

import Foundation

open class LinkElement: MarkdownElement {
let symbolsColor: UIColor
let textFont: UIFont
let linksColor: UIColor

init(symbolsColor: UIColor, font: UIFont, linksColor: UIColor) {
self.symbolsColor = symbolsColor
self.textFont = font
self.linksColor = linksColor

// the url regex was taken from: https://stackoverflow.com/a/3809435/3385517
// specifically from the link http://regexr.com/3e6m0 inside that question
let urlRegexPattern = "(http(s)?:\\/\\/.)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"

let linkPattern = "(?<!\\[)(\\[).+(\\])(\\()\(urlRegexPattern)(\\))(?!\\))"

guard let regex = try? NSRegularExpression(pattern: linkPattern, options: []) else {
fatalError()
}

super.init(regex: regex)
}

open override func styles(forMatch match: String) -> [MarkdownElement.Style] {
let ranges = symbolRanges(forMatch: match)

let symbolsColor = ranges.map { range in
Style(
attributeKey: .foregroundColor,
value: self.symbolsColor,
startIndex: range.location,
length: range.length
)
}

let textRange = NSRange(
location: ranges[0].location + 1,
length: ranges[1].location - ranges[0].location
)

let urlRange = NSRange(
location: ranges[2].location + 1,
length: ranges[3].location - ranges[2].location
)

let textStyles = [
MarkdownElement.Style(
attributeKey: .link,
value: (match as NSString).substring(with: urlRange),
startIndex: textRange.location,
length: textRange.length
),
MarkdownElement.Style(
attributeKey: .foregroundColor,
value: linksColor,
startIndex: textRange.location,
length: textRange.length
)
]

return symbolsColor + textStyles
}

open override func replacementRanges(forMatch match: String) -> [ReplacementRange] {
let symbolRanges = self.symbolRanges(forMatch: match)

let urlPortionRange = NSRange(
location: symbolRanges[2].location,
length: match.count - symbolRanges[2].location
)

return [symbolRanges[0], symbolRanges[1], urlPortionRange].map {
ReplacementRange(range: $0, replacementValue: NSAttributedString())
}
}

open override func applying(stylesConfiguration: StylesConfiguration) -> LinkElement {
return LinkElement(
symbolsColor: stylesConfiguration.symbolsColor,
font: stylesConfiguration.baseFont,
linksColor: linksColor
)
}

private func symbolRanges(forMatch match: String) -> [NSRange] {
let fullRange = NSRange(location: 0, length: match.count)

guard let regexMatch = regex.matches(in: match, options: [], range: fullRange).first else {
fatalError("ERROR: Unable to find a match for the given ouput")
}

let lastRange = regexMatch.range(at: regexMatch.numberOfRanges - 1)
let ranges = (1...3).map { regexMatch.range(at: $0) } + [lastRange]
return ranges
}
}
36 changes: 24 additions & 12 deletions Markdowner/Classes/MarkdownTextStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ public class MarkdownTextStorage: NSTextStorage {
let boldElement = BoldElement(symbolsColor: stylesConfiguration.symbolsColor)
let italicElement = ItalicElement(symbolsColor: stylesConfiguration.symbolsColor)
let strikeElement = StrikethroughElement(symbolsColor: stylesConfiguration.symbolsColor)

guard let monospaceFont = UIFont(name: "Menlo-Regular", size: stylesConfiguration.baseFont.pointSize) else {
fatalError()
}

let inlineCodeElement = InlineCodeElement(
symbolsColor: stylesConfiguration.symbolsColor,
font: monospaceFont
)

let linkElement = LinkElement(
symbolsColor: stylesConfiguration.symbolsColor,
font: stylesConfiguration.baseFont,
linksColor: UIColor.lightGray
)

let bulletElement = BulletElement(
symbolsColor: stylesConfiguration.symbolsColor,
textColor: stylesConfiguration.textColor,
Expand All @@ -40,7 +56,8 @@ public class MarkdownTextStorage: NSTextStorage {
fontProvider: DefaultHeaderElementFontProvider(font: stylesConfiguration.baseFont)
)

return [boldElement, italicElement, strikeElement, bulletElement, headerElement]
return [boldElement, italicElement, strikeElement, inlineCodeElement,
linkElement, bulletElement, headerElement]
}

public override init() {
Expand Down Expand Up @@ -74,15 +91,10 @@ public class MarkdownTextStorage: NSTextStorage {
let originalString = NSMutableAttributedString(attributedString: self)

for replacementRange in rangesToRemove.reversed() {
if replacementRange.replacementValue.string.isEmpty {
originalString.deleteCharacters(in: replacementRange.range)
}
else {
originalString.replaceCharacters(
in: replacementRange.range,
with: replacementRange.replacementValue
)
}
originalString.replaceCharacters(
in: replacementRange.range,
with: replacementRange.replacementValue
)
}

return originalString
Expand Down Expand Up @@ -111,10 +123,10 @@ public class MarkdownTextStorage: NSTextStorage {
override public func processEditing() {
let paragraphRange = (string as NSString).paragraphRange(for: editedRange)

let styles = markdownParser.styles(forString: string, atRange: paragraphRange)

self.setAttributes(defaultAttributes, range: paragraphRange)

let styles = markdownParser.styles(forString: string, atRange: paragraphRange)

for style in styles {
if style.attributeKey == .fontTraits {
guard let fontTraits = style.value as? UIFontDescriptorSymbolicTraits else {
Expand Down

0 comments on commit 1a4b220

Please sign in to comment.