Skip to content

Commit

Permalink
Use simple regex to convert status note into html instead markdown pa…
Browse files Browse the repository at this point in the history
…rser
  • Loading branch information
mczachurski committed Sep 24, 2024
1 parent f7fe661 commit d7fb021
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 4 deletions.
41 changes: 41 additions & 0 deletions Sources/VernissageServer/Extensions/String+HTML.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,26 @@
// Licensed under the Apache License 2.0.
//

import Foundation
import RegexBuilder
import Ink

extension String {
public func html(baseAddress: String) -> String {
var lines = self.split(separator: "\n", omittingEmptySubsequences: false).map({ String($0) })

for (index, line) in lines.enumerated() {
lines[index] = line
.convertUrlsIntoHtml()
.convertUsernamesIntoHtml(baseAddress: baseAddress)
.convertTagsIntoHtml(baseAddress: baseAddress)
}

let converted = lines.joined(separator: "<br />")
return "<p>\(converted)</p>"
}

public func markdownHtml(baseAddress: String) -> String {
var lines = self.split(separator: "\n").map({ String($0) })

for (index, line) in lines.enumerated() {
Expand Down Expand Up @@ -36,6 +51,24 @@ extension String {
return "\(match.prefix)[\(match.username)\(match.domain)](\(domain)/\(match.username))"
}
}

private func convertTagsIntoHtml(baseAddress: String) -> String {
let hashtagPattern = #/(?<prefix>^|[ \/\\+\-=!<>,\.:;*"'{}]{1})(?<tag>#[a-zA-Z0-9_]{1,})/#
return self.replacing(hashtagPattern) { match in
// "\(match.prefix)[\(match.tag)](\(baseAddress)/tags/\(match.tag.replacingOccurrences(of: "#", with: "")))"
"\(match.prefix)<a href=\"\(baseAddress)/tags/\(match.tag.replacingOccurrences(of: "#", with: ""))\" rel=\"tag\" class=\"mention hashtag\">\(match.tag)</a>"
}
}

private func convertUsernamesIntoHtml(baseAddress: String) -> String {
let usernamePattern = #/(?<prefix>^|[ +\-=!<>,\.:;*"'{}]{1})(?<username>@[a-zA-Z0-9(_)]{1,})(?<domain>[@]{1}[a-zA-Z0-9_\-\.]{0,}){0,}/#
return self.replacing(usernamePattern) { match in
let matchedDomain = match.domain ?? ""
let domain = matchedDomain.isEmpty ? baseAddress : "https://\(String(matchedDomain).deletingPrefix("@"))"

return "\(match.prefix)<a href=\"\(domain)/\(match.username)\" class=\"username\">\(match.username)\(matchedDomain)</a>"
}
}

private func convertUrlsIntoHtml() -> String {
let urlPattern = #/(?<prefix>^|[ +\-=!<>,\.:;*"'{}]{1})(?<address>https?:\/\/\S*)/#
Expand All @@ -49,4 +82,12 @@ extension String {
let parser = MarkdownParser()
return parser.html(from: self)
}

private func isCorrectUrl(domain: String) -> Bool {
if let url = URL(string: domain), url.host != nil {
return true
}

return false
}
}
63 changes: 59 additions & 4 deletions Tests/VernissageServerTests/ExtensionsTests/String+Html.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct StringHtmlTests {
// Assert.
let expectedHtml =
"""
<p><a href="https://vernissage.com/@marcin">@marcin</a> OK</p>
<p><a href="https://vernissage.com/@marcin" class="username">@marcin</a> OK</p>
"""
#expect(html == expectedHtml)
}
Expand All @@ -39,7 +39,7 @@ struct StringHtmlTests {
// Assert.
let expectedHtml =
"""
<p><a href="https://other.uk/@marcin">@[email protected]</a> OK</p>
<p><a href="https://other.uk/@marcin" class="username">@[email protected]</a> OK</p>
"""
#expect(html == expectedHtml)
}
Expand Down Expand Up @@ -73,7 +73,45 @@ struct StringHtmlTests {
// Assert.
let expectedHtml =
"""
<p>This is <a href="https://vernissage.com/tags/hashtag">#hashtag</a> OK</p>
<p>This is <a href="https://vernissage.com/tags/hashtag" rel="tag" class="mention hashtag">#hashtag</a> OK</p>
"""
#expect(html == expectedHtml)
}

@Test("Rendering single hashtag without prefix text")
func renderingSingleHashtagWithoutPrefixText() async throws {

// Arrange.
let text = "#hashtag OK"

// Act.
let html = text.html(baseAddress: "https://vernissage.com")

// Assert.
let expectedHtml =
"""
<p><a href="https://vernissage.com/tags/hashtag" rel="tag" class="mention hashtag">#hashtag</a> OK</p>
"""
#expect(html == expectedHtml)
}

@Test("Rendering with nee lines")
func renderingWithNewLines() async throws {

// Arrange.
let text = """
This status for @wify.
#street #photo #blackAndWwhite
"""

// Act.
let html = text.html(baseAddress: "https://vernissage.com")

// Assert.
let expectedHtml =
"""
<p>This status for <a href="https://vernissage.com/@wify" class="username">@wify</a>.<br /><br /><a href="https://vernissage.com/tags/street" rel="tag" class="mention hashtag">#street</a> <a href="https://vernissage.com/tags/photo" rel="tag" class="mention hashtag">#photo</a> <a href="https://vernissage.com/tags/blackAndWwhite" rel="tag" class="mention hashtag">#blackAndWwhite</a></p>
"""
#expect(html == expectedHtml)
}
Expand All @@ -90,7 +128,24 @@ struct StringHtmlTests {
// Assert.
let expectedHtml =
"""
<p>This is <a href="https://vernissage.com/tags/hashtag">#hashtag</a> for <a href="https://vernissage.com/@marcin">@marcin</a> and <a href="https://test.com" rel="me nofollow noopener noreferrer" class="url" target="_blank"><span class="invisible">https://</span>test.com</a> and <a href="https://vernissage.com/tags/street">#street</a> for <a href="https://mastodon.social/@marta">@[email protected]</a> and <a href="https://ap.com" rel="me nofollow noopener noreferrer" class="url" target="_blank"><span class="invisible">https://</span>ap.com</a> OK</p>
<p>This is <a href="https://vernissage.com/tags/hashtag" rel="tag" class="mention hashtag">#hashtag</a> for <a href="https://vernissage.com/@marcin" class=\"username\">@marcin</a> and <a href="https://test.com" rel="me nofollow noopener noreferrer" class="url" target="_blank"><span class="invisible">https://</span>test.com</a> and <a href="https://vernissage.com/tags/street" rel="tag" class="mention hashtag">#street</a> for <a href="https://mastodon.social/@marta" class=\"username\">@[email protected]</a> and <a href="https://ap.com" rel="me nofollow noopener noreferrer" class="url" target="_blank"><span class="invisible">https://</span>ap.com</a> OK</p>
"""
#expect(html == expectedHtml)
}

@Test("Rendering single url with user name")
func renderingSingleUrlWithUserName() async throws {

// Arrange.
let text = "Look here https://mastodon.social/@marcin please"

// Act.
let html = text.html(baseAddress: "https://vernissage.com")

// Assert.
let expectedHtml =
"""
<p>Look here <a href="https://mastodon.social/@marcin" rel="me nofollow noopener noreferrer" class="url" target="_blank"><span class="invisible">https://</span>mastodon.social/@marcin</a> please</p>
"""
#expect(html == expectedHtml)
}
Expand Down

0 comments on commit d7fb021

Please sign in to comment.