-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dae011a
commit cc573e0
Showing
2 changed files
with
205 additions
and
124 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
class Column | ||
property :string_list | ||
|
||
enum Layout | ||
Plain | ||
Column | ||
Row | ||
end | ||
|
||
enum Enable | ||
Always | ||
Never | ||
Auto | ||
end | ||
|
||
def initialize( | ||
@string_list : Array(String), | ||
@column_options : ColumnOptions, | ||
@rows : UInt32, | ||
@cols : UInt32, | ||
@cell_length : Array(UInt32), # len | ||
@width : Array(UInt16), # index to longest row in column | ||
@layout : Layout, | ||
@enable : Enable, | ||
@dense : Bool | ||
) | ||
end | ||
|
||
private def compute_column_width | ||
@cols.each_with_index do |col, x| | ||
@width[x] = x_y_to_linear(x, 0) | ||
@rows.each_with_index do |row, y| | ||
i = x_y_to_linear(x, y) | ||
@width[x] = i if i < @string_list.size && @cell_length[@width[x]] < @cell_length[i] | ||
end | ||
end | ||
end | ||
|
||
# Shrink all columns by shortening them one row each time (and adding more | ||
# columns along the way). Hopefully the longest cell will be moved to the | ||
# next column, column is shrunk so we have more space for new columns. The | ||
# process ends when the whole thing no longer fits in total_width. | ||
def shrink_columns | ||
while @rows > 1 | ||
rows = @rows | ||
cols = @cols | ||
@rows -= 1 | ||
@cols = (@string_list.size + @rows - 1) / @rows | ||
|
||
compute_column_width if @cols != cols | ||
|
||
total_width = @column_options.indent | ||
|
||
@cols.each_with_index do |col, x| | ||
total_width += @cell_length[@width[x]] | ||
total_width += @column_options.padding | ||
end | ||
|
||
if total_width > @column_options.width | ||
@rows = rows | ||
@cols = cols | ||
break | ||
end | ||
end | ||
|
||
compute_column_width | ||
end | ||
|
||
def print_columns(string_list : Array(String), column_options : ColumnOptions) | ||
return if string_list.empty? | ||
|
||
# raise "fail" unless col_auto_enabled | ||
|
||
nopts = ColumnOptions.new( | ||
indent: column_options.indent || "", | ||
newline: column_options.newline || "\n", | ||
padding: column_options.padding || 1, | ||
width: column_options.width || (term_columns - 1), | ||
) | ||
|
||
if @enable == Enable::Always | ||
display_plain(string_list, "", "\n") | ||
return | ||
end | ||
|
||
case @layout | ||
when Layout::Plain | ||
display_plain(list, nopts.indent, nopts.newline) | ||
when Layout::Row, Layout::Column | ||
display_table(list, nopts) | ||
else | ||
raise "Invalid layout mode: #{@layout}" | ||
end | ||
end | ||
|
||
private def display_plain(string_list : Array(String), indent : String, newline : String) | ||
string_list.each do |s| | ||
STDOUT.printf( | ||
"%s%s%s", | ||
indent, | ||
string_list[i], | ||
newline | ||
) | ||
end | ||
end | ||
|
||
# Print a cell to STDOUT with all necessary leading/trailing space | ||
private def display_cell(initial_width : UInt16, empty_cell : String, x : UInt32, y : UInt32) : Bool | ||
i = x_y_to_linear(x, y) | ||
return false if i >= @string_list.size | ||
|
||
length = @cell_length[i] | ||
if (@width && @cell_length[width[x]]) < initial_width | ||
length += initial_width - @cell_length[width[x]] | ||
length -= @column_options.padding | ||
end | ||
|
||
newline = if @column_options.cols_before_rows? | ||
i + @rows >= @string_list.size | ||
else | ||
x == @cols - 1 || i == @string_list.size - 1 | ||
end | ||
|
||
STDOUT.printf( | ||
"%s%s%s", | ||
x == 0 ? @column_options.indent : "", | ||
@string_list[i], | ||
newline ? @column_options.newline : empty_cell + length | ||
) | ||
|
||
return true | ||
end | ||
|
||
private def display_table(string_list : Array(String), column_options : ColumnOptions) | ||
column = Column.new( | ||
string_list: string_list, | ||
column_options: column_options, | ||
cell_length: string_list.map { |s| self.string_length_without_ansi(s) }, | ||
) | ||
|
||
initial_width = 0 | ||
layout(column, initial_width) | ||
|
||
# if COL_DENSE then shrink_columns(column) | ||
|
||
@rows.each_with_index do |row, y| | ||
@cols.each do |col, x| | ||
break if display_cell(column, initial_width, " ", x, y) | ||
end | ||
end | ||
end | ||
|
||
# Calculate cell width, rows, and columns for a table of equal cells, given | ||
# table width and how many spaces between the cells. | ||
private def layout(width : UInt16 = 0) | ||
width = @cell_length.max | ||
width += @column_options.padding | ||
|
||
@cols = (@column_options.width - @column_options.indent.size) / width | ||
@cols = 1 if @cols == 0 | ||
|
||
@rows = (@string_list.size + @cols - 1) / @cols | ||
end | ||
|
||
private def x_y_to_linear(x : UInt32, y : UInt32) : UInt32 | ||
if @column_options.cols_before_rows? | ||
x * @rows + y | ||
else | ||
y * @cols + x | ||
end | ||
end | ||
|
||
COL_LAYOUT_MASK = 0x000F | ||
COL_COLUMN = 0 # fill columns before rows | ||
COL_ROW = 1 # fill rows before columns | ||
COL_PLAIN = 15 # one column | ||
|
||
ANSI_ESCAPE_REGEX = /(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]/ | ||
|
||
private def self.string_length_without_ansi(string : String) : UInt32 | ||
string.gsub(ANSI_ESCAPE_REGEX, "").size | ||
end | ||
|
||
struct ColumnOptions | ||
property :width, :padding, :indent, :newline, :rows_before_cols | ||
|
||
def initialize( | ||
@width : UInt8, | ||
@padding : UInt8, | ||
@indent : String, | ||
@newline : String, | ||
@rows_before_cols : Bool | ||
) | ||
end | ||
|
||
def rows_before_cols? : Bool | ||
@rows_before_cols | ||
end | ||
|
||
def cols_before_rows? : Bool | ||
!rows_before_cols? | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters