table.go 22 KB

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