@@ -58,6 +58,9 @@ print(Header, Spec, Rows) ->
58
58
autosize_create_table (Schema , Rows ) ->
59
59
autosize_create_table (Schema , Rows , []).
60
60
61
+ % % Currently the only constraint supported in the proplist is
62
+ % % `fixed_width' with a list of columns that *must not* be shrunk
63
+ % % (e.g., integer values). First column is 0.
61
64
-spec autosize_create_table ([any ()], [[any ()]], [tuple ()]) -> iolist ().
62
65
autosize_create_table (Schema , Rows , Constraints ) ->
63
66
BorderSize = 1 + length (hd (Rows )),
@@ -94,31 +97,40 @@ create_table(Spec, [], _Length, IoList) ->
94
97
create_table (Spec , [Row | Rows ], Length , IoList ) ->
95
98
create_table (Spec , Rows , Length , [row (Spec , Row ) | IoList ]).
96
99
100
+ % % Measure and shrink table width as necessary to fit the console
97
101
-spec get_field_widths (pos_integer (), [term ()], [non_neg_integer ()]) -> [non_neg_integer ()].
98
102
get_field_widths (MaxLineLen , Rows , Unshrinkable ) ->
99
103
Widths = max_widths (Rows ),
100
- strip_fields (MaxLineLen , Widths , Unshrinkable ).
104
+ fit_widths_to_terminal (MaxLineLen , Widths , Unshrinkable ).
101
105
102
- strip_fields (MaxWidth , Widths , Unshrinkable ) ->
106
+ fit_widths_to_terminal (MaxWidth , Widths , Unshrinkable ) ->
103
107
Sum = lists :sum (Widths ),
104
108
Weights = calculate_field_weights (Sum , Widths , Unshrinkable ),
105
109
MustRemove = Sum - MaxWidth ,
106
- new_widths (MaxWidth , MustRemove , Widths , Weights ).
107
-
110
+ calculate_new_widths (MaxWidth , MustRemove , Widths , Weights ).
111
+
112
+ % % Determine field weighting as proportion of total width of the
113
+ % % table. Fields which were flagged as unshrinkable will be given a
114
+ % % weight of 0.
115
+ -spec calculate_field_weights (pos_integer (), list (pos_integer ()),
116
+ list (non_neg_integer ())) ->
117
+ list (number ()).
108
118
calculate_field_weights (Sum , Widths , []) ->
119
+ % % If no fields are constrained as unshrinkable, simply divide
120
+ % % each width by the sum of all widths for our proportions
109
121
lists :map (fun (X ) -> X / Sum end , Widths );
110
122
calculate_field_weights (_Sum , Widths , Unshrinkable ) ->
111
- % % Any column numbers represented in `Unshrinkable' will be given
112
- % % a weight of 0 and all other weights will be correspondingly
113
- % % higher
114
- NewWidths = flag_unshrinkable_widths (Widths , Unshrinkable ),
115
- NewSum = lists :sum (lists :filter (fun ({_X , noshrink }) -> false ;
116
- (_X ) -> true end ,
117
- NewWidths )),
123
+ TaggedWidths = flag_unshrinkable_widths (Widths , Unshrinkable ),
124
+ ShrinkableWidth = lists :sum (lists :filter (fun ({_X , noshrink }) -> false ;
125
+ (_X ) -> true end ,
126
+ TaggedWidths )),
118
127
lists :map (fun ({_X , noshrink }) -> 0 ;
119
- (X ) -> X / NewSum end ,
120
- NewWidths ).
128
+ (X ) -> X / ShrinkableWidth end ,
129
+ TaggedWidths ).
121
130
131
+ % % Takes a list of column widths and a list of (zero-based) index
132
+ % % values of the columns that must not shrink. Returns a mixed list of
133
+ % % widths and `noshrink' tuples.
122
134
flag_unshrinkable_widths (Widths , NoShrink ) ->
123
135
{_ , NewWidths } =
124
136
lists :foldl (fun (X , {Idx , Mapped }) ->
@@ -131,6 +143,8 @@ flag_unshrinkable_widths(Widths, NoShrink) ->
131
143
end , {0 , []}, Widths ),
132
144
lists :reverse (NewWidths ).
133
145
146
+ % % Calculate the proportional weight for each column for shrinking.
147
+ % % Zip the results into a `{Width, Weight, Index}' tuple list.
134
148
column_zip (Widths , Weights , ToNarrow ) ->
135
149
column_zip (Widths , Weights , ToNarrow , 0 , []).
136
150
@@ -141,36 +155,38 @@ column_zip([Width|Widths], [Weight|Weights], ToNarrow, Index, Accum) ->
141
155
column_zip (Widths , Weights , ToNarrow , Index + 1 ,
142
156
[{NewWidth , Weight , Index }] ++ Accum ).
143
157
144
- new_widths (_Max , ToNarrow , Widths , _Weights ) when ToNarrow =< 0 ->
158
+ % % Given the widths based on data to be displayed, return widths
159
+ % % necessary to narrow the table to fit the console.
160
+ calculate_new_widths (_Max , ToNarrow , Widths , _Weights ) when ToNarrow =< 0 ->
161
+ % % Console is wide enough, no need to narrow
145
162
Widths ;
146
- new_widths (MaxWidth , ToNarrow , Widths , Weights ) ->
147
- tweak_widths (MaxWidth , column_zip (Widths , Weights , ToNarrow )).
163
+ calculate_new_widths (MaxWidth , ToNarrow , Widths , Weights ) ->
164
+ fix_rounding (MaxWidth , column_zip (Widths , Weights , ToNarrow )).
148
165
149
166
% % Rounding may introduce an error. If so, remove the requisite number
150
167
% % of spaces from the widest field
151
- tweak_widths (Target , Cols ) ->
168
+ fix_rounding (Target , Cols ) ->
152
169
Widths = lists :map (fun ({Width , _Weight , _Idx }) -> Width end ,
153
170
Cols ),
154
171
SumWidths = lists :sum (Widths ),
155
172
shrink_widest (Target , SumWidths , Widths , Cols ).
156
173
157
- % % If our target table width is narrower than our calculated width,
158
- % % look for the widest column with a non-zero weight (zero weights are
159
- % % constrained to not be narrowed) and shrink it by the necessary
160
- % % value.
174
+ % % Determine whether our target table width is wider than the terminal
175
+ % % due to any rounding error and find columns eligible to be shrunk.
161
176
shrink_widest (Target , Current , Widths , _Cols ) when Target =< Current ->
162
177
Widths ;
163
178
shrink_widest (Target , Current , Widths , Cols ) ->
164
179
Gap = Current - Target ,
165
180
NonZeroWeighted = lists :dropwhile (fun ({_Width , 0 , _Idx }) -> true ;
166
181
(_ ) -> false end ,
167
182
Cols ),
168
- shrink_nonzero_widest (Gap , NonZeroWeighted , Widths ).
169
-
183
+ shrink_widest_weighted (Gap , NonZeroWeighted , Widths ).
170
184
171
- shrink_nonzero_widest (_Gap , [], Widths ) ->
185
+ % % Take the widest column with a non-zero weight and reduce it by the
186
+ % % amount necessary to compensate for any rounding error.
187
+ shrink_widest_weighted (_Gap , [], Widths ) ->
172
188
Widths ; % % All columns constrained to fixed widths, nothing we can do
173
- shrink_nonzero_widest (Gap , Cols , Widths ) ->
189
+ shrink_widest_weighted (Gap , Cols , Widths ) ->
174
190
SortedCols = lists :sort (
175
191
fun ({WidthA , _WeightA , _IdxA }, {WidthB , _WeightB , _IdxB }) ->
176
192
WidthA > WidthB
@@ -179,7 +195,9 @@ shrink_nonzero_widest(Gap, Cols, Widths) ->
179
195
NewWidth = ? MINWIDTH (OldWidth - Gap ),
180
196
replace_list_element (Idx , NewWidth , Widths ).
181
197
182
- % % Zero-based indexing. Deal with it
198
+ % % Replace the item at `Index' in `List' with `Element'.
199
+ % % Zero-based indexing.
200
+ -spec replace_list_element (non_neg_integer (), term (), list ()) -> list ().
183
201
replace_list_element (Index , Element , List ) ->
184
202
{Prefix , Suffix } = lists :split (Index , List ),
185
203
Prefix ++ [Element ] ++ tl (Suffix ).
0 commit comments