Anand S Katti | 0954135 | 2020-01-29 15:54:01 +0530 | [diff] [blame] | 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 | |
| 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 | package tablewriter |
| 9 | |
| 10 | import ( |
| 11 | "math" |
| 12 | "strings" |
| 13 | |
| 14 | "github.com/mattn/go-runewidth" |
| 15 | ) |
| 16 | |
| 17 | var ( |
| 18 | nl = "\n" |
| 19 | sp = " " |
| 20 | ) |
| 21 | |
| 22 | const defaultPenalty = 1e5 |
| 23 | |
| 24 | // Wrap wraps s into a paragraph of lines of length lim, with minimal |
| 25 | // raggedness. |
| 26 | func WrapString(s string, lim int) ([]string, int) { |
| 27 | words := strings.Split(strings.Replace(s, nl, sp, -1), sp) |
| 28 | var lines []string |
| 29 | max := 0 |
| 30 | for _, v := range words { |
| 31 | max = runewidth.StringWidth(v) |
| 32 | if max > lim { |
| 33 | lim = max |
| 34 | } |
| 35 | } |
| 36 | for _, line := range WrapWords(words, 1, lim, defaultPenalty) { |
| 37 | lines = append(lines, strings.Join(line, sp)) |
| 38 | } |
| 39 | return lines, lim |
| 40 | } |
| 41 | |
| 42 | // WrapWords is the low-level line-breaking algorithm, useful if you need more |
| 43 | // control over the details of the text wrapping process. For most uses, |
| 44 | // WrapString will be sufficient and more convenient. |
| 45 | // |
| 46 | // WrapWords splits a list of words into lines with minimal "raggedness", |
| 47 | // treating each rune as one unit, accounting for spc units between adjacent |
| 48 | // words on each line, and attempting to limit lines to lim units. Raggedness |
| 49 | // is the total error over all lines, where error is the square of the |
| 50 | // difference of the length of the line and lim. Too-long lines (which only |
| 51 | // happen when a single word is longer than lim units) have pen penalty units |
| 52 | // added to the error. |
| 53 | func WrapWords(words []string, spc, lim, pen int) [][]string { |
| 54 | n := len(words) |
| 55 | |
| 56 | length := make([][]int, n) |
| 57 | for i := 0; i < n; i++ { |
| 58 | length[i] = make([]int, n) |
| 59 | length[i][i] = runewidth.StringWidth(words[i]) |
| 60 | for j := i + 1; j < n; j++ { |
| 61 | length[i][j] = length[i][j-1] + spc + runewidth.StringWidth(words[j]) |
| 62 | } |
| 63 | } |
| 64 | nbrk := make([]int, n) |
| 65 | cost := make([]int, n) |
| 66 | for i := range cost { |
| 67 | cost[i] = math.MaxInt32 |
| 68 | } |
| 69 | for i := n - 1; i >= 0; i-- { |
| 70 | if length[i][n-1] <= lim { |
| 71 | cost[i] = 0 |
| 72 | nbrk[i] = n |
| 73 | } else { |
| 74 | for j := i + 1; j < n; j++ { |
| 75 | d := lim - length[i][j-1] |
| 76 | c := d*d + cost[j] |
| 77 | if length[i][j-1] > lim { |
| 78 | c += pen // too-long lines get a worse penalty |
| 79 | } |
| 80 | if c < cost[i] { |
| 81 | cost[i] = c |
| 82 | nbrk[i] = j |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | var lines [][]string |
| 88 | i := 0 |
| 89 | for i < n { |
| 90 | lines = append(lines, words[i:nbrk[i]]) |
| 91 | i = nbrk[i] |
| 92 | } |
| 93 | return lines |
| 94 | } |
| 95 | |
| 96 | // getLines decomposes a multiline string into a slice of strings. |
| 97 | func getLines(s string) []string { |
| 98 | return strings.Split(s, nl) |
| 99 | } |