├── .github └── workflows │ ├── build.yml │ └── docs.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs └── screenshot.png ├── print.nimble ├── src └── print.nim └── tests ├── test.nim └── test.nims /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Github Actions 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | strategy: 6 | fail-fast: false 7 | matrix: 8 | os: [ubuntu-latest, windows-latest] 9 | 10 | runs-on: ${{ matrix.os }} 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: jiro4989/setup-nim-action@v1 15 | with: 16 | repo-token: ${{ secrets.GITHUB_TOKEN }} 17 | - run: nimble test -y 18 | - run: nimble test --gc:orc -y 19 | - run: nim js -r tests/test.nim 20 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | on: 3 | push: 4 | branches: 5 | - master 6 | env: 7 | nim-version: 'stable' 8 | nim-src: src/${{ github.event.repository.name }}.nim 9 | deploy-dir: .gh-pages 10 | jobs: 11 | docs: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: jiro4989/setup-nim-action@v1 16 | with: 17 | nim-version: ${{ env.nim-version }} 18 | - run: nimble install -Y 19 | - run: nimble doc --index:on --project --git.url:https://github.com/${{ github.repository }} --git.commit:master --out:${{ env.deploy-dir }} ${{ env.nim-src }} 20 | - name: "Copy to index.html" 21 | run: cp ${{ env.deploy-dir }}/${{ github.event.repository.name }}.html ${{ env.deploy-dir }}/index.html 22 | - name: Deploy documents 23 | uses: peaceiris/actions-gh-pages@v3 24 | with: 25 | github_token: ${{ secrets.GITHUB_TOKEN }} 26 | publish_dir: ${{ env.deploy-dir }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore files with no extention: 2 | * 3 | !*/ 4 | !*.* 5 | 6 | # normal ignores: 7 | *.exe 8 | nimcache 9 | tests/test.js 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT 2 | 3 | Copyright 2018 Andre von Houck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Print - a better echo. 2 | 3 | `nimble install print` 4 | 5 | ![Github Actions](https://github.com/treeform/print/workflows/Github%20Actions/badge.svg) 6 | 7 | [API reference](https://treeform.github.io/print) 8 | 9 | This library has no dependencies other than the Nim standard library. 10 | 11 | ## About 12 | 13 | Use `print` the same way you would use `echo` for print-debugging. It prints objects the "Nim way" with syntax highlighting. Even with refs, pointers, or cycles! 14 | 15 | ```nim 16 | import print 17 | 18 | let a = 3 19 | print a 20 | ``` 21 | ```nim 22 | a = 3 23 | ``` 24 | 25 | ## The "Nim way" 26 | 27 | It prints data structures in the same way you would create them in Nim source code. Ideally you can take what it prints out and just copy paste that into code again and it should compile in many cases. 28 | 29 | ```nim 30 | let 31 | a = 3 32 | b = "hi there" 33 | c = "oh\nthis\0isit!" 34 | d = @[1, 2, 3] 35 | d2 = [1, 2, 3] 36 | f = Foo(a:"hi", b:@["a", "abc"], c:1234) 37 | 38 | print a, b, c, d, d2, f 39 | ``` 40 | ```nim 41 | a=3 b="hi there" c="oh\nthis\0isit!" d=@[1, 2, 3] d2=[1, 2, 3] f=Foo(a:"hi", b:@["a", "abc"], c:1234) 42 | ``` 43 | 44 | ## Syntax highlighting 45 | 46 | Screenshot from VS Code: 47 | 48 | ![Image of Yaktocat](docs/screenshot.png) 49 | 50 | If you are piping to a file it will detect not-a-terminal and give you plain ascii instead. 51 | 52 | ## Smart indention 53 | 54 | It will try to print out everything in one line, but it if it does not fit it will create indentation levels. Max width is based on current terminal max width. 55 | 56 | ```nim 57 | g2 = Bar(a: "hi a really really long string", b: @["a", "abc"], c: 1234) 58 | print g2 59 | ``` 60 | 61 | ## Stuff `echo` does not do well 62 | 63 | It will also print `nil`s, `ref`s, and `pointer`s. 64 | 65 | ```nim 66 | g2=Bar( 67 | a: "hi a really really long string", 68 | b: @["a", "abc"], 69 | c: 1234 70 | ) 71 | ``` 72 | 73 | ```nim 74 | let 75 | p1: ptr int = nil 76 | p2: ref Foo = nil 77 | print p1, p2 78 | ``` 79 | ```nim 80 | p1=nil p2=nil 81 | ``` 82 | 83 | ```nim 84 | var three = 3 85 | var pointerToThree = cast[pointer](addr three) 86 | print pointerToThree 87 | ``` 88 | ```nim 89 | pointerToThree=0x00000000004360A0 90 | ``` 91 | 92 | ## And even cycles! 93 | 94 | It will also stop recursing repeating structures: 95 | ```nim 96 | type Node = ref object 97 | data: string 98 | next: Node 99 | var n = Node(data:"hi") 100 | n.next = n 101 | print n 102 | ``` 103 | ```nim 104 | n=Node(data: "hi", next: ...) 105 | ``` 106 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/treeform/print/159e540f0686efdd1c8f30ad7b4225a6aa2a5ab9/docs/screenshot.png -------------------------------------------------------------------------------- /print.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "1.0.2" 4 | author = "Andre von Houck" 5 | description = "Print is a set of pretty print macros, useful for print-debugging." 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | # Dependencies 10 | 11 | requires "nim >= 1.4.0" 12 | -------------------------------------------------------------------------------- /src/print.nim: -------------------------------------------------------------------------------- 1 | import json, macros, strutils, tables, sets, math, unicode, typetraits 2 | 3 | when defined(js): 4 | var 5 | printWidth* = 120 6 | printColors* = false 7 | haveSeen: HashSet[uint64] 8 | line: string 9 | type 10 | ForegroundColor = enum ## terminal's foreground colors 11 | fgBlack = 30, ## black 12 | fgRed, ## red 13 | fgGreen, ## green 14 | fgYellow, ## yellow 15 | fgBlue, ## blue 16 | fgMagenta, ## magenta 17 | fgCyan, ## cyan 18 | fgWhite, ## white 19 | fg8Bit, ## 256-color (not supported, see ``enableTrueColors`` instead.) 20 | fgDefault ## default terminal foreground color 21 | else: 22 | import terminal 23 | var 24 | printWidth* = terminalWidth() 25 | printColors* = stdout.isatty() 26 | haveSeen: HashSet[uint64] 27 | 28 | type 29 | NodeKind = enum 30 | nkSupport 31 | nkTopLevel 32 | nkName 33 | nkNumber 34 | nkProc 35 | nkType 36 | nkString 37 | nkChar 38 | nkBool 39 | nkPointer 40 | nkSeq 41 | nkArray 42 | nkTuple 43 | nkTable 44 | nkObject 45 | nkTopPair 46 | nkFieldPair 47 | nkNil 48 | nkRepeat 49 | 50 | Node = ref object 51 | kind: NodeKind 52 | value: string 53 | nodes: seq[Node] 54 | 55 | # Work around for both jsony and print needs this. 56 | template fieldPairs2*(x: untyped): untyped = 57 | when compiles(x[]): 58 | x[].fieldPairs 59 | else: 60 | x.fieldPairs 61 | 62 | template justAddr(x): uint64 = 63 | cast[uint64](x.unsafeAddr) 64 | 65 | macro `$`(a: proc): untyped = 66 | let procDef = a.getTypeInst 67 | procDef.insert 0, ident($a) 68 | newLit(procDef.repr) 69 | 70 | proc escapeString*(v: string, q = "\""): string = 71 | result.add q 72 | for c in v: 73 | case c: 74 | of '\0': result.add r"\0" 75 | of '\\': result.add r"\\" 76 | of '\b': result.add r"\b" 77 | of '\f': result.add r"\f" 78 | of '\n': result.add r"\n" 79 | of '\r': result.add r"\r" 80 | of '\t': result.add r"\t" 81 | else: 82 | if ord(c) > 128: 83 | result.add "\\x" & toHex(ord(c), 2).toLowerAscii() 84 | result.add c 85 | result.add q 86 | 87 | proc escapeChar(v: string): string = 88 | escapeString(v, "'") 89 | 90 | proc newSupportNode*(value: string): Node = 91 | Node(kind: nkSupport, value: value) 92 | 93 | proc newNameNode*(name: string): Node = 94 | Node(kind: nkName, value: name) 95 | 96 | proc newTopPairNode*(k, v: Node): Node = 97 | Node(kind: nkTopPair, nodes: @[k, v]) 98 | 99 | proc newFieldPairNode*(k, v: Node): Node = 100 | Node(kind: nkFieldPair, nodes: @[k, v]) 101 | 102 | #proc newNode[K, V](t: Table[K, V]): Node 103 | proc newNode*[T](x: seq[T]): Node 104 | proc newNode*[N, T](x: array[N, T]): Node 105 | proc newNode*(x: SomeNumber): Node 106 | proc newNode*(x: string): Node 107 | proc newNode*(x: char): Node 108 | proc newNodeFromBaseType*[T](x: T): Node 109 | #proc newNode[T: object](s: T): Node 110 | 111 | proc newNode*(x: SomeNumber): Node = 112 | Node(kind: nkNumber, value: system.`$`(x)) 113 | 114 | proc newNode*(x: bool): Node = 115 | Node(kind: nkBool, value: $x) 116 | 117 | proc newNode*(x: string): Node = 118 | Node(kind: nkString, value: x) 119 | 120 | proc newNode*(x: cstring): Node = 121 | Node(kind: nkString, value: $x) 122 | 123 | proc newNode*(x: char): Node = 124 | Node(kind: nkChar, value: $x) 125 | 126 | proc newNode*(x: Rune): Node = 127 | Node(kind: nkChar, value: $x) 128 | 129 | proc newNode*(x: proc): Node = 130 | when compiles($x): 131 | Node(kind: nkProc, value: $x) 132 | else: 133 | Node(kind: nkProc, value: x.type.name) 134 | 135 | proc newNode*(x: type): Node = 136 | Node(kind: nkType, value: $x) 137 | 138 | proc newNode*[T](x: seq[T]): Node = 139 | var nodes: seq[Node] 140 | for e in x: 141 | nodes.add(newNodeFromBaseType(e)) 142 | Node(kind: nkSeq, nodes:nodes) 143 | 144 | proc newNode*[N, T](x: array[N, T]): Node = 145 | var nodes: seq[Node] 146 | for e in x: 147 | nodes.add(newNodeFromBaseType(e)) 148 | Node(kind: nkArray, nodes:nodes) 149 | 150 | proc newNode*[K, V](x: Table[K, V]): Node = 151 | var nodes: seq[Node] 152 | for k, v in x.pairs(): 153 | nodes.add(newFieldPairNode(newNodeFromBaseType(k), newNodeFromBaseType(v))) 154 | Node(kind: nkTable, nodes:nodes) 155 | 156 | proc newNode*[T](x: HashSet[T] | set[T]): Node = 157 | var nodes: seq[Node] 158 | for e in x.items(): 159 | nodes.add(newNodeFromBaseType(e)) 160 | Node(kind: nkArray, nodes:nodes) 161 | 162 | proc newNode*[T: tuple](x: T): Node = 163 | var nodes: seq[Node] 164 | for _, e in x.fieldPairs2: 165 | nodes.add(newNodeFromBaseType(e)) 166 | Node(kind: nkTuple, nodes:nodes) 167 | 168 | proc newNode*[T: object](x: T): Node = 169 | var nodes: seq[Node] 170 | for n, e in x.fieldPairs2: 171 | nodes.add(newFieldPairNode(newNameNode(n), newNodeFromBaseType(e))) 172 | Node(kind: nkObject, value: $type(x), nodes:nodes) 173 | 174 | proc newNode*[T](x: ref T): Node = 175 | if x != nil: 176 | when not defined(js): 177 | if x[].justAddr in haveSeen: 178 | Node(kind: nkRepeat, value:"...") 179 | else: 180 | if x[].justAddr != 0: 181 | haveSeen.incl x[].justAddr 182 | newNodeFromBaseType(x[]) 183 | else: 184 | newNodeFromBaseType(x[]) 185 | else: 186 | Node(kind: nkNil, value:"nil") 187 | 188 | proc newNode*[T](x: ptr T): Node = 189 | if x != nil: 190 | newNodeFromBaseType(x[]) 191 | else: 192 | Node(kind: nkNil, value:"nil") 193 | 194 | proc newNode*(x: pointer): Node = 195 | if x != nil: 196 | Node(kind: nkPointer, value:"0x" & toHex(cast[uint64](x))) 197 | else: 198 | Node(kind: nkNil, value:"nil") 199 | 200 | proc newNode*[T](x: ptr UncheckedArray[T]): Node = 201 | newNodeFromBaseType(cast[pointer](x)) 202 | 203 | proc newNode*(x: enum): Node = 204 | newNode($x) 205 | 206 | proc newNodeFromBaseType*[T](x: T): Node = 207 | newNode(x.distinctBase(recursive = true)) 208 | 209 | proc newNodeFromBaseType*(x: type): Node = 210 | newNode(x) 211 | 212 | proc textLine(node: Node): string = 213 | case node.kind: 214 | of nkNumber, nkNil, nkRepeat, nkPointer, nkProc, nkBool, nkType: 215 | result.add node.value 216 | of nkString, nkChar: 217 | result.add node.value.escapeString() 218 | of nkSeq, nkArray: 219 | if node.kind == nkSeq: 220 | result.add "@" 221 | result.add "[" 222 | for i, e in node.nodes: 223 | if i != 0: 224 | result.add ", " 225 | result.add textLine(e) 226 | result.add "]" 227 | of nkTable: 228 | result.add "{" 229 | for i, e in node.nodes: 230 | if i != 0: 231 | result.add ", " 232 | result.add textLine(e) 233 | result.add "}" 234 | of nkObject, nkTuple: 235 | result.add node.value 236 | result.add "(" 237 | for i, e in node.nodes: 238 | if i != 0: 239 | result.add ", " 240 | result.add textLine(e) 241 | result.add ")" 242 | of nkTopLevel: 243 | result.add node.value 244 | for i, e in node.nodes: 245 | if i != 0: 246 | result.add " " 247 | result.add textLine(e) 248 | of nkTopPair: 249 | result.add textLine(node.nodes[0]) 250 | result.add "=" 251 | result.add textLine(node.nodes[1]) 252 | of nkFieldPair: 253 | result.add textLine(node.nodes[0]) 254 | result.add ": " 255 | result.add textLine(node.nodes[1]) 256 | else: 257 | result.add node.value 258 | 259 | proc printStr(s: string) = 260 | when defined(js): 261 | line.add(s) 262 | else: 263 | stdout.write(s) 264 | 265 | proc printStr(c: ForeGroundColor, s: string) = 266 | when defined(js): 267 | line.add(s) 268 | else: 269 | if printColors: 270 | stdout.styledWrite(c, s) 271 | else: 272 | stdout.write(s) 273 | 274 | proc printNode*(node: Node, indent: int) = 275 | 276 | let wrap = textLine(node).len + indent >= printWidth 277 | 278 | case node.kind: 279 | of nkNumber, nkBool: 280 | printStr(fgBlue, node.value) 281 | of nkRepeat, nkNil, nkPointer: 282 | printStr(fgRed, node.value) 283 | of nkProc, nkType: 284 | printStr(fgMagenta, node.value) 285 | of nkString: 286 | printStr(fgGreen, node.value.escapeString()) 287 | of nkChar: 288 | printStr(fgGreen, "'" & node.value.escapeString()[1..^2] & "'") 289 | of nkSeq, nkArray: 290 | if node.kind == nkSeq: 291 | printStr "@" 292 | if wrap: 293 | printStr "[\n" 294 | for i, e in node.nodes: 295 | printStr " ".repeat(indent + 1) 296 | printNode(e, indent + 1) 297 | if i != node.nodes.len - 1: 298 | printStr ",\n" 299 | printStr "\n" 300 | printStr " ".repeat(indent) 301 | printStr "]" 302 | else: 303 | printStr "[" 304 | for i, e in node.nodes: 305 | if i != 0: 306 | printStr ", " 307 | printNode(e, 0) 308 | printStr "]" 309 | of nkTable, nkObject, nkTuple: 310 | if node.kind in [nkObject, nkTuple]: 311 | printStr(fgCyan, node.value) 312 | printStr "(" 313 | else: 314 | printStr "{" 315 | if wrap: 316 | printStr "\n" 317 | for i, e in node.nodes: 318 | printNode(e, indent + 1) 319 | if i != node.nodes.len - 1: 320 | printStr ",\n" 321 | printStr "\n" 322 | printStr " ".repeat(indent) 323 | else: 324 | for i, e in node.nodes: 325 | if i != 0: 326 | printStr ", " 327 | printNode(e, 0) 328 | if node.kind in [nkObject, nkTuple]: 329 | printStr ")" 330 | else: 331 | printStr "}" 332 | 333 | of nkTopLevel: 334 | if wrap: 335 | for i, e in node.nodes: 336 | printNode(e, 0) 337 | if i != node.nodes.len - 1: 338 | printStr "\n" 339 | else: 340 | for i, e in node.nodes: 341 | if i != 0: 342 | printStr " " 343 | printNode(e, 0) 344 | printStr "\n" 345 | 346 | of nkTopPair: 347 | printNode(node.nodes[0], 0) 348 | printStr "=" 349 | printNode(node.nodes[1], 0) 350 | 351 | of nkFieldPair: 352 | printStr " ".repeat(indent) 353 | printNode(node.nodes[0], indent) 354 | printStr ": " 355 | printNode(node.nodes[1], indent) 356 | 357 | else: 358 | printStr(node.value) 359 | 360 | proc printNodes*(s: varargs[Node]) = 361 | haveSeen.clear() 362 | var nodes: seq[Node] 363 | for e in s: 364 | nodes.add(e) 365 | var node = Node(kind: nkTopLevel, nodes: nodes) 366 | printNode(node, 0) 367 | when defined(js): 368 | echo line[0 .. ^2] 369 | line = "" 370 | 371 | macro rawPrint*(n: varargs[untyped]): untyped = 372 | var command = nnkCommand.newTree( 373 | newIdentNode("printNodes") 374 | ) 375 | for i in 0..n.len-1: 376 | if n[i].kind == nnkStrLit: 377 | command.add nnkCommand.newTree( 378 | newIdentNode("newSupportNode"), 379 | n[i] 380 | ) 381 | else: 382 | command.add nnkCommand.newTree( 383 | newIdentNode("newTopPairNode"), 384 | nnkCommand.newTree( 385 | newIdentNode("newNameNode"), 386 | newStrLitNode(n[i].repr) 387 | ), 388 | nnkCommand.newTree( 389 | newIdentNode("newNodeFromBaseType"), 390 | n[i] 391 | ) 392 | ) 393 | 394 | var s = nnkStmtList.newTree(command) 395 | return s 396 | 397 | template print*(n: varargs[untyped]): untyped = 398 | {.cast(gcSafe), cast(noSideEffect).}: 399 | try: 400 | rawPrint(n) 401 | except: 402 | discard 403 | 404 | type TableStyle* = enum 405 | Fancy 406 | Plain 407 | 408 | proc printTable*[T](arr: seq[T], style = Fancy) = 409 | ## Given a list of items prints them as a table. 410 | 411 | # Turns items into table props. 412 | var 413 | header: seq[string] 414 | widths: seq[int] 415 | number: seq[bool] 416 | table: seq[seq[string]] 417 | 418 | var headerItem: T 419 | for k, v in headerItem.fieldPairs2: 420 | header.add(k) 421 | widths.add(len(k)) 422 | number.add(type(v) is SomeNumber) 423 | 424 | for i, item in arr: 425 | var 426 | row: seq[string] 427 | col = 0 428 | for k, v in item.fieldPairs2: 429 | let text = 430 | when type(v) is char: 431 | escapeChar($v) 432 | elif type(v) is string: 433 | v.escapeString("") 434 | else: 435 | $v 436 | row.add(text) 437 | widths[col] = max(text.len, widths[col]) 438 | inc col 439 | table.add(row) 440 | 441 | case style: 442 | of Fancy: 443 | # Print header. 444 | printStr("╭─") 445 | for col in 0 ..< header.len: 446 | for j in 0 ..< widths[col]: 447 | printStr("─") 448 | if col != header.len - 1: 449 | printStr("─┬─") 450 | else: 451 | printStr("─╮") 452 | printStr("\n") 453 | 454 | # Print header. 455 | printStr("│ ") 456 | for col in 0 ..< header.len: 457 | if number[col]: 458 | for j in header[col].len ..< widths[col]: 459 | printStr(" ") 460 | printStr(header[col]) 461 | else: 462 | printStr(header[col]) 463 | for j in header[col].len ..< widths[col]: 464 | printStr(" ") 465 | printStr(" │ ") 466 | printStr("\n") 467 | 468 | # Print header divider. 469 | printStr("├─") 470 | for col in 0 ..< header.len: 471 | for j in 0 ..< widths[col]: 472 | printStr("─") 473 | if col != header.len - 1: 474 | printStr("─┼─") 475 | else: 476 | printStr("─┤") 477 | printStr("\n") 478 | 479 | # Print the values 480 | for i, item in arr: 481 | var col = 0 482 | printStr("│ ") 483 | for k, v in item.fieldPairs2: 484 | let text = table[i][col] 485 | if number[col]: 486 | printStr(" ".repeat(widths[col] - text.len)) 487 | printStr(fgBlue, text) 488 | else: 489 | printStr(fgGreen, text) 490 | printStr(" ".repeat(widths[col] - text.len)) 491 | printStr(" │ ") 492 | inc col 493 | printStr("\n") 494 | 495 | # Print footer. 496 | printStr("╰─") 497 | for col in 0 ..< header.len: 498 | for j in 0 ..< widths[col]: 499 | printStr("─") 500 | if col != header.len - 1: 501 | printStr("─┴─") 502 | else: 503 | printStr("─╯") 504 | printStr("\n") 505 | 506 | of Plain: 507 | # Print header. 508 | for col in 0 ..< header.len: 509 | printStr(header[col]) 510 | for j in header[col].len ..< widths[col]: 511 | printStr(" ") 512 | printStr(" ") 513 | printStr("\n") 514 | 515 | # Print the values 516 | for i, item in arr: 517 | var col = 0 518 | for k, v in item.fieldPairs2: 519 | let text = table[i][col] 520 | if number[col]: 521 | for j in text.len ..< widths[col]: 522 | printStr(" ") 523 | printStr(fgBlue, text) 524 | else: 525 | printStr(fgGreen, text) 526 | if not number[col]: 527 | for j in text.len ..< widths[col]: 528 | printStr(" ") 529 | printStr(" ") 530 | inc col 531 | printStr("\n") 532 | 533 | proc printBarChart*[N:SomeNumber](data: seq[(string, N)]) = 534 | ## prints a bar chart like this: 535 | ## zpu: ######### 20.45 536 | ## cpu: ################################################# 70.00 537 | ## gpu: ########################### 45.56 538 | const fillChar = "#" 539 | proc maximize(a: var SomeNumber, v: SomeNumber) = a = max(a, v) 540 | proc minimize(a: var SomeNumber, v: SomeNumber) = a = min(a, v) 541 | proc frac(a: SomeFloat): SomeFloat = a - floor(a) 542 | var 543 | maxKeyWidth = 0 544 | minNumber: N = 0 545 | maxNumber: N = 0 546 | maxLabel = 0 547 | 548 | for (k, v) in data: 549 | maximize(maxKeyWidth, k.len) 550 | maximize(maxLabel, ($v).len) 551 | minimize(minNumber, v) 552 | maximize(maxNumber, v) 553 | 554 | var 555 | chartWidth = printWidth - maxKeyWidth - 3 - maxLabel - 2 556 | if minNumber != 0: 557 | chartWidth -= maxLabel + 1 558 | var 559 | barScale = chartWidth.float / (maxNumber.float - minNumber.float) 560 | preZero = (-minNumber.float * barScale).ceil.int 561 | 562 | for (k, v) in data: 563 | var line = "" 564 | printStr " ".repeat(maxKeyWidth - k.len) 565 | printStr fgGreen, k 566 | printStr ": " 567 | 568 | let barWidth = v.float * barScale 569 | if minNumber == 0: 570 | printStr fillChar.repeat(floor(barWidth).int) 571 | printStr " " 572 | printStr fgBlue, $v 573 | else: 574 | if barWidth >= 0: 575 | printStr " ".repeat(preZero + maxLabel) 576 | printStr fillChar.repeat(floor(barWidth).int) 577 | printStr " " 578 | printStr fgBlue, $v 579 | else: 580 | printStr " ".repeat(preZero + barWidth.int + maxLabel - ($v).len) 581 | printStr fgBlue, $v 582 | printStr " " 583 | printStr fillChar.repeat(floor(-barWidth).int - 1) 584 | printStr "\n" 585 | -------------------------------------------------------------------------------- /tests/test.nim: -------------------------------------------------------------------------------- 1 | import print, tables, sets 2 | 3 | #printWidth = 40 4 | var s = "" 5 | for i in 0 ..< printWidth: 6 | s.add "#" 7 | echo s 8 | 9 | block: 10 | let 11 | a = true 12 | b = 1 13 | c = 1.34 14 | d = "one" 15 | print a, b, c, d 16 | 17 | type Foo = object 18 | a: string 19 | b: seq[string] 20 | c: int 21 | 22 | type Bar = ref object 23 | a: string 24 | b: seq[string] 25 | c: int 26 | 27 | type Colors = enum 28 | Red, White, Blue 29 | 30 | var g: Bar 31 | var g2 = Bar(a: "hi", b: @[], c: 1234) 32 | print g, g2 33 | 34 | g2 = Bar(a: "hi a really really long string", b: @["a", "abc"], c: 1234) 35 | print g, g2 36 | 37 | g2 = Bar(a: "hi", b: @["a", "abc", "a really really long string"], c: 1234) 38 | print g, g2 39 | 40 | proc hi() = 41 | echo "hi" 42 | print hi 43 | 44 | proc hi2(a: int, s:string): bool = 45 | echo "hi" 46 | print hi2 47 | 48 | let what = "\0\tworld\n\r" 49 | print "hello", what 50 | 51 | print 12 52 | 53 | let whatc = cstring "hi there c string" 54 | print whatc 55 | 56 | let 57 | a = 3 58 | b = "hi there" 59 | c = "oh\nthis\0isit!" 60 | d = @[1, 2, 3] 61 | d2 = [1, 2, 3] 62 | f = Foo(a: "hi", b: @["a", "abc"], c: 1234) 63 | 64 | print a, b, c, d, d2, f 65 | 66 | when not defined(js): 67 | let 68 | p1: ptr int = nil 69 | p2: ref Foo = nil 70 | p3: pointer = nil 71 | print p1, p2, p3 72 | 73 | var three = 3 74 | var pointerToThree = cast[pointer](addr three) 75 | print pointerToThree 76 | 77 | var t = ("hi", 1, (2, 3)) 78 | print t 79 | 80 | let smallArr = [1, 2, 3] 81 | print "array", smallArr 82 | 83 | let smallSeq = @[1, 2, 3] 84 | print "seq", smallSeq 85 | 86 | let smallTable = {1: "one", 2: "two"}.toTable 87 | print "table", smallTable 88 | 89 | let smallTableRef = {1: "one", 2: "two"}.newTable 90 | print "table", smallTableRef 91 | 92 | type 93 | SomeObj = object 94 | id: string 95 | year: int 96 | let someThing = SomeObj(id: "xy8", year: 2017) 97 | print someThing 98 | 99 | # Really big lines should wrap: 100 | let bigTable = newTable[string, int]() 101 | for i in 0..<10: 102 | bigTable["id" & $i] = i 103 | print "table", bigTable 104 | 105 | # Really Nested Stuff 106 | let bigTable2 = newTable[string, SomeObj]() 107 | for i in 0..<10: 108 | bigTable2["id" & $i] = SomeObj(id: "xy{8}", year: i) 109 | print "table", bigTable2 110 | 111 | let color = Red 112 | print "Colors", color 113 | 114 | when not defined(js): 115 | # Test circular structures 116 | block: 117 | type Node = ref object 118 | data: string 119 | next: Node 120 | var n = Node(data:"hi") 121 | n.next = n 122 | print n 123 | 124 | block: 125 | type Node = ref object 126 | data: string 127 | next: ptr[Node] 128 | var n = Node(data:"hi") 129 | n.next = n.addr 130 | print n 131 | 132 | block: 133 | type Node = ref object 134 | data: string 135 | next: pointer 136 | var n = Node(data:"hi") 137 | n.next = n.addr 138 | print n 139 | 140 | var file = open("tests/test.nim") 141 | print file 142 | 143 | var ua: ptr UncheckedArray[int] 144 | print ua 145 | 146 | block: 147 | type 148 | Mob = object 149 | name: string 150 | hp: int 151 | x: float 152 | y: float 153 | mode: char 154 | var mobs = @[ 155 | Mob(name:"Boyar Dens", hp: 120, x:1.2, y:2.33333, mode:'a'), 156 | Mob(name:"Kozar\0", hp: 20, x:21.23, y:2.3), 157 | Mob(name:"Goffer", hp: 25, x:31.2, y:2.3), 158 | Mob(name:"Hexer the Great", hp: 0, x:41.2, y:122.3, mode:'b'), 159 | ] 160 | printTable mobs 161 | printTable mobs, Plain 162 | 163 | block: 164 | var hs: HashSet[uint64] 165 | hs.incl(1.uint64) 166 | hs.incl(12.uint64) 167 | hs.incl(123.uint64) 168 | print hs 169 | 170 | block: 171 | var a = @[1, 2, 3] 172 | print type(a) 173 | 174 | block: 175 | printBarChart(@[ 176 | ("2018", 200.45), 177 | ("2017", 110.45), 178 | ("2016", 77.89), 179 | ("2015", 66.20 ), 180 | ("2014", 760.00), 181 | ("2013", 450.56), 182 | ]) 183 | print "second bar" 184 | printBarChart(@[ 185 | ("2018", -200), 186 | ("2017", -100), 187 | ("2016", 0), 188 | ("2015", 100), 189 | ("2014", 200), 190 | ("2013", 300), 191 | ]) 192 | print "second bar" 193 | printBarChart(@[ 194 | ("2018", -200.45), 195 | ("2017", 110.45), 196 | ("2016", -177.89), 197 | ("2015", -66.20 ), 198 | ("2014", 760.00), 199 | ("2013", 450.56), 200 | ]) 201 | 202 | block: 203 | type Dollar = distinct int 204 | let a = 5.Dollar 205 | print a 206 | let b = @[4.Dollar] 207 | print b 208 | 209 | block: 210 | let c = {2, 3} 211 | print c 212 | 213 | block: 214 | type MyProc = proc() 215 | let a: MyProc = proc() = discard 216 | print a 217 | -------------------------------------------------------------------------------- /tests/test.nims: -------------------------------------------------------------------------------- 1 | --path:"../src" 2 | --------------------------------------------------------------------------------