Skip to content

Commit 5090c79

Browse files
Merge pull request #1242 from wordpress-mobile/issue/implement_list_reverse_start
Implement start and reversed in lists.
2 parents cd80f36 + 03e15fc commit 5090c79

7 files changed

Lines changed: 126 additions & 23 deletions

File tree

Aztec/Classes/Extensions/NSAttributedString+Lists.swift

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,41 @@ extension NSAttributedString {
6868
return resultRange
6969
}
7070

71+
/// Returns the number of items of a list at the given location.
72+
///
73+
/// - Parameters:
74+
/// - list: The list.
75+
/// - location: The location of the item.
76+
///
77+
/// - Returns: Returns the total number of items within the list or NSNotFound if no list is available at location.
78+
///
79+
func numberOfItems(in list: TextList, at location: Int) -> Int {
80+
guard
81+
let paragraphStyle = attribute(.paragraphStyle, at: location, effectiveRange: nil) as? ParagraphStyle
82+
else {
83+
return NSNotFound
84+
}
85+
let listDepth = paragraphStyle.lists.count
86+
guard let rangeOfList = range(of:list, at: location) else {
87+
return NSNotFound
88+
}
89+
var numberInList = 0
90+
let paragraphRanges = self.paragraphRanges(intersecting: rangeOfList)
91+
for (_, enclosingRange) in paragraphRanges {
92+
if let paragraphStyle = attribute(.paragraphStyle, at: enclosingRange.location, effectiveRange: nil) as? ParagraphStyle,
93+
listDepth == paragraphStyle.lists.count {
94+
numberInList += 1
95+
}
96+
}
97+
return numberInList
98+
}
7199
/// Returns the index of the item at the given location within the list.
72100
///
73101
/// - Parameters:
74102
/// - list: The list.
75103
/// - location: The location of the item.
76104
///
77-
/// - Returns: Returns the index within the list.
105+
/// - Returns: Returns the index within the list or NSNotFound is no list is available at location.
78106
///
79107
func itemNumber(in list: TextList, at location: Int) -> Int {
80108
guard
@@ -86,7 +114,7 @@ extension NSAttributedString {
86114
guard let rangeOfList = range(of:list, at: location) else {
87115
return NSNotFound
88116
}
89-
var numberInList = 1
117+
var numberInList = 0
90118
let paragraphRanges = self.paragraphRanges(intersecting: rangeOfList)
91119

92120
for (_, enclosingRange) in paragraphRanges {

Aztec/Classes/Libxml2/DOM/Data/AttributeType.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@ extension AttributeType {
3636
public static let src = AttributeType("src")
3737
public static let style = AttributeType("style")
3838
public static let target = AttributeType("target")
39+
public static let reversed = AttributeType("reversed")
40+
public static let start = AttributeType("start")
3941
}

Aztec/Classes/NSAttributedString/Conversions/AttributedStringParser.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,15 @@ private extension AttributedStringParser {
765765
listElement = element.toElementNode()
766766
} else {
767767
listElement = ElementNode(type: listType)
768+
if list.style == .ordered {
769+
if list.reversed {
770+
listElement.updateAttribute(named: "reversed", value: .none)
771+
}
772+
773+
if let start = list.start {
774+
listElement.updateAttribute(named: "start", value: .string("\(start)"))
775+
}
776+
}
768777
}
769778

770779
return listElement

Aztec/Classes/TextKit/LayoutManager.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,16 @@ private extension LayoutManager {
210210

211211
let glyphRange = self.glyphRange(forCharacterRange: enclosingRange, actualCharacterRange: nil)
212212
let markerRect = rectForItem(range: glyphRange, origin: origin, paragraphStyle: paragraphStyle)
213-
let markerNumber = textStorage.itemNumber(in: list, at: enclosingRange.location)
214-
213+
var markerNumber = textStorage.itemNumber(in: list, at: enclosingRange.location)
214+
var start = list.start ?? 1
215+
if list.reversed {
216+
markerNumber = -markerNumber
217+
if list.start == nil {
218+
start = textStorage.numberOfItems(in: list, at: enclosingRange.location)
219+
}
220+
}
221+
markerNumber += start
222+
215223
drawItem(number: markerNumber, in: markerRect, from: list, using: paragraphStyle, at: enclosingRange.location)
216224
}
217225
}

Aztec/Classes/TextKit/ParagraphProperty/TextList.swift

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,34 @@ open class TextList: ParagraphProperty {
2222
}
2323
}
2424

25+
public let reversed: Bool
26+
27+
public let start: Int?
28+
2529
// MARK: - Properties
2630

2731
/// Kind of List: Ordered / Unordered
2832
///
2933
let style: Style
3034

31-
init(style: Style, with representation: HTMLRepresentation? = nil) {
35+
init(style: Style, start: Int? = nil, reversed: Bool = false, with representation: HTMLRepresentation? = nil) {
3236
self.style = style
37+
38+
if let representation = representation, case let .element( html ) = representation.kind {
39+
self.reversed = html.attribute(ofType: .reversed) != nil
40+
41+
if let startAttribute = html.attribute(ofType: .start),
42+
case let .string( value ) = startAttribute.value,
43+
let start = Int(value)
44+
{
45+
self.start = start
46+
} else {
47+
self.start = nil
48+
}
49+
} else {
50+
self.start = start
51+
self.reversed = reversed
52+
}
3353
super.init(with: representation)
3454
}
3555

@@ -40,15 +60,31 @@ open class TextList: ParagraphProperty {
4060
} else {
4161
style = .ordered
4262
}
63+
if aDecoder.containsValue(forKey: AttributeType.start.rawValue) {
64+
let decodedStart = aDecoder.decodeInteger(forKey: AttributeType.start.rawValue)
65+
start = decodedStart
66+
} else {
67+
start = nil
68+
}
69+
70+
if aDecoder.containsValue(forKey: AttributeType.reversed.rawValue) {
71+
let decodedReversed = aDecoder.decodeBool(forKey: AttributeType.reversed.rawValue)
72+
reversed = decodedReversed
73+
} else {
74+
reversed = false
75+
}
76+
4377
super.init(coder: aDecoder)
4478
}
4579

4680
public override func encode(with aCoder: NSCoder) {
4781
super.encode(with: aCoder)
4882
aCoder.encode(style.rawValue, forKey: String(describing: Style.self))
83+
aCoder.encode(start, forKey: AttributeType.start.rawValue)
84+
aCoder.encode(reversed, forKey: AttributeType.reversed.rawValue)
4985
}
5086

5187
public static func ==(lhs: TextList, rhs: TextList) -> Bool {
52-
return lhs.style == rhs.style
88+
return lhs.style == rhs.style && lhs.start == rhs.start && lhs.reversed == rhs.reversed
5389
}
5490
}

