mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 05:50:30 +00:00
add alignTable, parseTableCells to align/format a tab(etc) delimited table (#10182)
* add compiler/unittest_light.nim for easy diffing: assertEquals and mismatch * fixup * add alignTable, parseTableCells
This commit is contained in:
committed by
Andreas Rumpf
parent
258952832f
commit
23c1ee982e
83
compiler/asciitables.nim
Normal file
83
compiler/asciitables.nim
Normal file
@@ -0,0 +1,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'
|
||||
37
compiler/unittest_light.nim
Normal file
37
compiler/unittest_light.nim
Normal file
@@ -0,0 +1,37 @@
|
||||
# note: consider merging tests/assert/testhelper.nim here.
|
||||
|
||||
proc mismatch*[T](lhs: T, rhs: T): string =
|
||||
## Simplified version of `unittest.require` that satisfies a common use case,
|
||||
## while avoiding pulling too many dependencies. On failure, diagnostic
|
||||
## information is provided that in particular makes it easy to spot
|
||||
## whitespace mismatches and where the mismatch is.
|
||||
proc replaceInvisible(s: string): string =
|
||||
for a in s:
|
||||
case a
|
||||
of '\n': result.add "\\n\n"
|
||||
else: result.add a
|
||||
|
||||
proc quoted(s: string): string = result.addQuoted s
|
||||
|
||||
result.add "\n"
|
||||
result.add "lhs:{\n" & replaceInvisible(
|
||||
$lhs) & "}\nrhs:{\n" & replaceInvisible($rhs) & "}\n"
|
||||
when compiles(lhs.len):
|
||||
if lhs.len != rhs.len:
|
||||
result.add "lhs.len: " & $lhs.len & " rhs.len: " & $rhs.len & "\n"
|
||||
when compiles(lhs[0]):
|
||||
var i = 0
|
||||
while i < lhs.len and i < rhs.len:
|
||||
if lhs[i] != rhs[i]: break
|
||||
i.inc
|
||||
result.add "first mismatch index: " & $i & "\n"
|
||||
if i < lhs.len and i < rhs.len:
|
||||
result.add "lhs[i]: {" & quoted($lhs[i]) & "} rhs[i]: {" & quoted(
|
||||
$rhs[i]) & "}"
|
||||
result.add "lhs[0..<i]:{\n" & replaceInvisible($lhs[
|
||||
0..<i]) & "}\nrhs[0..<i]:{\n" & replaceInvisible($rhs[0..<i]) & "}"
|
||||
|
||||
proc assertEquals*[T](lhs: T, rhs: T) =
|
||||
when false: # can be useful for debugging to see all that's fed to this.
|
||||
echo "----" & $lhs
|
||||
doAssert lhs==rhs, mismatch(lhs, rhs)
|
||||
7
tests/compiler/nim.cfg
Normal file
7
tests/compiler/nim.cfg
Normal file
@@ -0,0 +1,7 @@
|
||||
# note: consider moving tests/compilerapi/ to tests/compiler/ since
|
||||
# that's more predictable.
|
||||
|
||||
# note: without this, tests may succeed locally but fail on CI (it can succeed
|
||||
# locally even when compiling via `./bin/nim` because `$HOME/.nimble` is being
|
||||
# used).
|
||||
--path:"../../" # so we can `import compiler/foo` in this dir
|
||||
109
tests/compiler/tasciitables.nim
Normal file
109
tests/compiler/tasciitables.nim
Normal file
@@ -0,0 +1,109 @@
|
||||
import compiler/unittest_light
|
||||
import compiler/asciitables
|
||||
|
||||
import strformat
|
||||
|
||||
proc alignTableCustom(s: string, delim = '\t', sep = ","): string =
|
||||
for cell in parseTableCells(s, delim):
|
||||
result.add fmt"({cell.row},{cell.col}): "
|
||||
for i in cell.text.len..<cell.width:
|
||||
result.add " "
|
||||
result.add cell.text
|
||||
if cell.col < cell.ncols-1:
|
||||
result.add sep
|
||||
if cell.col == cell.ncols-1 and cell.row < cell.nrows - 1:
|
||||
result.add '\n'
|
||||
|
||||
proc testAlignTable() =
|
||||
block: # test with variable width columns
|
||||
var ret = ""
|
||||
ret.add "12\t143\tbcdef\n"
|
||||
ret.add "2\t14394852020\tbcdef\n"
|
||||
ret.add "45342\t1\tbf\n"
|
||||
ret.add "45342\t1\tbsadfasdfasfdasdff\n"
|
||||
ret.add "453232323232342\t1\tbsadfasdfasfdasdff\n"
|
||||
ret.add "45342\t1\tbf\n"
|
||||
ret.add "45342\t1\tb afasf a ff\n"
|
||||
ret.add "4\t1\tbf\n"
|
||||
|
||||
assertEquals alignTable(ret),
|
||||
"""
|
||||
12 143 bcdef
|
||||
2 14394852020 bcdef
|
||||
45342 1 bf
|
||||
45342 1 bsadfasdfasfdasdff
|
||||
453232323232342 1 bsadfasdfasfdasdff
|
||||
45342 1 bf
|
||||
45342 1 b afasf a ff
|
||||
4 1 bf
|
||||
"""
|
||||
|
||||
assertEquals alignTable(ret, fill = '.', sep = ","),
|
||||
"""
|
||||
12.............,143........,bcdef.............
|
||||
2..............,14394852020,bcdef.............
|
||||
45342..........,1..........,bf................
|
||||
45342..........,1..........,bsadfasdfasfdasdff
|
||||
453232323232342,1..........,bsadfasdfasfdasdff
|
||||
45342..........,1..........,bf................
|
||||
45342..........,1..........,b afasf a ff......
|
||||
4..............,1..........,bf................
|
||||
"""
|
||||
|
||||
assertEquals alignTableCustom(ret, sep = " "),
|
||||
"""
|
||||
(0,0): 12 (0,1): 143 (0,2): bcdef
|
||||
(1,0): 2 (1,1): 14394852020 (1,2): bcdef
|
||||
(2,0): 45342 (2,1): 1 (2,2): bf
|
||||
(3,0): 45342 (3,1): 1 (3,2): bsadfasdfasfdasdff
|
||||
(4,0): 453232323232342 (4,1): 1 (4,2): bsadfasdfasfdasdff
|
||||
(5,0): 45342 (5,1): 1 (5,2): bf
|
||||
(6,0): 45342 (6,1): 1 (6,2): b afasf a ff
|
||||
(7,0): 4 (7,1): 1 (7,2): bf
|
||||
"""
|
||||
|
||||
block: # test with 1 column
|
||||
var ret = "12\nasdfa\nadf"
|
||||
assertEquals alignTable(ret), """
|
||||
12
|
||||
asdfa
|
||||
adf """
|
||||
|
||||
block: # test with empty input
|
||||
var ret = ""
|
||||
assertEquals alignTable(ret), ""
|
||||
|
||||
block: # test with 1 row
|
||||
var ret = "abc\tdef"
|
||||
assertEquals alignTable(ret), """
|
||||
abc def"""
|
||||
|
||||
block: # test with 1 row ending in \t
|
||||
var ret = "abc\tdef\t"
|
||||
assertEquals alignTable(ret), """
|
||||
abc def """
|
||||
|
||||
block: # test with 1 row starting with \t
|
||||
var ret = "\tabc\tdef\t"
|
||||
assertEquals alignTable(ret), """
|
||||
abc def """
|
||||
|
||||
|
||||
block: # test with variable number of cols per row
|
||||
var ret = """
|
||||
a1,a2,a3
|
||||
|
||||
b1
|
||||
c1,c2
|
||||
,d1
|
||||
"""
|
||||
assertEquals alignTableCustom(ret, delim = ',', sep = ","),
|
||||
"""
|
||||
(0,0): a1,(0,1): a2,(0,2): a3
|
||||
(1,0): ,(1,1): ,(1,2):
|
||||
(2,0): b1,(2,1): ,(2,2):
|
||||
(3,0): c1,(3,1): c2,(3,2):
|
||||
(4,0): ,(4,1): d1,(4,2):
|
||||
"""
|
||||
|
||||
testAlignTable()
|
||||
55
tests/compiler/tunittest_light.nim
Normal file
55
tests/compiler/tunittest_light.nim
Normal file
@@ -0,0 +1,55 @@
|
||||
import compiler/unittest_light
|
||||
|
||||
proc testAssertEquals() =
|
||||
assertEquals("foo", "foo")
|
||||
doAssertRaises(AssertionError):
|
||||
assertEquals("foo", "foo ")
|
||||
|
||||
proc testMismatch() =
|
||||
assertEquals(1+1, 2*1)
|
||||
|
||||
let a = """
|
||||
some test with space at the end of lines
|
||||
|
||||
can be hard to spot differences when diffing in a terminal
|
||||
without this helper function
|
||||
|
||||
"""
|
||||
|
||||
let b = """
|
||||
some test with space at the end of lines
|
||||
|
||||
can be hard to spot differences when diffing in a terminal
|
||||
without this helper function
|
||||
|
||||
"""
|
||||
|
||||
doAssert mismatch(a, b) == """
|
||||
|
||||
lhs:{
|
||||
some test with space at the end of lines \n
|
||||
\n
|
||||
can be hard to spot differences when diffing in a terminal \n
|
||||
without this helper function\n
|
||||
\n
|
||||
}
|
||||
rhs:{
|
||||
some test with space at the end of lines \n
|
||||
\n
|
||||
can be hard to spot differences when diffing in a terminal \n
|
||||
without this helper function\n
|
||||
\n
|
||||
}
|
||||
lhs.len: 144 rhs.len: 143
|
||||
first mismatch index: 110
|
||||
lhs[i]: {" "} rhs[i]: {"\n"}lhs[0..<i]:{
|
||||
some test with space at the end of lines \n
|
||||
\n
|
||||
can be hard to spot differences when diffing in a terminal }
|
||||
rhs[0..<i]:{
|
||||
some test with space at the end of lines \n
|
||||
\n
|
||||
can be hard to spot differences when diffing in a terminal }"""
|
||||
|
||||
testMismatch()
|
||||
testAssertEquals()
|
||||
Reference in New Issue
Block a user