Add support for shortening GitHub issues links (#531)

* Add support for shortening GitHub issues links

Relates to #411

* Added Tests
This commit is contained in:
James Sherlock
2017-10-14 00:42:23 +01:00
committed by Ryan Nystrom
parent d85cb91f07
commit ba4a0713ec
4 changed files with 79 additions and 1 deletions

View File

@@ -357,6 +357,53 @@ func updateIssueShorthand(
return mutableAttributedString
}
private let issueURLRegex = try! NSRegularExpression(pattern: "https?:\\/\\/.*github.com\\/(\\w*)\\/([^/]*?)\\/issues\\/([0-9]+)", options: [])
func shortenGitHubLinks(attributedString: NSAttributedString,
options: GitHubMarkdownOptions) -> NSAttributedString {
guard options.flavors.contains(.issueShorthand) else { return attributedString }
let string = attributedString.string
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
let matches = issueURLRegex.matches(in: string, options: [], range: string.nsrange)
for match in matches.reversed() {
let ownerRange = match.range(at: 1)
let repoRange = match.range(at: 2)
let numberRange = match.range(at: 3)
guard let ownerSubstring = string.substring(with: ownerRange),
let repoSubstring = string.substring(with: repoRange),
let numberSubstring = string.substring(with: numberRange) else { continue }
var attributes = attributedString.attributes(at: match.range.location, effectiveRange: nil)
// manually disable link shortening for some text (namely code)
guard attributes[MarkdownAttribute.linkShorteningDisabled] == nil else { continue }
attributes[.foregroundColor] = Styles.Colors.Blue.medium.color
attributes[MarkdownAttribute.issue] = IssueDetailsModel(
owner: ownerSubstring,
repo: repoSubstring,
number: (numberSubstring as NSString).integerValue
)
attributes[MarkdownAttribute.url] = nil
var shortenedText: String
if ownerSubstring == options.owner && repoSubstring == options.repo {
shortenedText = "#\(numberSubstring)"
} else {
shortenedText = "\(ownerSubstring)/\(repoSubstring)#\(numberSubstring)"
}
let linkAttributedString = NSAttributedString(string: shortenedText, attributes: attributes)
mutableAttributedString.replaceCharacters(in: match.range, with: linkAttributedString)
}
return mutableAttributedString
}
func createTextModelUpdatingGitHubFeatures(
attributedString: NSAttributedString,
width: CGFloat,
@@ -366,10 +413,11 @@ func createTextModelUpdatingGitHubFeatures(
let usernames = updateUsernames(attributedString: attributedString, options: options)
let issues = updateIssueShorthand(attributedString: usernames, options: options)
let shorten = shortenGitHubLinks(attributedString: issues, options: options)
return NSAttributedStringSizing(
containerWidth: width,
attributedText: issues,
attributedText: shorten,
inset: inset
)
}

View File

@@ -42,6 +42,7 @@ func PushAttributes(
NSAttributedStringKey.backgroundColor: Styles.Colors.Gray.lighter.color,
.foregroundColor: Styles.Colors.Gray.dark.color,
MarkdownAttribute.usernameDisabled: true,
MarkdownAttribute.linkShorteningDisabled: true,
]
case .link: newAttributes = [
.foregroundColor: Styles.Colors.Blue.medium.color,

View File

@@ -13,5 +13,6 @@ enum MarkdownAttribute {
static let email = NSAttributedStringKey(rawValue: "com.freetime.Markdown.email-name")
static let username = NSAttributedStringKey(rawValue: "com.freetime.Markdown.username-name")
static let usernameDisabled = NSAttributedStringKey(rawValue: "com.freetime.Markdown.username-disabled-name")
static let linkShorteningDisabled = NSAttributedStringKey(rawValue: "com.freetime.Markdown.link-shortening-disabled-name")
static let issue = NSAttributedStringKey(rawValue: "com.freetime.Markdown.issue")
}

View File

@@ -124,5 +124,33 @@ final class MMMarkdownASTTests: XCTestCase {
XCTAssertEqual(comboDetails.repo, "bar")
XCTAssertEqual(comboDetails.number, 456)
}
func test_shortenLinks() {
let options = GitHubMarkdownOptions(owner: "owner", repo: "repo", flavors: [.issueShorthand])
// Test Same Repository
let testOne = "https://github.com/owner/repo/issues/123"
let resultOne = CreateCommentModels(markdown: testOne, width: 300, options: options)
let textOne = resultOne.first as! NSAttributedStringSizing
XCTAssertEqual(textOne.attributedText.string, "#123")
XCTAssertNotNil(textOne.attributedText.attributes(at: 1, effectiveRange: nil)[MarkdownAttribute.issue])
let detailsOne = textOne.attributedText.attributes(at: 1, effectiveRange: nil)[MarkdownAttribute.issue] as! IssueDetailsModel
XCTAssertEqual(detailsOne.owner, "owner")
XCTAssertEqual(detailsOne.repo, "repo")
XCTAssertEqual(detailsOne.number, 123)
// Test Cross Repository
let testTwo = "https://github.com/differentOwner/differentRepo/issues/321"
let resultTwo = CreateCommentModels(markdown: testTwo, width: 300, options: options)
let textTwo = resultTwo.first as! NSAttributedStringSizing
XCTAssertEqual(textTwo.attributedText.string, "differentOwner/differentRepo#321")
XCTAssertNotNil(textTwo.attributedText.attributes(at: 1, effectiveRange: nil)[MarkdownAttribute.issue])
let detailsTwo = textTwo.attributedText.attributes(at: 1, effectiveRange: nil)[MarkdownAttribute.issue] as! IssueDetailsModel
XCTAssertEqual(detailsTwo.owner, "differentOwner")
XCTAssertEqual(detailsTwo.repo, "differentRepo")
XCTAssertEqual(detailsTwo.number, 321)
}
}