AztecTests/Formatters/TextListFormatterTests.swift

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ class TextListFormatterTests: XCTestCase {
3333
XCTAssert(lists[2].style == .unordered)
3434
XCTAssert(lists[3].style == .ordered)
3535

36-
XCTAssert(list.itemNumber(in: lists[0], at: ranges[0].location) == 1)
37-
XCTAssert(list.itemNumber(in: lists[3], at: ranges[3].location) == 1)
36+
XCTAssert(list.itemNumber(in: lists[0], at: ranges[0].location) == 0)
37+
XCTAssert(list.itemNumber(in: lists[3], at: ranges[3].location) == 0)
3838
}
3939

4040
// Helpers #2:
@@ -64,7 +64,7 @@ class TextListFormatterTests: XCTestCase {
6464
}
6565

6666
for (index, textList) in lists.enumerated() {
67-
XCTAssert(list.itemNumber(in: textList, at: ranges[index].location) == index + 1)
67+
XCTAssert(list.itemNumber(in: textList, at: ranges[index].location) == index)
6868
}
6969
}
7070

@@ -92,7 +92,7 @@ class TextListFormatterTests: XCTestCase {
9292
XCTAssert(lists.count == 1)
9393

9494
XCTAssert(lists[0].style == .ordered)
95-
XCTAssert(string.itemNumber(in: lists[0], at: ranges[0].location) == 1)
95+
XCTAssert(string.itemNumber(in: lists[0], at: ranges[0].location) == 0)
9696
}
9797

9898

@@ -152,7 +152,7 @@ class TextListFormatterTests: XCTestCase {
152152
}
153153

154154
for (index, textList) in lists.enumerated() {
155-
XCTAssertEqual(list.itemNumber(in: textList, at: ranges[index+1].location), index + 1)
155+
XCTAssertEqual(list.itemNumber(in: textList, at: ranges[index+1].location), index)
156156
}
157157
}
158158

