summary refs log tree commit diff stats
path: root/compiler/asciitables.nim
blob: c25d54bdea0d5c85c20ceb9e1f215233038e56fa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#[
move to std/asciitables.nim once stable, or to a nimble paackage
once compiler can depend on nimble
]#

type Cell* = object
  text*: string
  width*, row*, col*, ncols*, nrows*: int

iterator parseTableCells*(s: string, delim = '\t'): Cell =
  ## iterates over all cells in a `delim`-delimited `s`, after a 1st
  ## pass that computes number of rows, columns, and width of each column.
  var widths: seq[int]
  var cell: Cell
  template update() =
    if widths.len<=cell.col:
      widths.setLen cell.col+1
      widths[cell.col] = cell.width
    else:
      widths[cell.col] = max(widths[cell.col], cell.width)
    cell.width = 0

  for a in s:
    case a
    of '\n':
      update()
      cell.col = 0
      cell.row.inc
    elif a == delim:
      update()
      cell.col.inc
    else:
      # todo: consider multi-width chars when porting to non-ascii implementation
      cell.width.inc
  if s.len > 0 and s[^1] != '\n':
    update()

  cell.ncols = widths.len
  cell.nrows = cell.row + 1
  cell.row = 0
  cell.col = 0
  cell.width = 0

  template update2() =
    cell.width = widths[cell.col]
    yield cell
    cell.text = ""
    cell.width = 0
    cell.col.inc

  template finishRow() =
    for col in cell.col..<cell.ncols:
      cell.col = col
      update2()
    cell.col = 0

  for a in s:
    case a
    of '\n':
      finishRow()
      cell.row.inc
    elif a == delim:
      update2()
    else:
      cell.width.inc
      cell.text.add a

  if s.len > 0 and s[^1] != '\n':
    finishRow()

proc alignTable*(s: string, delim = '\t', fill = ' ', sep = " "): string =
  ## formats a `delim`-delimited `s` representing a table; each cell is aligned
  ## to a width that's computed for each column; consecutive columns are
  ## delimted by `sep`, and alignment space is filled using `fill`.
  ## More customized formatting can be done by calling `parseTableCells` directly.
  for cell in parseTableCells(s, delim):
    result.add cell.text
    for i in cell.text.len..<cell.width:
      result.add fill
    if cell.col < cell.ncols-1:
      result.add sep
    if cell.col == cell.ncols-1 and cell.row < cell.nrows - 1:
      result.add '\n'