blob: cf63eadfc4ebc0e4320691d8a2584e396ce710b9 [file] [log] [blame]
Anand S Katti09541352020-01-29 15:54:01 +05301// Copyright 2014 Oleku Konko All rights reserved.
2// Use of this source code is governed by a MIT
3// license that can be found in the LICENSE file.
4
5// This module is a Table Writer API for the Go Programming Language.
6// The protocols were written in pure Go and works on windows and unix systems
7
8// Create & Generate text based table
9package tablewriter
10
11import (
12 "bytes"
13 "fmt"
14 "io"
15 "regexp"
16 "strings"
17)
18
19const (
20 MAX_ROW_WIDTH = 30
21)
22
23const (
24 CENTER = "+"
25 ROW = "-"
26 COLUMN = "|"
27 SPACE = " "
28 NEWLINE = "\n"
29)
30
31const (
32 ALIGN_DEFAULT = iota
33 ALIGN_CENTER
34 ALIGN_RIGHT
35 ALIGN_LEFT
36)
37
38var (
39 decimal = regexp.MustCompile(`^-?(?:\d{1,3}(?:,\d{3})*|\d+)(?:\.\d+)?$`)
40 percent = regexp.MustCompile(`^-?\d+\.?\d*$%$`)
41)
42
43type Border struct {
44 Left bool
45 Right bool
46 Top bool
47 Bottom bool
48}
49
50type Table struct {
51 out io.Writer
52 rows [][]string
53 lines [][][]string
54 cs map[int]int
55 rs map[int]int
56 headers [][]string
57 footers [][]string
58 caption bool
59 captionText string
60 autoFmt bool
61 autoWrap bool
62 reflowText bool
63 mW int
64 pCenter string
65 pRow string
66 pColumn string
67 tColumn int
68 tRow int
69 hAlign int
70 fAlign int
71 align int
72 newLine string
73 rowLine bool
74 autoMergeCells bool
75 noWhiteSpace bool
76 tablePadding string
77 hdrLine bool
78 borders Border
79 colSize int
80 headerParams []string
81 columnsParams []string
82 footerParams []string
83 columnsAlign []int
84}
85
86// Start New Table
87// Take io.Writer Directly
88func NewWriter(writer io.Writer) *Table {
89 t := &Table{
90 out: writer,
91 rows: [][]string{},
92 lines: [][][]string{},
93 cs: make(map[int]int),
94 rs: make(map[int]int),
95 headers: [][]string{},
96 footers: [][]string{},
97 caption: false,
98 captionText: "Table caption.",
99 autoFmt: true,
100 autoWrap: true,
101 reflowText: true,
102 mW: MAX_ROW_WIDTH,
103 pCenter: CENTER,
104 pRow: ROW,
105 pColumn: COLUMN,
106 tColumn: -1,
107 tRow: -1,
108 hAlign: ALIGN_DEFAULT,
109 fAlign: ALIGN_DEFAULT,
110 align: ALIGN_DEFAULT,
111 newLine: NEWLINE,
112 rowLine: false,
113 hdrLine: true,
114 borders: Border{Left: true, Right: true, Bottom: true, Top: true},
115 colSize: -1,
116 headerParams: []string{},
117 columnsParams: []string{},
118 footerParams: []string{},
119 columnsAlign: []int{}}
120 return t
121}
122
123// Render table output
124func (t *Table) Render() {
125 if t.borders.Top {
126 t.printLine(true)
127 }
128 t.printHeading()
129 if t.autoMergeCells {
130 t.printRowsMergeCells()
131 } else {
132 t.printRows()
133 }
134 if !t.rowLine && t.borders.Bottom {
135 t.printLine(true)
136 }
137 t.printFooter()
138
139 if t.caption {
140 t.printCaption()
141 }
142}
143
144const (
145 headerRowIdx = -1
146 footerRowIdx = -2
147)
148
149// Set table header
150func (t *Table) SetHeader(keys []string) {
151 t.colSize = len(keys)
152 for i, v := range keys {
153 lines := t.parseDimension(v, i, headerRowIdx)
154 t.headers = append(t.headers, lines)
155 }
156}
157
158// Set table Footer
159func (t *Table) SetFooter(keys []string) {
160 //t.colSize = len(keys)
161 for i, v := range keys {
162 lines := t.parseDimension(v, i, footerRowIdx)
163 t.footers = append(t.footers, lines)
164 }
165}
166
167// Set table Caption
168func (t *Table) SetCaption(caption bool, captionText ...string) {
169 t.caption = caption
170 if len(captionText) == 1 {
171 t.captionText = captionText[0]
172 }
173}
174
175// Turn header autoformatting on/off. Default is on (true).
176func (t *Table) SetAutoFormatHeaders(auto bool) {
177 t.autoFmt = auto
178}
179
180// Turn automatic multiline text adjustment on/off. Default is on (true).
181func (t *Table) SetAutoWrapText(auto bool) {
182 t.autoWrap = auto
183}
184
185// Turn automatic reflowing of multiline text when rewrapping. Default is on (true).
186func (t *Table) SetReflowDuringAutoWrap(auto bool) {
187 t.reflowText = auto
188}
189
190// Set the Default column width
191func (t *Table) SetColWidth(width int) {
192 t.mW = width
193}
194
195// Set the minimal width for a column
196func (t *Table) SetColMinWidth(column int, width int) {
197 t.cs[column] = width
198}
199
200// Set the Column Separator
201func (t *Table) SetColumnSeparator(sep string) {
202 t.pColumn = sep
203}
204
205// Set the Row Separator
206func (t *Table) SetRowSeparator(sep string) {
207 t.pRow = sep
208}
209
210// Set the center Separator
211func (t *Table) SetCenterSeparator(sep string) {
212 t.pCenter = sep
213}
214
215// Set Header Alignment
216func (t *Table) SetHeaderAlignment(hAlign int) {
217 t.hAlign = hAlign
218}
219
220// Set Footer Alignment
221func (t *Table) SetFooterAlignment(fAlign int) {
222 t.fAlign = fAlign
223}
224
225// Set Table Alignment
226func (t *Table) SetAlignment(align int) {
227 t.align = align
228}
229
230// Set No White Space
231func (t *Table) SetNoWhiteSpace(allow bool) {
232 t.noWhiteSpace = allow
233}
234
235// Set Table Padding
236func (t *Table) SetTablePadding(padding string) {
237 t.tablePadding = padding
238}
239
240func (t *Table) SetColumnAlignment(keys []int) {
241 for _, v := range keys {
242 switch v {
243 case ALIGN_CENTER:
244 break
245 case ALIGN_LEFT:
246 break
247 case ALIGN_RIGHT:
248 break
249 default:
250 v = ALIGN_DEFAULT
251 }
252 t.columnsAlign = append(t.columnsAlign, v)
253 }
254}
255
256// Set New Line
257func (t *Table) SetNewLine(nl string) {
258 t.newLine = nl
259}
260
261// Set Header Line
262// This would enable / disable a line after the header
263func (t *Table) SetHeaderLine(line bool) {
264 t.hdrLine = line
265}
266
267// Set Row Line
268// This would enable / disable a line on each row of the table
269func (t *Table) SetRowLine(line bool) {
270 t.rowLine = line
271}
272
273// Set Auto Merge Cells
274// This would enable / disable the merge of cells with identical values
275func (t *Table) SetAutoMergeCells(auto bool) {
276 t.autoMergeCells = auto
277}
278
279// Set Table Border
280// This would enable / disable line around the table
281func (t *Table) SetBorder(border bool) {
282 t.SetBorders(Border{border, border, border, border})
283}
284
285func (t *Table) SetBorders(border Border) {
286 t.borders = border
287}
288
289// Append row to table
290func (t *Table) Append(row []string) {
291 rowSize := len(t.headers)
292 if rowSize > t.colSize {
293 t.colSize = rowSize
294 }
295
296 n := len(t.lines)
297 line := [][]string{}
298 for i, v := range row {
299
300 // Detect string width
301 // Detect String height
302 // Break strings into words
303 out := t.parseDimension(v, i, n)
304
305 // Append broken words
306 line = append(line, out)
307 }
308 t.lines = append(t.lines, line)
309}
310
311// Append row to table with color attributes
312func (t *Table) Rich(row []string, colors []Colors) {
313 rowSize := len(t.headers)
314 if rowSize > t.colSize {
315 t.colSize = rowSize
316 }
317
318 n := len(t.lines)
319 line := [][]string{}
320 for i, v := range row {
321
322 // Detect string width
323 // Detect String height
324 // Break strings into words
325 out := t.parseDimension(v, i, n)
326
327 if len(colors) > i {
328 color := colors[i]
329 out[0] = format(out[0], color)
330 }
331
332 // Append broken words
333 line = append(line, out)
334 }
335 t.lines = append(t.lines, line)
336}
337
338// Allow Support for Bulk Append
339// Eliminates repeated for loops
340func (t *Table) AppendBulk(rows [][]string) {
341 for _, row := range rows {
342 t.Append(row)
343 }
344}
345
346// NumLines to get the number of lines
347func (t *Table) NumLines() int {
348 return len(t.lines)
349}
350
351// Clear rows
352func (t *Table) ClearRows() {
353 t.lines = [][][]string{}
354}
355
356// Clear footer
357func (t *Table) ClearFooter() {
358 t.footers = [][]string{}
359}
360
361// Center based on position and border.
362func (t *Table) center(i int) string {
363 if i == -1 && !t.borders.Left {
364 return t.pRow
365 }
366
367 if i == len(t.cs)-1 && !t.borders.Right {
368 return t.pRow
369 }
370
371 return t.pCenter
372}
373
374// Print line based on row width
375func (t *Table) printLine(nl bool) {
376 fmt.Fprint(t.out, t.center(-1))
377 for i := 0; i < len(t.cs); i++ {
378 v := t.cs[i]
379 fmt.Fprintf(t.out, "%s%s%s%s",
380 t.pRow,
381 strings.Repeat(string(t.pRow), v),
382 t.pRow,
383 t.center(i))
384 }
385 if nl {
386 fmt.Fprint(t.out, t.newLine)
387 }
388}
389
390// Print line based on row width with our without cell separator
391func (t *Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) {
392 fmt.Fprint(t.out, t.pCenter)
393 for i := 0; i < len(t.cs); i++ {
394 v := t.cs[i]
395 if i > len(displayCellSeparator) || displayCellSeparator[i] {
396 // Display the cell separator
397 fmt.Fprintf(t.out, "%s%s%s%s",
398 t.pRow,
399 strings.Repeat(string(t.pRow), v),
400 t.pRow,
401 t.pCenter)
402 } else {
403 // Don't display the cell separator for this cell
404 fmt.Fprintf(t.out, "%s%s",
405 strings.Repeat(" ", v+2),
406 t.pCenter)
407 }
408 }
409 if nl {
410 fmt.Fprint(t.out, t.newLine)
411 }
412}
413
414// Return the PadRight function if align is left, PadLeft if align is right,
415// and Pad by default
416func pad(align int) func(string, string, int) string {
417 padFunc := Pad
418 switch align {
419 case ALIGN_LEFT:
420 padFunc = PadRight
421 case ALIGN_RIGHT:
422 padFunc = PadLeft
423 }
424 return padFunc
425}
426
427// Print heading information
428func (t *Table) printHeading() {
429 // Check if headers is available
430 if len(t.headers) < 1 {
431 return
432 }
433
434 // Identify last column
435 end := len(t.cs) - 1
436
437 // Get pad function
438 padFunc := pad(t.hAlign)
439
440 // Checking for ANSI escape sequences for header
441 is_esc_seq := false
442 if len(t.headerParams) > 0 {
443 is_esc_seq = true
444 }
445
446 // Maximum height.
447 max := t.rs[headerRowIdx]
448
449 // Print Heading
450 for x := 0; x < max; x++ {
451 // Check if border is set
452 // Replace with space if not set
453 if !t.noWhiteSpace {
454 fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
455 }
456
457 for y := 0; y <= end; y++ {
458 v := t.cs[y]
459 h := ""
460
461 if y < len(t.headers) && x < len(t.headers[y]) {
462 h = t.headers[y][x]
463 }
464 if t.autoFmt {
465 h = Title(h)
466 }
467 pad := ConditionString((y == end && !t.borders.Left), SPACE, t.pColumn)
468 if t.noWhiteSpace {
469 pad = ConditionString((y == end && !t.borders.Left), SPACE, t.tablePadding)
470 }
471 if is_esc_seq {
472 if !t.noWhiteSpace {
473 fmt.Fprintf(t.out, " %s %s",
474 format(padFunc(h, SPACE, v),
475 t.headerParams[y]), pad)
476 } else {
477 fmt.Fprintf(t.out, "%s %s",
478 format(padFunc(h, SPACE, v),
479 t.headerParams[y]), pad)
480 }
481 } else {
482 if !t.noWhiteSpace {
483 fmt.Fprintf(t.out, " %s %s",
484 padFunc(h, SPACE, v),
485 pad)
486 } else {
487 // the spaces between breaks the kube formatting
488 fmt.Fprintf(t.out, "%s%s",
489 padFunc(h, SPACE, v),
490 pad)
491 }
492 }
493 }
494 // Next line
495 fmt.Fprint(t.out, t.newLine)
496 }
497 if t.hdrLine {
498 t.printLine(true)
499 }
500}
501
502// Print heading information
503func (t *Table) printFooter() {
504 // Check if headers is available
505 if len(t.footers) < 1 {
506 return
507 }
508
509 // Only print line if border is not set
510 if !t.borders.Bottom {
511 t.printLine(true)
512 }
513
514 // Identify last column
515 end := len(t.cs) - 1
516
517 // Get pad function
518 padFunc := pad(t.fAlign)
519
520 // Checking for ANSI escape sequences for header
521 is_esc_seq := false
522 if len(t.footerParams) > 0 {
523 is_esc_seq = true
524 }
525
526 // Maximum height.
527 max := t.rs[footerRowIdx]
528
529 // Print Footer
530 erasePad := make([]bool, len(t.footers))
531 for x := 0; x < max; x++ {
532 // Check if border is set
533 // Replace with space if not set
534 fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
535
536 for y := 0; y <= end; y++ {
537 v := t.cs[y]
538 f := ""
539 if y < len(t.footers) && x < len(t.footers[y]) {
540 f = t.footers[y][x]
541 }
542 if t.autoFmt {
543 f = Title(f)
544 }
545 pad := ConditionString((y == end && !t.borders.Top), SPACE, t.pColumn)
546
547 if erasePad[y] || (x == 0 && len(f) == 0) {
548 pad = SPACE
549 erasePad[y] = true
550 }
551
552 if is_esc_seq {
553 fmt.Fprintf(t.out, " %s %s",
554 format(padFunc(f, SPACE, v),
555 t.footerParams[y]), pad)
556 } else {
557 fmt.Fprintf(t.out, " %s %s",
558 padFunc(f, SPACE, v),
559 pad)
560 }
561
562 //fmt.Fprintf(t.out, " %s %s",
563 // padFunc(f, SPACE, v),
564 // pad)
565 }
566 // Next line
567 fmt.Fprint(t.out, t.newLine)
568 //t.printLine(true)
569 }
570
571 hasPrinted := false
572
573 for i := 0; i <= end; i++ {
574 v := t.cs[i]
575 pad := t.pRow
576 center := t.pCenter
577 length := len(t.footers[i][0])
578
579 if length > 0 {
580 hasPrinted = true
581 }
582
583 // Set center to be space if length is 0
584 if length == 0 && !t.borders.Right {
585 center = SPACE
586 }
587
588 // Print first junction
589 if i == 0 {
590 if length > 0 && !t.borders.Left {
591 center = t.pRow
592 }
593 fmt.Fprint(t.out, center)
594 }
595
596 // Pad With space of length is 0
597 if length == 0 {
598 pad = SPACE
599 }
600 // Ignore left space as it has printed before
601 if hasPrinted || t.borders.Left {
602 pad = t.pRow
603 center = t.pCenter
604 }
605
606 // Change Center end position
607 if center != SPACE {
608 if i == end && !t.borders.Right {
609 center = t.pRow
610 }
611 }
612
613 // Change Center start position
614 if center == SPACE {
615 if i < end && len(t.footers[i+1][0]) != 0 {
616 if !t.borders.Left {
617 center = t.pRow
618 } else {
619 center = t.pCenter
620 }
621 }
622 }
623
624 // Print the footer
625 fmt.Fprintf(t.out, "%s%s%s%s",
626 pad,
627 strings.Repeat(string(pad), v),
628 pad,
629 center)
630
631 }
632
633 fmt.Fprint(t.out, t.newLine)
634}
635
636// Print caption text
637func (t Table) printCaption() {
638 width := t.getTableWidth()
639 paragraph, _ := WrapString(t.captionText, width)
640 for linecount := 0; linecount < len(paragraph); linecount++ {
641 fmt.Fprintln(t.out, paragraph[linecount])
642 }
643}
644
645// Calculate the total number of characters in a row
646func (t Table) getTableWidth() int {
647 var chars int
648 for _, v := range t.cs {
649 chars += v
650 }
651
652 // Add chars, spaces, seperators to calculate the total width of the table.
653 // ncols := t.colSize
654 // spaces := ncols * 2
655 // seps := ncols + 1
656
657 return (chars + (3 * t.colSize) + 2)
658}
659
660func (t Table) printRows() {
661 for i, lines := range t.lines {
662 t.printRow(lines, i)
663 }
664}
665
666func (t *Table) fillAlignment(num int) {
667 if len(t.columnsAlign) < num {
668 t.columnsAlign = make([]int, num)
669 for i := range t.columnsAlign {
670 t.columnsAlign[i] = t.align
671 }
672 }
673}
674
675// Print Row Information
676// Adjust column alignment based on type
677
678func (t *Table) printRow(columns [][]string, rowIdx int) {
679 // Get Maximum Height
680 max := t.rs[rowIdx]
681 total := len(columns)
682
683 // TODO Fix uneven col size
684 // if total < t.colSize {
685 // for n := t.colSize - total; n < t.colSize ; n++ {
686 // columns = append(columns, []string{SPACE})
687 // t.cs[n] = t.mW
688 // }
689 //}
690
691 // Pad Each Height
692 pads := []int{}
693
694 // Checking for ANSI escape sequences for columns
695 is_esc_seq := false
696 if len(t.columnsParams) > 0 {
697 is_esc_seq = true
698 }
699 t.fillAlignment(total)
700
701 for i, line := range columns {
702 length := len(line)
703 pad := max - length
704 pads = append(pads, pad)
705 for n := 0; n < pad; n++ {
706 columns[i] = append(columns[i], " ")
707 }
708 }
709 //fmt.Println(max, "\n")
710 for x := 0; x < max; x++ {
711 for y := 0; y < total; y++ {
712
713 // Check if border is set
714 if !t.noWhiteSpace {
715 fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
716 fmt.Fprintf(t.out, SPACE)
717 }
718
719 str := columns[y][x]
720
721 // Embedding escape sequence with column value
722 if is_esc_seq {
723 str = format(str, t.columnsParams[y])
724 }
725
726 // This would print alignment
727 // Default alignment would use multiple configuration
728 switch t.columnsAlign[y] {
729 case ALIGN_CENTER: //
730 fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
731 case ALIGN_RIGHT:
732 fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
733 case ALIGN_LEFT:
734 fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
735 default:
736 if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
737 fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
738 } else {
739 fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
740
741 // TODO Custom alignment per column
742 //if max == 1 || pads[y] > 0 {
743 // fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
744 //} else {
745 // fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
746 //}
747
748 }
749 }
750 if !t.noWhiteSpace {
751 fmt.Fprintf(t.out, SPACE)
752 } else {
753 fmt.Fprintf(t.out, t.tablePadding)
754 }
755 }
756 // Check if border is set
757 // Replace with space if not set
758 if !t.noWhiteSpace {
759 fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
760 }
761 fmt.Fprint(t.out, t.newLine)
762 }
763
764 if t.rowLine {
765 t.printLine(true)
766 }
767}
768
769// Print the rows of the table and merge the cells that are identical
770func (t *Table) printRowsMergeCells() {
771 var previousLine []string
772 var displayCellBorder []bool
773 var tmpWriter bytes.Buffer
774 for i, lines := range t.lines {
775 // We store the display of the current line in a tmp writer, as we need to know which border needs to be print above
776 previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine)
777 if i > 0 { //We don't need to print borders above first line
778 if t.rowLine {
779 t.printLineOptionalCellSeparators(true, displayCellBorder)
780 }
781 }
782 tmpWriter.WriteTo(t.out)
783 }
784 //Print the end of the table
785 if t.rowLine {
786 t.printLine(true)
787 }
788}
789
790// Print Row Information to a writer and merge identical cells.
791// Adjust column alignment based on type
792
793func (t *Table) printRowMergeCells(writer io.Writer, columns [][]string, rowIdx int, previousLine []string) ([]string, []bool) {
794 // Get Maximum Height
795 max := t.rs[rowIdx]
796 total := len(columns)
797
798 // Pad Each Height
799 pads := []int{}
800
801 // Checking for ANSI escape sequences for columns
802 is_esc_seq := false
803 if len(t.columnsParams) > 0 {
804 is_esc_seq = true
805 }
806 for i, line := range columns {
807 length := len(line)
808 pad := max - length
809 pads = append(pads, pad)
810 for n := 0; n < pad; n++ {
811 columns[i] = append(columns[i], " ")
812 }
813 }
814
815 var displayCellBorder []bool
816 t.fillAlignment(total)
817 for x := 0; x < max; x++ {
818 for y := 0; y < total; y++ {
819
820 // Check if border is set
821 fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
822
823 fmt.Fprintf(writer, SPACE)
824
825 str := columns[y][x]
826
827 // Embedding escape sequence with column value
828 if is_esc_seq {
829 str = format(str, t.columnsParams[y])
830 }
831
832 if t.autoMergeCells {
833 //Store the full line to merge mutli-lines cells
834 fullLine := strings.TrimRight(strings.Join(columns[y], " "), " ")
835 if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" {
836 // If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty.
837 displayCellBorder = append(displayCellBorder, false)
838 str = ""
839 } else {
840 // First line or different content, keep the content and print the cell border
841 displayCellBorder = append(displayCellBorder, true)
842 }
843 }
844
845 // This would print alignment
846 // Default alignment would use multiple configuration
847 switch t.columnsAlign[y] {
848 case ALIGN_CENTER: //
849 fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y]))
850 case ALIGN_RIGHT:
851 fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
852 case ALIGN_LEFT:
853 fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
854 default:
855 if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
856 fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
857 } else {
858 fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
859 }
860 }
861 fmt.Fprintf(writer, SPACE)
862 }
863 // Check if border is set
864 // Replace with space if not set
865 fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE))
866 fmt.Fprint(writer, t.newLine)
867 }
868
869 //The new previous line is the current one
870 previousLine = make([]string, total)
871 for y := 0; y < total; y++ {
872 previousLine[y] = strings.TrimRight(strings.Join(columns[y], " "), " ") //Store the full line for multi-lines cells
873 }
874 //Returns the newly added line and wether or not a border should be displayed above.
875 return previousLine, displayCellBorder
876}
877
878func (t *Table) parseDimension(str string, colKey, rowKey int) []string {
879 var (
880 raw []string
881 maxWidth int
882 )
883
884 raw = getLines(str)
885 maxWidth = 0
886 for _, line := range raw {
887 if w := DisplayWidth(line); w > maxWidth {
888 maxWidth = w
889 }
890 }
891
892 // If wrapping, ensure that all paragraphs in the cell fit in the
893 // specified width.
894 if t.autoWrap {
895 // If there's a maximum allowed width for wrapping, use that.
896 if maxWidth > t.mW {
897 maxWidth = t.mW
898 }
899
900 // In the process of doing so, we need to recompute maxWidth. This
901 // is because perhaps a word in the cell is longer than the
902 // allowed maximum width in t.mW.
903 newMaxWidth := maxWidth
904 newRaw := make([]string, 0, len(raw))
905
906 if t.reflowText {
907 // Make a single paragraph of everything.
908 raw = []string{strings.Join(raw, " ")}
909 }
910 for i, para := range raw {
911 paraLines, _ := WrapString(para, maxWidth)
912 for _, line := range paraLines {
913 if w := DisplayWidth(line); w > newMaxWidth {
914 newMaxWidth = w
915 }
916 }
917 if i > 0 {
918 newRaw = append(newRaw, " ")
919 }
920 newRaw = append(newRaw, paraLines...)
921 }
922 raw = newRaw
923 maxWidth = newMaxWidth
924 }
925
926 // Store the new known maximum width.
927 v, ok := t.cs[colKey]
928 if !ok || v < maxWidth || v == 0 {
929 t.cs[colKey] = maxWidth
930 }
931
932 // Remember the number of lines for the row printer.
933 h := len(raw)
934 v, ok = t.rs[rowKey]
935
936 if !ok || v < h || v == 0 {
937 t.rs[rowKey] = h
938 }
939 //fmt.Printf("Raw %+v %d\n", raw, len(raw))
940 return raw
941}