@@ -185,7 +185,7 @@ class TextListFormatterTests: XCTestCase {
185185
XCTAssert(lists[2].style == .ordered)
186186
XCTAssert(lists[3].style == .unordered)
187187

188-
XCTAssert(list.itemNumber(in: lists[0], at: ranges[0].location) == 1)
188+
XCTAssert(list.itemNumber(in: lists[0], at: ranges[0].location) == 0)
189189
XCTAssertEqual(list.itemNumber(in: lists[2], at: ranges[2].location), NSNotFound)
190190
}
191191

@@ -246,9 +246,9 @@ class TextListFormatterTests: XCTestCase {
246246
XCTAssert(lists[2].style == .unordered)
247247
XCTAssert(lists[3].style == .ordered)
248248

249-
XCTAssert(list.itemNumber(in: lists[0], at: ranges[0].location) == 1)
250-
XCTAssert(list.itemNumber(in: lists[1], at: ranges[1].location) == 2)
251-
XCTAssert(list.itemNumber(in: lists[3], at: ranges[3].location) == 1)
249+
XCTAssert(list.itemNumber(in: lists[0], at: ranges[0].location) == 0)
250+
XCTAssert(list.itemNumber(in: lists[1], at: ranges[1].location) == 1)
251+
XCTAssert(list.itemNumber(in: lists[3], at: ranges[3].location) == 0)
252252
}
253253

254254

@@ -280,9 +280,9 @@ class TextListFormatterTests: XCTestCase {
280280
XCTAssert(lists[3].style == .ordered)
281281
XCTAssert(lists[4].style == .ordered)
282282

283-
XCTAssert(list.itemNumber(in: lists[0], at: ranges[0].location) == 1)
284-
XCTAssert(list.itemNumber(in: lists[1], at: ranges[1].location) == 2)
285-
XCTAssert(list.itemNumber(in: lists[3], at: ranges[3].location) == 4)
283+
XCTAssert(list.itemNumber(in: lists[0], at: ranges[0].location) == 0)
284+
XCTAssert(list.itemNumber(in: lists[1], at: ranges[1].location) == 1)
285+
XCTAssert(list.itemNumber(in: lists[3], at: ranges[3].location) == 3)
286286

287287
}
288288

@@ -318,7 +318,7 @@ class TextListFormatterTests: XCTestCase {
318318
XCTAssert(list!.style == .ordered)
319319

320320

321-
XCTAssert(string.itemNumber(in: list!, at: range.location) == index + 1)
321+
XCTAssert(string.itemNumber(in: list!, at: range.location) == index)
322322
}
323323
}
324324

@@ -356,7 +356,7 @@ class TextListFormatterTests: XCTestCase {
356356
}
357357

358358
XCTAssert(list.style == .ordered)
359-
XCTAssert(string.itemNumber(in: list, at: range.location) == index+1)
359+
XCTAssert(string.itemNumber(in: list, at: range.location) == index)
360360
}
361361
}
362362

@@ -424,11 +424,10 @@ class TextListFormatterTests: XCTestCase {
424424

425425
XCTAssert(items.count == 5)
426426

427-
XCTAssert(list.itemNumber(in: items[0], at: 0) == 1)
428-
XCTAssert(list.itemNumber(in: items[0], at: 13) == 2)
427+
XCTAssert(list.itemNumber(in: items[0], at: 0) == 0)
428+
XCTAssert(list.itemNumber(in: items[0], at: 13) == 1)
429429
}
430430

431-
432431
// Verifies that the `present(in: at:)` helper effectively returns true, as needed.
433432
//
434433
func testPresentInStorageAtIndexReturnsTrueWhenTextListIsEffectivelyThere() {

Example/Example/SampleContent/content.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,27 @@ <h4>Ordered List:</h4>
3737
<li>Three</li>
3838
</ol>
3939

40+
<h4>Start in 10 List:</h4>
41+
<ol start="10">
42+
<li>Ten</li>
43+
<li>Eleven</li>
44+
<li>Twelve</li>
45+
</ol>
46+
47+
<h4>Reversed List:</h4>
48+
<ol reversed>
49+
<li>Three</li>
50+
<li>Two</li>
51+
<li>One</li>
52+
</ol>
53+
54+
<h4>Reversed Start in 10 List:</h4>
55+
<ol reversed start="10">
56+
<li>Ten</li>
57+
<li>Nine</li>
58+
<li>Eight</li>
59+
</ol>
60+
4061
<h4>Nested lists:</h4>
4162

4263
<ol>

0 commit comments

Comments
 (0)