Module: Coradoc::AsciiDoc::Transformer::TableLayout

Defined in:
lib/coradoc/asciidoc/transformer/table_layout.rb

Overview

Pure functions for table row/column layout.

Extracted from the Transformer god class so that:

  • the Transformer stays focused on rule wiring

  • the layout math can be unit-tested in isolation

  • future formats (Markdown tables, DOCX) can reuse the math without reaching into AsciiDoc::Transformer

Class Method Summary collapse

Class Method Details

.group_cells_into_rows(cells, explicit_col_count = nil) ⇒ Array<Model::TableRow>

Group a flat list of cells into rows of ‘col_count` slots.

Parameters:

  • cells (Array<Model::TableCell, Hash, Object>)
  • explicit_col_count (Integer, nil) (defaults to: nil)

Returns:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/coradoc/asciidoc/transformer/table_layout.rb', line 43

def group_cells_into_rows(cells, explicit_col_count = nil)
  return [] if cells.nil? || cells.empty?

  normalized_cells = cells.map { |cell| TableCellBuilder.normalize_cell(cell) }

  col_count = explicit_col_count
  if col_count.nil? || col_count.zero?
    col_count = infer_column_count(normalized_cells)
  end
  col_count = normalized_cells.size if col_count.nil? || col_count.zero?

  rows = []
  current_row = []
  current_slots = 0

  normalized_cells.each do |cell|
    colspan = cell.is_a?(Model::TableCell) && cell.colspan ? cell.colspan : 1

    current_row << cell
    current_slots += colspan
    next unless current_slots >= col_count

    rows << Model::TableRow.new(columns: current_row)
    current_row = []
    current_slots = 0
  end

  rows << Model::TableRow.new(columns: current_row) if current_row.any?
  rows
end

.infer_column_count(cells) ⇒ Integer?

Infer a column count that consistently divides the colspan slots.

Parameters:

Returns:

  • (Integer, nil)


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/coradoc/asciidoc/transformer/table_layout.rb', line 77

def infer_column_count(cells)
  return nil if cells.nil? || cells.empty?

  col_slots = cells.map do |cell|
    cell.is_a?(Model::TableCell) && cell.colspan ? cell.colspan : 1
  end
  total_cells = col_slots.sum

  possible_cols = (1..[total_cells, 12].min).select do |candidate|
    next false if candidate > total_cells
    next false if total_cells % candidate != 0

    slots_used = 0
    valid = true

    col_slots.each do |slots|
      slots_used += slots
      if slots_used == candidate
        slots_used = 0
      elsif slots_used > candidate
        valid = false
        break
      end
    end

    valid && slots_used.zero?
  end

  possible_cols.max || col_slots.first || 1
end

.parse_cols_attribute(attrs) ⇒ Integer?

Parse the ‘cols=` attribute to determine column count.

Parameters:

Returns:

  • (Integer, nil)


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/coradoc/asciidoc/transformer/table_layout.rb', line 19

def parse_cols_attribute(attrs)
  return nil if attrs.nil?

  cols_value = if attrs.is_a?(Model::AttributeList)
                 attrs.named.find { |n| n.name.to_s == 'cols' }&.value
               elsif attrs.is_a?(Hash)
                 attrs['cols'] || attrs[:cols]
               end

  return nil if cols_value.nil?

  cols_str = cols_value.is_a?(Array) ? cols_value.first.to_s : cols_value.to_s
  cols_str = cols_str.gsub(/^["']|["']$/, '')

  return Regexp.last_match(1).to_i if cols_str =~ /^(\d+)\*$/
  return cols_str.split(',').size if cols_str.include?(',')

  cols_str.to_i if /^\d+$/.match?(cols_str)
end

.regroup_table_rows(rows, attrs = nil) ⇒ Array<Model::TableRow>

Regroup parser-level rows into proper AsciiDoc rows. The parser produces one “row” per line; this flattens all cells and regroups by the cols attribute, then marks the first row as header.

Parameters:

Returns:



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/coradoc/asciidoc/transformer/table_layout.rb', line 114

def regroup_table_rows(rows, attrs = nil)
  return rows if rows.nil? || rows.empty?

  col_count = parse_cols_attribute(attrs)
  if col_count.nil? && rows.first.is_a?(Model::TableRow) && rows.first.columns.any?
    col_count = rows.first.columns.sum { |c| (c.colspan || 1).to_i }
  end

  all_cells = rows.flat_map do |r|
    r.is_a?(Model::TableRow) ? r.columns : []
  end

  return rows if all_cells.empty?

  grouped = group_cells_into_rows(all_cells, col_count)
  grouped.first.header = true unless grouped.empty?
  grouped
end