lib/format_table.rb in markdown_exec-2.4.0 vs lib/format_table.rb in markdown_exec-2.5.0
- old
+ new
@@ -5,66 +5,14 @@
require_relative 'hierarchy_string'
module MarkdownTableFormatter
module_function
- def format_table(lines, columns, decorate: nil)
- rows = parse_rows(lines, columns)
+ def calculate_column_alignment_and_widths(rows, column_count)
+ alignment_indicators = Array.new(column_count, :left)
+ column_widths = Array.new(column_count, 0)
- alignment_indicators, column_widths =
- calculate_column_alignment_and_widths(rows, columns)
-
- format_rows(rows, alignment_indicators, column_widths, decorate)
- end
-
- def parse_rows(lines, columns)
- role = :header_row
- counter = -1
-
- lines.map.with_index do |line, _row_ind|
- line += '|' unless line.end_with?('|')
- counter += 1
-
- role = update_role(role, line)
- counter = reset_counter_if_needed(role, counter)
-
- cells = extract_cells(line, columns)
-
- OpenStruct.new(cells: cells, role: role, counter: counter)
- end
- end
-
- def update_role(current_role, line)
- case current_role
- when :header_row
- if line =~ /^[ \t]*\| *[:\-][:\- |]*$/
- :separator_line
- else
- current_role
- end
- when :separator_line
- :row
- when :row
- current_role
- else
- raise "Unexpected role: #{current_role} for line #{line}"
- end
- end
-
- def reset_counter_if_needed(role, counter)
- %i[header_row row].include?(role) ? counter : 0
- end
-
- def extract_cells(line, columns)
- cells = line.split('|').map(&:strip)[1..-1]
- cells&.fill('', cells.length...columns)
- end
-
- def calculate_column_alignment_and_widths(rows, columns)
- alignment_indicators = Array.new(columns, :left)
- column_widths = Array.new(columns, 0)
-
rows.each do |row|
next if row.cells.nil?
row.cells.each_with_index do |cell, i|
column_widths[i] = [column_widths[i], cell.length].max
@@ -74,36 +22,64 @@
end
end
end
# 2024-08-24 remove last column if it is 0-width
- if column_widths.last.zero?
+ if column_widths.last&.zero?
column_widths.pop
alignment_indicators.pop
end
[alignment_indicators, column_widths]
end
+ def decorate_line(line, role, counter, decorate)
+ return line unless decorate
+
+ style = decoration_style(line, role, counter, decorate)
+ return line unless style
+
+ AnsiString.new(line).send(style)
+ end
+
+ def decoration_style(role, counter, decorate)
+ return nil unless decorate
+
+ return nil unless (style = decorate[role])
+
+ if style.is_a?(Array)
+ style[counter % style.count]
+ else
+ style
+ end
+ end
+
def determine_column_alignment(cell)
if cell =~ /^-+:$/
:right
elsif cell =~ /^:-+:$/
:center
else
:left
end
end
- def format_rows(rows, alignment_indicators, column_widths, decorate)
- rows.map do |row|
- format_row_line(row, alignment_indicators, column_widths, decorate)
+ def format_cell(cell, align, width)
+ case align
+ when :center
+ cell.center(width)
+ when :right
+ cell.rjust(width)
+ else
+ cell.ljust(width)
end
end
- def format_row_line(row, alignment_indicators, column_widths, decorate,
- text_sym: :text, style_sym: :color)
+ def format_row_line(
+ row, alignment_indicators, column_widths, decorate,
+ text_sym: :text, style_sym: :color
+ )
return '' if row.cells.nil?
border_style = decorate && decorate[:border]
HierarchyString.new(
[{ text_sym => '| ', style_sym => border_style },
@@ -128,49 +104,83 @@
style_sym: style_sym,
text_sym: text_sym
).decorate
end
- def format_cell(cell, align, width)
- case align
- when :center
- cell.center(width)
- when :right
- cell.rjust(width)
- else
- cell.ljust(width)
+ def format_rows(rows, alignment_indicators, column_widths, decorate)
+ rows.map do |row|
+ format_row_line(row, alignment_indicators, column_widths, decorate)
end
end
- def decorate_line(line, role, counter, decorate)
- return line unless decorate
+ def format_table(lines:, column_count:, decorate: nil)
+ unless column_count.positive?
+ return lines.map do |line|
+ HierarchyString.new([{ text: line }])
+ # HierarchyString.new([{ text: line, color: decorate }]) #???
+ end
+ end
- return line unless (style = decoration_style(line, role, counter, decorate))
+ rows = raw_lines_into_row_role_cells(lines, column_count)
- AnsiString.new(line).send(style)
- end
+ alignment_indicators, column_widths =
+ calculate_column_alignment_and_widths(rows, column_count)
- def decoration_style(role, counter, decorate)
- return nil unless decorate
-
- return nil unless (style = decorate[role])
-
- if style.is_a?(Array)
- style[counter % style.count]
- else
- style
- end
+ format_rows(rows, alignment_indicators, column_widths, decorate)
end
def insert_every_other(array, obj)
result = []
array.each_with_index do |element, index|
result << element
result << obj if index < array.size - 1
end
result
end
+
+ def raw_lines_into_row_role_cells(lines, column_count)
+ role = :header_row
+ counter = -1
+
+ ret = []
+ lines.each do |line|
+ line += '|' unless line.end_with?('|')
+ counter += 1
+
+ role = role_for_raw_row(role, line)
+ counter = reset_counter_if_needed(role, counter)
+ cells = split_decorated_row_into_cells(line, column_count)
+ ret << OpenStruct.new(cells: cells, role: role, counter: counter)
+ end
+ ret
+ end
+
+ def reset_counter_if_needed(role, counter)
+ %i[header_row row].include?(role) ? counter : 0
+ end
+
+ def role_for_raw_row(current_role, line)
+ case current_role
+ when :header_row
+ if line =~ /^[ \t]*\| *[:\-][:\- |]*$/
+ :separator_line
+ else
+ current_role
+ end
+ when :separator_line
+ :row
+ when :row
+ current_role
+ else
+ raise "Unexpected role: #{current_role} for line #{line}"
+ end
+ end
+
+ def split_decorated_row_into_cells(line, column_count)
+ cells = line.split('|').map(&:strip)[1..-1]
+ cells&.slice(0, column_count)&.fill('', cells.length...column_count)
+ end
end
return if $PROGRAM_NAME != __FILE__
require 'minitest/autorun'
@@ -182,15 +192,17 @@
'| Header 1 | Header 2 | Header 3 |',
'|----------|:--------:|---------:|',
'| Row 1 Col 1 | Row 1 Col 2 | Row 1 Col 3 |',
'| Row 2 Col 1 | Row 2 Col 2 | Row 2 Col 3 |'
]
- @columns = 3
+ @column_count = 3
end
def test_format_table
- result = MarkdownTableFormatter.format_table(@lines, @columns)
+ result = MarkdownTableFormatter.format_table(
+ column_count: @column_count, lines: @lines
+ )
expected = [
'| Header 1 | Header 2 | Header 3 |',
'| ----------- | ----------- | ----------- |',
'| Row 1 Col 1 | Row 1 Col 2 | Row 1 Col 3 |',
'| Row 2 Col 1 | Row 2 Col 2 | Row 2 Col 3 |'
@@ -198,12 +210,15 @@
assert_equal expected, result
end
def test_format_table_with_decoration
decorate = { header_row: :upcase, row: %i[downcase upcase] }
- result = MarkdownTableFormatter.format_table(@lines, @columns,
- decorate: decorate)
+ result = MarkdownTableFormatter.format_table(
+ column_count: @column_count,
+ decorate: decorate,
+ lines: @lines
+ )
expected = [
'| HEADER 1 | HEADER 2 | HEADER 3 |',
'| ----------- | ----------- | ----------- |',
'| ROW 1 COL 1 | ROW 1 COL 2 | ROW 1 COL 3 |',
'| row 2 col 1 | row 2 col 2 | row 2 col 3 |'
@@ -217,11 +232,14 @@
'|----------|:--------:|---------:|',
'| Row 1 Col 1 | Row 1 Col 2 | Row 1 Col 3 |',
'',
'| Row 2 Col 1 | Row 2 Col 2 | Row 2 Col 3 |'
]
- result = MarkdownTableFormatter.format_table(lines_with_empty, @columns)
+ result = MarkdownTableFormatter.format_table(
+ lines: lines_with_empty,
+ column_count: @column_count
+ )
expected = [
'| Header 1 | Header 2 | Header 3 |',
'| ----------- | ----------- | ----------- |',
'| Row 1 Col 1 | Row 1 Col 2 | Row 1 Col 3 |',
'',
@@ -233,11 +251,14 @@
def test_alignment_detection
lines_with_alignment = [
'| Header 1 | Header 2 | Header 3 |',
'|:-------- |:--------:| --------:|'
]
- result = MarkdownTableFormatter.format_table(lines_with_alignment, @columns)
+ result = MarkdownTableFormatter.format_table(
+ lines: lines_with_alignment,
+ column_count: @column_count
+ )
expected = [
'| Header 1 | Header 2 | Header 3 |',
'| --------- | ---------- | --------- |'
]
assert_equal expected, result[0..1] # only checking the header and separator
@@ -250,78 +271,93 @@
'| Species| Genus| Family',
'|-|-|-',
'| Pongo tapanuliensis| Pongo| Hominidae',
'| | Histiophryne| Antennariidae'
]
- columns = 3
+ column_count = 3
expected = [
'| Species | Genus | Family |',
'| ------------------- | ------------ | ------------- |',
'| Pongo tapanuliensis | Pongo | Hominidae |',
'| | Histiophryne | Antennariidae |'
]
- assert_equal expected, MarkdownTableFormatter.format_table(lines, columns)
+ assert_equal expected, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: column_count
+ )
end
- def test_missing_columns
+ def test_missing_column_count
lines = [
'| A| B| C',
'| 1| 2',
'| X'
]
- columns = 3
+ column_count = 3
expected = [
'| A | B | C |',
'| 1 | 2 | |',
'| X | | |'
]
- assert_equal expected, MarkdownTableFormatter.format_table(lines, columns)
+ assert_equal expected, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: column_count
+ )
end
- # def test_extra_columns
+ # def test_extra_column_count
# lines = [
# "| A| B| C| D",
# "| 1| 2| 3| 4| 5"
# ]
- # columns = 3
+ # column_count = 3
# expected = [
# "| A | B | C ",
# "| 1 | 2 | 3 "
# ]
- # assert_equal expected, MarkdownTableFormatter.format_table(lines, columns)
+ # assert_equal expected, MarkdownTableFormatter.format_table(lines, column_count)
# end
def test_empty_input
- assert_equal [], MarkdownTableFormatter.format_table([], 3)
+ assert_equal [], MarkdownTableFormatter.format_table(
+ lines: [],
+ column_count: 3
+ )
end
def test_single_column
lines = [
'| A',
'| Longer text',
'| Short'
]
- columns = 1
+ column_count = 1
expected = [
'| A |',
'| Longer text |',
'| Short |'
]
- assert_equal expected, MarkdownTableFormatter.format_table(lines, columns)
+ assert_equal expected, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: column_count
+ )
end
def test_no_pipe_at_end
lines = [
'| A| B| C',
'| 1| 2| 3'
]
- columns = 3
+ column_count = 3
expected = [
'| A | B | C |',
'| 1 | 2 | 3 |'
]
- assert_equal expected, MarkdownTableFormatter.format_table(lines, columns)
+ assert_equal expected, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: column_count
+ )
end
end
class TestFormatTable2 < Minitest::Test
def test_basic_formatting
@@ -333,39 +369,48 @@
expected_output = [
'| Name | Age | City |',
'| John | 30 | New York |',
'| Jane | 25 | Los Angeles |'
]
- assert_equal expected_output, MarkdownTableFormatter.format_table(lines, 3)
+ assert_equal expected_output, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: 3
+ )
end
- def test_incomplete_columns
+ def test_incomplete_column_count
lines = [
'| Name | Age |',
'| John | 30 | New York |',
'| Jane | 25 | Los Angeles |'
]
expected_output = [
'| Name | Age | |',
'| John | 30 | New York |',
'| Jane | 25 | Los Angeles |'
]
- assert_equal expected_output, MarkdownTableFormatter.format_table(lines, 3)
+ assert_equal expected_output, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: 3
+ )
end
- def test_extra_columns
+ def test_extra_column_count
lines = [
'| Name | Age | City | Country |',
'| John | 30 | New York | USA |',
'| Jane | 25 | Los Angeles | USA |'
]
expected_output = [
'| Name | Age | City | Country |',
'| John | 30 | New York | USA |',
'| Jane | 25 | Los Angeles | USA |'
]
- assert_equal expected_output, MarkdownTableFormatter.format_table(lines, 4)
+ assert_equal expected_output, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: 4
+ )
end
def test_varied_column_lengths
lines = [
'| Name | Age |',
@@ -375,23 +420,32 @@
expected_output = [
'| Name | Age | |',
'| Johnathan | 30 | New York |',
'| Jane | 25 | LA |'
]
- assert_equal expected_output, MarkdownTableFormatter.format_table(lines, 3)
+ assert_equal expected_output, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: 3
+ )
end
def test_single_line
lines = ['| Name | Age | City |']
expected_output = ['| Name | Age | City |']
- assert_equal expected_output, MarkdownTableFormatter.format_table(lines, 3)
+ assert_equal expected_output, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: 3
+ )
end
def test_empty_lines
lines = []
expected_output = []
- assert_equal expected_output, MarkdownTableFormatter.format_table(lines, 3)
+ assert_equal expected_output, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: 3
+ )
end
def test_complete_rows
lines = [
'| Name | Age |',
@@ -399,8 +453,11 @@
]
expected_output = [
'| Name | Age |',
'| John | 30 |'
]
- assert_equal expected_output, MarkdownTableFormatter.format_table(lines, 3)
+ assert_equal expected_output, MarkdownTableFormatter.format_table(
+ lines: lines,
+ column_count: 3
+ )
end
end