@@ -235,13 +235,16 @@ public enum RenderBlockContent: Equatable {
235235 public var header : HeaderType
236236 /// The rows in this table.
237237 public var rows : [ TableRow ]
238+ /// Any extended information that describes cells in this table.
239+ public var extendedData : Set < TableCellExtendedData >
238240 /// Additional metadata for this table, if present.
239241 public var metadata : RenderContentMetadata ?
240242
241243 /// Creates a new table with the given data.
242- public init ( header: HeaderType , rows: [ TableRow ] , metadata: RenderContentMetadata ? = nil ) {
244+ public init ( header: HeaderType , rows: [ TableRow ] , extendedData : Set < TableCellExtendedData > , metadata: RenderContentMetadata ? = nil ) {
243245 self . header = header
244246 self . rows = rows
247+ self . extendedData = extendedData
245248 self . metadata = metadata
246249 }
247250 }
@@ -382,6 +385,36 @@ public enum RenderBlockContent: Equatable {
382385 cells = try container. decode ( [ Cell ] . self)
383386 }
384387 }
388+
389+ /// Extended data that may be applied to a table cell.
390+ public struct TableCellExtendedData : Equatable , Hashable {
391+ /// The row coordinate for the cell described by this data.
392+ public let rowIndex : Int
393+ /// The column coordinate for the cell described by this data.
394+ public let columnIndex : Int
395+
396+ /// The number of columns this cell spans over.
397+ ///
398+ /// A value of 1 is the default. A value of zero means that this cell is being "spanned
399+ /// over" by a previous cell in this row. A value of greater than 1 means that this cell
400+ /// "spans over" later cells in this row.
401+ public let colspan : UInt
402+
403+ /// The number of rows this cell spans over.
404+ ///
405+ /// A value of 1 is the default. A value of zero means that this cell is being "spanned
406+ /// over" by another cell in a previous row. A value of greater than one means that this
407+ /// cell "spans over" other cells in later rows.
408+ public let rowspan : UInt
409+
410+ public init ( rowIndex: Int , columnIndex: Int ,
411+ colspan: UInt , rowspan: UInt ) {
412+ self . rowIndex = rowIndex
413+ self . columnIndex = columnIndex
414+ self . colspan = colspan
415+ self . rowspan = rowspan
416+ }
417+ }
385418
386419 /// A term definition.
387420 ///
@@ -442,6 +475,83 @@ public enum RenderBlockContent: Equatable {
442475 }
443476}
444477
478+ // Writing a manual Codable implementation for tables because the encoding of `extendedData` does
479+ // not follow from the struct layout.
480+ extension RenderBlockContent . Table : Codable {
481+ enum CodingKeys : String , CodingKey {
482+ case header, rows, extendedData, metadata
483+ }
484+
485+ // TableCellExtendedData encodes the row and column indices as a dynamic key with the format "{row}_{column}".
486+ struct DynamicIndexCodingKey : CodingKey , Equatable {
487+ let row , column : Int
488+ init ( row: Int , column: Int ) {
489+ self . row = row
490+ self . column = column
491+ }
492+
493+ var stringValue : String {
494+ return " \( row) _ \( column) "
495+ }
496+ init ? ( stringValue: String ) {
497+ let coordinates = stringValue. split ( separator: " _ " )
498+ guard coordinates. count == 2 ,
499+ let rowIndex = Int ( coordinates. first!) ,
500+ let columnIndex = Int ( coordinates. last!) else {
501+ return nil
502+ }
503+ row = rowIndex
504+ column = columnIndex
505+ }
506+ // The key is only represented by a string value
507+ var intValue : Int ? { nil }
508+ init ? ( intValue: Int ) { nil }
509+ }
510+
511+ enum ExtendedDataCodingKeys : String , CodingKey {
512+ case colspan, rowspan
513+ }
514+
515+ public init ( from decoder: Decoder ) throws {
516+ let container = try decoder. container ( keyedBy: CodingKeys . self)
517+
518+ self . header = try container. decode ( RenderBlockContent . HeaderType. self, forKey: . header)
519+ self . rows = try container. decode ( [ RenderBlockContent . TableRow ] . self, forKey: . rows)
520+ self . metadata = try container. decodeIfPresent ( RenderContentMetadata . self, forKey: . metadata)
521+
522+ var extendedData = Set < RenderBlockContent . TableCellExtendedData > ( )
523+ if container. contains ( . extendedData) {
524+ let dataContainer = try container. nestedContainer ( keyedBy: DynamicIndexCodingKey . self, forKey: . extendedData)
525+
526+ for index in dataContainer. allKeys {
527+ let cellContainer = try dataContainer. nestedContainer ( keyedBy: ExtendedDataCodingKeys . self, forKey: index)
528+ extendedData. insert ( . init( rowIndex: index. row,
529+ columnIndex: index. column,
530+ colspan: try cellContainer. decode ( UInt . self, forKey: . colspan) ,
531+ rowspan: try cellContainer. decode ( UInt . self, forKey: . rowspan) ) )
532+ }
533+ }
534+ self . extendedData = extendedData
535+ }
536+
537+ public func encode( to encoder: Encoder ) throws {
538+ var container = encoder. container ( keyedBy: CodingKeys . self)
539+ try container. encode ( header, forKey: . header)
540+ try container. encode ( rows, forKey: . rows)
541+ try container. encodeIfPresent ( metadata, forKey: . metadata)
542+
543+ if !extendedData. isEmpty {
544+ var dataContainer = container. nestedContainer ( keyedBy: DynamicIndexCodingKey . self, forKey: . extendedData)
545+ for data in extendedData {
546+ var cellContainer = dataContainer. nestedContainer ( keyedBy: ExtendedDataCodingKeys . self,
547+ forKey: . init( row: data. rowIndex, column: data. columnIndex) )
548+ try cellContainer. encode ( data. colspan, forKey: . colspan)
549+ try cellContainer. encode ( data. rowspan, forKey: . rowspan)
550+ }
551+ }
552+ }
553+ }
554+
445555// Codable conformance
446556extension RenderBlockContent : Codable {
447557 private enum CodingKeys : CodingKey {
@@ -488,11 +598,8 @@ extension RenderBlockContent: Codable {
488598 case . dictionaryExample:
489599 self = try . dictionaryExample( . init( summary: container. decodeIfPresent ( [ RenderBlockContent ] . self, forKey: . summary) , example: container. decode ( CodeExample . self, forKey: . example) ) )
490600 case . table:
491- self = try . table( . init(
492- header: container. decode ( HeaderType . self, forKey: . header) ,
493- rows: container. decode ( [ TableRow ] . self, forKey: . rows) ,
494- metadata: container. decodeIfPresent ( RenderContentMetadata . self, forKey: . metadata)
495- ) )
601+ // Defer to Table's own Codable implemenatation to parse `extendedData` properly.
602+ self = try . table( . init( from: decoder) )
496603 case . termList:
497604 self = try . termList( . init( items: container. decode ( [ TermListItem ] . self, forKey: . items) ) )
498605 case . row:
@@ -569,9 +676,8 @@ extension RenderBlockContent: Codable {
569676 try container. encodeIfPresent ( e. summary, forKey: . summary)
570677 try container. encode ( e. example, forKey: . example)
571678 case . table( let t) :
572- try container. encode ( t. header, forKey: . header)
573- try container. encode ( t. rows, forKey: . rows)
574- try container. encodeIfPresent ( t. metadata, forKey: . metadata)
679+ // Defer to Table's own Codable implemenatation to format `extendedData` properly.
680+ try t. encode ( to: encoder)
575681 case . termList( items: let l) :
576682 try container. encode ( l. items, forKey: . items)
577683 case . row( let row) :
0 commit comments