From cc573e093e2a32037e5c255723753a62f2cc796a Mon Sep 17 00:00:00 2001 From: Taylor Thurlow Date: Thu, 25 Jun 2020 21:29:06 -0700 Subject: [PATCH] Add some more --- src/thicket/column.cr | 204 ++++++++++++++++++++++++++++++++++++++++++ src/thicket/graph.cr | 125 +------------------------- 2 files changed, 205 insertions(+), 124 deletions(-) create mode 100644 src/thicket/column.cr diff --git a/src/thicket/column.cr b/src/thicket/column.cr new file mode 100644 index 0000000..9c1914f --- /dev/null +++ b/src/thicket/column.cr @@ -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 diff --git a/src/thicket/graph.cr b/src/thicket/graph.cr index 200a603..b1d05d7 100644 --- a/src/thicket/graph.cr +++ b/src/thicket/graph.cr @@ -10,7 +10,7 @@ module Thicket end def initialize(revs) - @commit = nil + @commit : Commit # the commit currently being processed @revs = revs @num_parents = 0 @expansion_row = 0 @@ -28,127 +28,4 @@ module Thicket @columns = Array(Column) end end - - class Column - property :string_list - - 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 - ) - 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) : Boolean - 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 : Boolean - ) - end - - def rows_before_cols? : Boolean - @rows_before_cols - end - - def cols_before_rows? : Boolean - !rows_before_cols? - end - end - end end