Browse Source

Fix hashtag parsing

Changelog-Fixed: No longer parse hashtags in urls
Signed-off-by: William Casarin <jb55@jb55.com>
profiles-everywhere
William Casarin 2 years ago
parent
commit
e6db7369cd
  1. 20
      damus/Models/Mentions.swift
  2. 24
      damus/Models/PostBlock.swift
  3. 72
      damusTests/ReplyTests.swift

20
damus/Models/Mentions.swift

@ -132,6 +132,19 @@ func is_hashtag_char(_ c: Character) -> Bool {
return c.isLetter || c.isNumber return c.isLetter || c.isNumber
} }
func prev_char(_ p: Parser, n: Int) -> Character? {
if p.pos - n < 0 {
return nil
}
let ind = p.str.index(p.str.startIndex, offsetBy: p.pos - n)
return p.str[ind]
}
func is_punctuation(_ c: Character) -> Bool {
return c.isWhitespace || c.isPunctuation
}
func parse_hashtag(_ p: Parser) -> String? { func parse_hashtag(_ p: Parser) -> String? {
let start = p.pos let start = p.pos
@ -139,6 +152,13 @@ func parse_hashtag(_ p: Parser) -> String? {
return nil return nil
} }
if let prev = prev_char(p, n: 2) {
// we don't allow adjacent hashtags
if !is_punctuation(prev) {
return nil
}
}
guard let str = parse_while(p, match: is_hashtag_char) else { guard let str = parse_while(p, match: is_hashtag_char) else {
p.pos = start p.pos = start
return nil return nil

24
damus/Models/PostBlock.swift

@ -12,25 +12,25 @@ enum PostBlock {
case ref(ReferencedId) case ref(ReferencedId)
case hashtag(String) case hashtag(String)
var is_text: Bool { var is_text: String? {
if case .text = self { if case .text(let txt) = self {
return true return txt
} }
return false return nil
} }
var is_hashtag: Bool { var is_hashtag: String? {
if case .hashtag = self { if case .hashtag(let ht) = self {
return true return ht
} }
return false return nil
} }
var is_ref: Bool { var is_ref: ReferencedId? {
if case .ref = self { if case .ref(let ref) = self {
return true return ref
} }
return false return nil
} }
} }

72
damusTests/ReplyTests.swift

@ -35,6 +35,46 @@ class ReplyTests: XCTestCase {
XCTAssertEqual(ref.is_mention!.ref.ref_id, "event_id") XCTAssertEqual(ref.is_mention!.ref.ref_id, "event_id")
} }
func testUrlAnchorsAreNotHashtags() {
let content = "this is my link: https://jb55.com/index.html#buybitcoin this is not a hashtag!"
let blocks = parse_post_blocks(content: content)
XCTAssertEqual(blocks.count, 1)
XCTAssertEqual(blocks[0].is_text != nil, true)
}
func testHashtagsInQuote() {
let content = "This is my \"#awesome post\""
let blocks = parse_post_blocks(content: content)
XCTAssertEqual(blocks.count, 3)
XCTAssertEqual(blocks[0].is_text, "This is my \"")
XCTAssertEqual(blocks[1].is_hashtag, "awesome")
XCTAssertEqual(blocks[2].is_text, " post\"")
}
func testHashtagAtStartWorks() {
let content = "#hashtag"
let blocks = parse_post_blocks(content: content)
XCTAssertEqual(blocks.count, 3)
XCTAssertEqual(blocks[1].is_hashtag, "hashtag")
}
func testGroupOfHashtags() {
let content = "#hashtag#what#nope"
let blocks = parse_post_blocks(content: content)
XCTAssertEqual(blocks.count, 3)
XCTAssertEqual(blocks[1].is_hashtag, "hashtag")
XCTAssertEqual(blocks[2].is_text, "#what#nope")
switch blocks[1] {
case .hashtag(let htag):
XCTAssertEqual(htag, "hashtag")
default:
break
}
}
func testRootReplyWithMention() throws { func testRootReplyWithMention() throws {
let content = "this is #[1] a mention" let content = "this is #[1] a mention"
let tags = [["e", "thread_id"], ["e", "mentioned_id"]] let tags = [["e", "thread_id"], ["e", "mentioned_id"]]
@ -83,7 +123,7 @@ class ReplyTests: XCTestCase {
//let tags: [[String]] = [] //let tags: [[String]] = []
let blocks = parse_post_blocks(content: content) let blocks = parse_post_blocks(content: content)
let mentions = blocks.filter { $0.is_ref } let mentions = blocks.filter { $0.is_ref != nil }
XCTAssertEqual(mentions.count, 10) XCTAssertEqual(mentions.count, 10)
} }
@ -221,9 +261,9 @@ class ReplyTests: XCTestCase {
XCTAssertNotNil(parsed) XCTAssertNotNil(parsed)
XCTAssertEqual(parsed.count, 3) XCTAssertEqual(parsed.count, 3)
XCTAssertTrue(parsed[0].is_text) XCTAssertEqual(parsed[0].is_text, "this is a nostr:")
XCTAssertTrue(parsed[1].is_ref) XCTAssertTrue(parsed[1].is_ref != nil)
XCTAssertTrue(parsed[2].is_text) XCTAssertEqual(parsed[2].is_text, ":\(id) event mention")
guard case .ref(let ref) = parsed[1] else { guard case .ref(let ref) = parsed[1] else {
XCTAssertTrue(false) XCTAssertTrue(false)
@ -268,9 +308,9 @@ class ReplyTests: XCTestCase {
XCTAssertNotNil(parsed) XCTAssertNotNil(parsed)
XCTAssertEqual(parsed.count, 3) XCTAssertEqual(parsed.count, 3)
XCTAssertTrue(parsed[0].is_text) XCTAssertEqual(parsed[0].is_text, "this is a ")
XCTAssertTrue(parsed[1].is_ref) XCTAssertNotNil(parsed[1].is_ref)
XCTAssertTrue(parsed[2].is_text) XCTAssertEqual(parsed[2].is_text, " event mention")
guard case .ref(let ref) = parsed[1] else { guard case .ref(let ref) = parsed[1] else {
XCTAssertTrue(false) XCTAssertTrue(false)
@ -299,9 +339,9 @@ class ReplyTests: XCTestCase {
XCTAssertNotNil(parsed) XCTAssertNotNil(parsed)
XCTAssertEqual(parsed.count, 3) XCTAssertEqual(parsed.count, 3)
XCTAssertTrue(parsed[0].is_text) XCTAssertEqual(parsed[0].is_text, "this is a ")
XCTAssertTrue(parsed[1].is_ref) XCTAssertNotNil(parsed[1].is_ref)
XCTAssertTrue(parsed[2].is_text) XCTAssertEqual(parsed[2].is_text, " event mention")
guard case .ref(let ref) = parsed[1] else { guard case .ref(let ref) = parsed[1] else {
XCTAssertTrue(false) XCTAssertTrue(false)
@ -330,9 +370,9 @@ class ReplyTests: XCTestCase {
XCTAssertNotNil(parsed) XCTAssertNotNil(parsed)
XCTAssertEqual(parsed.count, 3) XCTAssertEqual(parsed.count, 3)
XCTAssertTrue(parsed[0].is_text) XCTAssertEqual(parsed[0].is_text, "this is a ")
XCTAssertTrue(parsed[1].is_ref) XCTAssertNotNil(parsed[1].is_ref)
XCTAssertTrue(parsed[2].is_text) XCTAssertEqual(parsed[2].is_text, " event mention")
guard case .ref(let ref) = parsed[1] else { guard case .ref(let ref) = parsed[1] else {
XCTAssertTrue(false) XCTAssertTrue(false)
@ -361,9 +401,9 @@ class ReplyTests: XCTestCase {
XCTAssertNotNil(parsed) XCTAssertNotNil(parsed)
XCTAssertEqual(parsed.count, 3) XCTAssertEqual(parsed.count, 3)
XCTAssertTrue(parsed[0].is_text) XCTAssertEqual(parsed[0].is_text, "this is a ")
XCTAssertTrue(parsed[1].is_ref) XCTAssertNotNil(parsed[1].is_ref)
XCTAssertTrue(parsed[2].is_text) XCTAssertEqual(parsed[2].is_text, " mention")
guard case .ref(let ref) = parsed[1] else { guard case .ref(let ref) = parsed[1] else {
XCTAssertTrue(false) XCTAssertTrue(false)

Loading…
Cancel
Save