Skip to content

Commit

Permalink
Rework wrong usage of String.Index. Fixes #77
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel Sharanda committed Mar 28, 2019
1 parent a1864cb commit eaf3af6
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 22 deletions.
2 changes: 1 addition & 1 deletion Atributika.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Atributika"
s.version = "4.7.1"
s.version = "4.7.2"
s.summary = "Convert text with HTML tags, hashtags, mentions, links into NSAttributedString. Make them clickable with UILabel drop-in replacement."
s.description = <<-DESC
`Atributika` is an easy and painless way to build NSAttributedString. It is able to detect HTML-like tags, links, phone numbers, hashtags, any regex or even standard ios data detectors and style them with various attributes like font, color, etc. `Atributika` comes with drop-in label replacement `AttributedLabel` which is able to make any detection clickable.
Expand Down
4 changes: 3 additions & 1 deletion Demo/AttributedLabelDemoViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ class AttributedLabelDemoViewController: UIViewController {
"RT or tweet #camilahammersledge for a follow 👽",
"Denny JA: Dengan RT ini, anda ikut memenangkan Jokowi-JK. Pilih pemimpin yg bisa dipercaya (Jokowi) dan pengalaman (JK). #DJoJK",
"Always in my heart @Harry_Styles . Yours sincerely, Louis",
"HELP ME PLEASE. A MAN NEEDS HIS NUGGS https://pbs.twimg.com/media/C8sk8QlUwAAR3qI.jpg"
"HELP ME PLEASE. A MAN NEEDS HIS NUGGS https://pbs.twimg.com/media/C8sk8QlUwAAR3qI.jpg",
"Подтверждая номер телефона, вы\nпринимаете «<a>пользовательское соглашение</a>»",
"Here's how a similar one was solved 😄 \nhttps://medium.com/@narcelio/solving-decred-mockingbird-puzzle-5366efeaeed7\n"
]

init() {
Expand Down
2 changes: 1 addition & 1 deletion Sources/AttributedLabel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ open class AttributedLabel: UIView {

if let detection = state.detection {
let higlightedAttributedString = NSMutableAttributedString(attributedString: string)
higlightedAttributedString.addAttributes(detection.style.highlightedAttributes, range: NSRange(detection.range, in: string.string))
higlightedAttributedString.addAttributes(detection.style.highlightedAttributes, range: NSRange(detection.range, in: text.string))
label.attributedText = higlightedAttributedString
} else {
if state.isEnabled {
Expand Down
45 changes: 26 additions & 19 deletions Sources/String+Detection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,28 @@ extension String {

public func detectTags(transformers: [TagTransformer] = []) -> (string: String, tagsInfo: [TagInfo]) {

struct TagInfoInternal {
public let tag: Tag
public let rangeStart: Int
public let rangeEnd: Int
public let level: Int
}

let scanner = Scanner(string: self)
scanner.charactersToBeSkipped = nil
var resultString = String()
var tagsResult = [TagInfo]()
var tagsStack = [(Tag, String.Index, Int)]()
var tagsResult = [TagInfoInternal]()
var tagsStack = [(Tag, Int, Int)]()

while !scanner.isAtEnd {

if let textString = scanner.scanUpToCharacters(from: CharacterSet(charactersIn: "<&")) {
resultString += textString
resultString.append(textString)
} else {
if scanner.scanString("<") != nil {

if scanner.isAtEnd {
resultString += "<"
resultString.append("<")
} else {
let nextChar = (scanner.string as NSString).substring(with: NSRange(location: scanner.scanLocation, length: 1))
if CharacterSet.letters.contains(nextChar.unicodeScalars.first!) || (nextChar == "/") {
Expand All @@ -115,64 +122,64 @@ extension String {
if scanner.scanString(">") != nil {
if let tag = parseTag(tagString, parseAttributes: tagType == .start ) {

let resultTextEndIndex = resultString.endIndex
let resultTextEndIndex = resultString.count

if let transformer = transformers.first(where: {
$0.tagName.lowercased() == tag.name.lowercased() && $0.tagType == tagType
}) {
resultString += transformer.transform(tag)
resultString.append(transformer.transform(tag))
}

if tagType == .start {
tagsStack.append((tag, resultTextEndIndex, (tagsStack.last?.2 ?? -1) + 1))
} else {
for (index, (tagInStack, startIndex, level)) in tagsStack.enumerated().reversed() {
if tagInStack.name.lowercased() == tag.name.lowercased() {
tagsResult.append(TagInfo(tag: tagInStack, range: startIndex..<resultTextEndIndex, level: level))
tagsResult.append(TagInfoInternal(tag: tagInStack, rangeStart: startIndex, rangeEnd: resultTextEndIndex, level: level))
tagsStack.remove(at: index)
break
}
}
}
}
} else {
resultString += "<"
resultString += tagString
resultString.append("<")
resultString.append(tagString)
}
}
} else {
resultString += "<"
resultString.append("<")
}
}
} else if scanner.scanString("&") != nil {
if scanner.scanString("#") != nil {
if let potentialSpecial = scanner.scanCharacters(from: CharacterSet.alphanumerics) {
if scanner.scanString(";") != nil {
resultString += potentialSpecial.unescapeAsNumber() ?? "&#\(potentialSpecial);"
resultString.append(potentialSpecial.unescapeAsNumber() ?? "&#\(potentialSpecial);")
} else {
resultString += "&#"
resultString += potentialSpecial
resultString.append("&#")
resultString.append(potentialSpecial)
}
} else {
resultString += "&#"
resultString.append("&#")
}
} else {
if let potentialSpecial = scanner.scanCharacters(from: CharacterSet.letters) {
if scanner.scanString(";") != nil {
resultString += HTMLSpecial(for: potentialSpecial) ?? "&\(potentialSpecial);"
resultString.append(HTMLSpecial(for: potentialSpecial) ?? "&\(potentialSpecial);")
} else {
resultString += "&"
resultString += potentialSpecial
resultString.append("&")
resultString.append(potentialSpecial)
}
} else {
resultString += "&"
resultString.append("&")
}
}
}
}
}

return (resultString, tagsResult)
return (resultString, tagsResult.map { TagInfo(tag: $0.tag, range: resultString.index(resultString.startIndex, offsetBy: $0.rangeStart)..<resultString.index(resultString.startIndex, offsetBy: $0.rangeEnd), level: $0.level) })
}

public func detectHashTags() -> [Range<String.Index>] {
Expand Down

0 comments on commit eaf3af6

Please sign in to comment.