Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Aztec.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
B57D1C3D1E92C38000EA4B16 /* HTMLAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57D1C3C1E92C38000EA4B16 /* HTMLAttachment.swift */; };
B59C9F9F1DF74BB80073B1D6 /* UIFont+Traits.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59C9F9E1DF74BB80073B1D6 /* UIFont+Traits.swift */; };
B5A99D841EBA073D00DED081 /* HTMLStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A99D831EBA073D00DED081 /* HTMLStorage.swift */; };
B5AB79F81F5F3E0B00DF26F5 /* String+EndOfParagraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AB79F71F5F3E0B00DF26F5 /* String+EndOfParagraph.swift */; };
B5AB79FA1F5F403C00DF26F5 /* StringEndOfParagraphTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AB79F91F5F403C00DF26F5 /* StringEndOfParagraphTests.swift */; };
B5AB79F81F5F3E0B00DF26F5 /* String+Paragraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AB79F71F5F3E0B00DF26F5 /* String+Paragraph.swift */; };
B5AB79FA1F5F403C00DF26F5 /* StringParagraphTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AB79F91F5F403C00DF26F5 /* StringParagraphTests.swift */; };
B5AF89321E93CFC80051EFDB /* RenderableAttachmentDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AF89311E93CFC80051EFDB /* RenderableAttachmentDelegate.swift */; };
B5B86D371DA3EC250083DB3F /* NSRange+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18733C41DA096EE005AEB80 /* NSRange+Helpers.swift */; };
B5B96DAB1E01B2F300791315 /* UIPasteboard+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B96DAA1E01B2F300791315 /* UIPasteboard+Helpers.swift */; };
Expand Down Expand Up @@ -221,8 +221,8 @@
B57D1C3C1E92C38000EA4B16 /* HTMLAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLAttachment.swift; sourceTree = "<group>"; };
B59C9F9E1DF74BB80073B1D6 /* UIFont+Traits.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIFont+Traits.swift"; sourceTree = "<group>"; };
B5A99D831EBA073D00DED081 /* HTMLStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLStorage.swift; sourceTree = "<group>"; };
B5AB79F71F5F3E0B00DF26F5 /* String+EndOfParagraph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+EndOfParagraph.swift"; sourceTree = "<group>"; };
B5AB79F91F5F403C00DF26F5 /* StringEndOfParagraphTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StringEndOfParagraphTests.swift; path = Extensions/StringEndOfParagraphTests.swift; sourceTree = "<group>"; };
B5AB79F71F5F3E0B00DF26F5 /* String+Paragraph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Paragraph.swift"; sourceTree = "<group>"; };
B5AB79F91F5F403C00DF26F5 /* StringParagraphTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StringParagraphTests.swift; path = Extensions/StringParagraphTests.swift; sourceTree = "<group>"; };
B5AF89311E93CFC80051EFDB /* RenderableAttachmentDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RenderableAttachmentDelegate.swift; sourceTree = "<group>"; };
B5B96DAA1E01B2F300791315 /* UIPasteboard+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIPasteboard+Helpers.swift"; sourceTree = "<group>"; };
B5BC4FED1DA2C17800614582 /* NSAttributedString+Lists.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Lists.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -511,7 +511,7 @@
B5B96DAA1E01B2F300791315 /* UIPasteboard+Helpers.swift */,
B5C99D3E1E72E2E700335355 /* UIStackView+Helpers.swift */,
F1DE83D41EF20493009269E6 /* UIColor+Parsers.swift */,
B5AB79F71F5F3E0B00DF26F5 /* String+EndOfParagraph.swift */,
B5AB79F71F5F3E0B00DF26F5 /* String+Paragraph.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -779,7 +779,7 @@
B5375F481EC2569500F5D7EC /* StringHTMLTests.swift */,
FF152D8D1E68552A00FF596C /* StringRangeConversionTests.swift */,
FF7DCB4A1E815F9400AB77CB /* UIColorHexParserTests.swift */,
B5AB79F91F5F403C00DF26F5 /* StringEndOfParagraphTests.swift */,
B5AB79F91F5F403C00DF26F5 /* StringParagraphTests.swift */,
);
name = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -970,7 +970,7 @@
F12F586C1EF20394008AE298 /* HTMLParagraphFormatter.swift in Sources */,
F17BC86B1F4E466100398E2B /* NSAttributedString+HTMLInitializer.swift in Sources */,
F12F58661EF20394008AE298 /* StandardAttributeFormatter.swift in Sources */,
B5AB79F81F5F3E0B00DF26F5 /* String+EndOfParagraph.swift in Sources */,
B5AB79F81F5F3E0B00DF26F5 /* String+Paragraph.swift in Sources */,
B5A99D841EBA073D00DED081 /* HTMLStorage.swift in Sources */,
F13CE53E1F4DD05E0043368D /* PipelineProcessor.swift in Sources */,
F17BC8AA1F4E512800398E2B /* AttributedStringParser.swift in Sources */,
Expand Down Expand Up @@ -1077,7 +1077,7 @@
B5F84B631E706B720089A76C /* NSAttributedStringAnalyzerTests.swift in Sources */,
F10BE61C1EA7B1DB002E4625 /* NSAttributedStringReplaceOcurrencesTests.swift in Sources */,
B5375F491EC2569500F5D7EC /* StringHTMLTests.swift in Sources */,
B5AB79FA1F5F403C00DF26F5 /* StringEndOfParagraphTests.swift in Sources */,
B5AB79FA1F5F403C00DF26F5 /* StringParagraphTests.swift in Sources */,
B57534521F267D63009D4904 /* ArrayHelperTests.swift in Sources */,
F14665451EA7C230008DE2B8 /* NSMutableAttributedStringReplaceOcurrencesTests.swift in Sources */,
F17BC8B51F4E517100398E2B /* AttributedStringParserTests.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Aztec/Classes/Extensions/String+EndOfLine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extension String {
func isEmptyLine(at offset: Int) -> Bool {
guard let index = self.indexFromLocation(offset) else {
return true
}
}

return isEmptyLine(at: index)
}
Expand Down
28 changes: 0 additions & 28 deletions Aztec/Classes/Extensions/String+EndOfParagraph.swift

This file was deleted.

77 changes: 77 additions & 0 deletions Aztec/Classes/Extensions/String+Paragraph.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import Foundation


// MARK: - Paragraph Analysis Helpers
//
extension String {

/// This methods verifies if the receiver string contains a new paragraph at the specified index.
///
/// - Parameter index: the index to check
///
/// - Returns: `true` if the receiver contains a new paragraph at the specified Index.
///
func isStartOfParagraph(at index: String.Index) -> Bool {
guard index != startIndex else {
return true
}

return isEndOfParagraph(before: index)
}


/// This methods verifies if the receiver string contains an End of Paragraph at the specified index.
///
/// - Parameter index: the index to check
///
/// - Returns: `true` if the receiver contains an end-of-paragraph character at the specified Index.
///
func isEndOfParagraph(at index: String.Index) -> Bool {
guard index != endIndex else {
return true
}

let endingString = substring(with: index ..< self.index(after: index))
let paragraphSeparators = [String(.carriageReturn), String(.lineFeed), String(.paragraphSeparator)]

return paragraphSeparators.contains(endingString)
}


/// This methods verifies if the receiver string contains an End of Paragraph before the specified index.
///
/// - Parameter index: the index to check
///
/// - Returns: `true` if the receiver contains an end-of-paragraph character before the specified Index.
///
func isEndOfParagraph(before index: String.Index) -> Bool {
assert(index != startIndex)
return isEndOfParagraph(at: self.index(before: index))
}


/// Checks if the receiver has an empty paragraph at the specified index.
///
/// - Parameter index: the receiver's index to check
///
/// - Returns: `true` if the specified index is in an empty paragraph, `false` otherwise.
///
func isEmptyParagraph(at index: String.Index) -> Bool {
return isStartOfParagraph(at: index) && isEndOfParagraph(at: index)
}


/// Checks if the receiver has an empty paragraph at the specified offset.
///
/// - Parameter offset: the receiver's offset to check
///
/// - Returns: `true` if the specified offset is in an empty line, `false` otherwise.
///
func isEmptyParagraph(at offset: Int) -> Bool {
guard let index = self.indexFromLocation(offset) else {
return true
}

return isEmptyParagraph(at: index)
}
}
2 changes: 1 addition & 1 deletion Aztec/Classes/TextKit/TextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1323,7 +1323,7 @@ private extension TextView {
/// - Returns: `true` if we should nuke the paragraph attributes.
///
private func mustRemoveParagraphStylesBeforeRemovingCharacter(at range: NSRange) -> Bool {
return storage.string.isEmptyLine(at: range.location)
return storage.string.isEmptyParagraph(at: range.location)
}

// MARK: - WORKAROUND: Removing paragraph styles after entering a newline.
Expand Down
17 changes: 0 additions & 17 deletions AztecTests/Extensions/StringEndOfParagraphTests.swift

This file was deleted.

92 changes: 92 additions & 0 deletions AztecTests/Extensions/StringParagraphTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import XCTest
@testable import Aztec

class StringParagraphTests: XCTestCase {

/// Verifies that isStartOfParagraph(at:) returns true on the first position of an empty string
///
func testIsStartOfParagraphReturnsTrueOnEmptyStrings() {
let sample = String()

XCTAssertTrue(sample.isStartOfParagraph(at: sample.startIndex))
}

/// Verifies that isStartOfParagraph(at:) returns true when checked against the first position
///
func testIsStartOfParagraphReturnsTrueAtTheBeginningOfNewParagraphs() {
let sample = "Sample"

XCTAssertTrue(sample.isStartOfParagraph(at: sample.startIndex))
}

/// Verifies that isStartOfParagraph(at:) returns false when checked against any position that is not the first one
///
func testIsStartOfParagraphReturnsFalseAtAnyPositionThatIsNotTheFirstOne() {
let sample = "Sample"

for location in 1 ..< sample.characters.count {
let index = sample.indexFromLocation(location)!
XCTAssertFalse(sample.isStartOfParagraph(at: index))
}
}

/// Verifies that isEndOfParagraph(before:) returns false when checked against a Line Separator character
/// (which effectively adds a new paragraph)
///
func testIsEndOfParagraphReturnsTrueWheneverTestStringEndsWithCarriageReturn() {
let sample = "Sample" + String(.carriageReturn)

XCTAssertTrue(sample.isEndOfParagraph(before: sample.endIndex))
}

/// Verifies that isEndOfParagraph(before:) returns false when checked against a Line Separator character
/// (which effectively adds a newline that belongs to the current paragraph)
///
func testIsEndOfParagraphReturnsFalseWheneverTestStringEndsWithLineSeparator() {
let sample = "Sample" + String(.lineSeparator)

XCTAssertFalse(sample.isEndOfParagraph(before: sample.endIndex))
}

/// Verifies that isEndOfParagraph(at:) does not crash on empty strings
///
func testIsEndOfParagraphDoesNotCrashOnEmptyStrings() {
let sample = String()

XCTAssertNoThrow(sample.isEndOfParagraph(at: sample.endIndex))
}

/// Verifies that isEmptyParagraph(at:) does not crash on empty strings
///
func testIsEmptyParagraphDoesNotCrashOnEmptyStrings() {
let sample = String()

XCTAssertNoThrow(sample.isEmptyParagraph(at: sample.endIndex))
}

/// Verifies that isEmptyParagraph(at:) returns false on any position that does not belong to an empty paragraph.
///
func testIsEmptyParagraphReturnsFalseOnNonEmptyParagraphs() {
let sample = "Sample"

for i in 0 ..< sample.characters.count {
XCTAssertFalse(sample.isEmptyParagraph(at: i))
}
}

/// Verifies that isEmptyParagraph(at:) returns false on empty lines that DO belong to the previous paragraph.
///
func testIsEmptyParagraphReturnsFalseOnEmptyLinesThatBelongToABiggerParagraph() {
let sample = "Sample" + String(.lineSeparator)

XCTAssertFalse(sample.isEmptyParagraph(at: sample.characters.count - 1))
}

/// Verifies that isEmptyParagraph(at:) returns true on empty lines, that do not belong to the previous paragraph.
///
func testIsEmptyParagraphReturnsTrueOnEmptyLinesThatDoNotBelongToABiggerParagraph() {
let sample = "Sample" + String(.lineFeed)

XCTAssertTrue(sample.isEmptyParagraph(at: sample.characters.count))
}
}