Merge branch 'devel' into sighashes

This commit is contained in:
Andreas Rumpf 2016-12-06 10:08:38 +01:00
commit 36a9703866
10 changed files with 242 additions and 178 deletions

View file

@ -16,7 +16,7 @@ type
{.deprecated: [Tcflag: Cflag].} {.deprecated: [Tcflag: Cflag].}
const const
NCCS* = 32 NCCS* = when defined(macosx): 20 else: 32
type type
Termios* {.importc: "struct termios", header: "<termios.h>".} = object Termios* {.importc: "struct termios", header: "<termios.h>".} = object
@ -28,174 +28,136 @@ type
# cc characters # cc characters
const var
VINTR* = 0 VINTR* {.importc, header: "<termios.h>".}: cint
VQUIT* = 1 VQUIT* {.importc, header: "<termios.h>".}: cint
VERASE* = 2 VERASE* {.importc, header: "<termios.h>".}: cint
VKILL* = 3 VKILL* {.importc, header: "<termios.h>".}: cint
VEOF* = 4 VEOF* {.importc, header: "<termios.h>".}: cint
VTIME* = 5 VTIME* {.importc, header: "<termios.h>".}: cint
VMIN* = 6 VMIN* {.importc, header: "<termios.h>".}: cint
VSWTC* = 7 VSTART* {.importc, header: "<termios.h>".}: cint
VSTART* = 8 VSTOP* {.importc, header: "<termios.h>".}: cint
VSTOP* = 9 VSUSP* {.importc, header: "<termios.h>".}: cint
VSUSP* = 10 VEOL* {.importc, header: "<termios.h>".}: cint
VEOL* = 11
VREPRINT* = 12
VDISCARD* = 13
VWERASE* = 14
VLNEXT* = 15
VEOL2* = 16
# iflag bits # iflag bits
const var
IGNBRK* = 1 IGNBRK* {.importc, header: "<termios.h>".}: Cflag
BRKINT* = 2 BRKINT* {.importc, header: "<termios.h>".}: Cflag
IGNPAR* = 4 IGNPAR* {.importc, header: "<termios.h>".}: Cflag
PARMRK* = 10 PARMRK* {.importc, header: "<termios.h>".}: Cflag
INPCK* = 20 INPCK* {.importc, header: "<termios.h>".}: Cflag
ISTRIP* = 40 ISTRIP* {.importc, header: "<termios.h>".}: Cflag
INLCR* = 100 INLCR* {.importc, header: "<termios.h>".}: Cflag
IGNCR* = 200 IGNCR* {.importc, header: "<termios.h>".}: Cflag
ICRNL* = 400 ICRNL* {.importc, header: "<termios.h>".}: Cflag
IUCLC* = 1000 IUCLC* {.importc, header: "<termios.h>".}: Cflag
IXON* = 2000 IXON* {.importc, header: "<termios.h>".}: Cflag
IXANY* = 4000 IXANY* {.importc, header: "<termios.h>".}: Cflag
IXOFF* = 10000 IXOFF* {.importc, header: "<termios.h>".}: Cflag
IMAXBEL* = 20000
IUTF8* = 40000
# oflag bits # oflag bits
const var
OPOST* = 1 OPOST* {.importc, header: "<termios.h>".}: Cflag
OLCUC* = 2 ONLCR* {.importc, header: "<termios.h>".}: Cflag
ONLCR* = 4 OCRNL* {.importc, header: "<termios.h>".}: Cflag
OCRNL* = 10 ONOCR* {.importc, header: "<termios.h>".}: Cflag
ONOCR* = 20 ONLRET* {.importc, header: "<termios.h>".}: Cflag
ONLRET* = 40 OFILL* {.importc, header: "<termios.h>".}: Cflag
OFILL* = 100 OFDEL* {.importc, header: "<termios.h>".}: Cflag
OFDEL* = 200 NLDLY* {.importc, header: "<termios.h>".}: Cflag
NLDLY* = 400 NL0* {.importc, header: "<termios.h>".}: Cflag
NL0* = 0 NL1* {.importc, header: "<termios.h>".}: Cflag
NL1* = 400 CRDLY* {.importc, header: "<termios.h>".}: Cflag
CRDLY* = 3000 CR0* {.importc, header: "<termios.h>".}: Cflag
CR0* = 0 CR1* {.importc, header: "<termios.h>".}: Cflag
CR1* = 1000 CR2* {.importc, header: "<termios.h>".}: Cflag
CR2* = 2000 CR3* {.importc, header: "<termios.h>".}: Cflag
CR3* = 3000 TABDLY* {.importc, header: "<termios.h>".}: Cflag
TABDLY* = 14000 TAB0* {.importc, header: "<termios.h>".}: Cflag
TAB0* = 0 TAB1* {.importc, header: "<termios.h>".}: Cflag
TAB1* = 4000 TAB2* {.importc, header: "<termios.h>".}: Cflag
TAB2* = 10000 TAB3* {.importc, header: "<termios.h>".}: Cflag
TAB3* = 14000 BSDLY* {.importc, header: "<termios.h>".}: Cflag
BSDLY* = 20000 BS0* {.importc, header: "<termios.h>".}: Cflag
BS0* = 0 BS1* {.importc, header: "<termios.h>".}: Cflag
BS1* = 20000 FFDLY* {.importc, header: "<termios.h>".}: Cflag
FFDLY* = 0o000000100000 FF0* {.importc, header: "<termios.h>".}: Cflag
FF0* = 0 FF1* {.importc, header: "<termios.h>".}: Cflag
FF1* = 0o000000100000 VTDLY* {.importc, header: "<termios.h>".}: Cflag
VTDLY* = 40000 VT0* {.importc, header: "<termios.h>".}: Cflag
VT0* = 0 VT1* {.importc, header: "<termios.h>".}: Cflag
VT1* = 40000
XTABS* = 14000
# cflag bit meaning # cflag bit meaning
const var
CBAUD* = 10017 B0* {.importc, header: "<termios.h>".}: Speed
B0* = 0 B50* {.importc, header: "<termios.h>".}: Speed
B50* = 1 B75* {.importc, header: "<termios.h>".}: Speed
B75* = 2 B110* {.importc, header: "<termios.h>".}: Speed
B110* = 3 B134* {.importc, header: "<termios.h>".}: Speed
B134* = 4 B150* {.importc, header: "<termios.h>".}: Speed
B150* = 5 B200* {.importc, header: "<termios.h>".}: Speed
B200* = 6 B300* {.importc, header: "<termios.h>".}: Speed
B300* = 7 B600* {.importc, header: "<termios.h>".}: Speed
B600* = 10 B1200* {.importc, header: "<termios.h>".}: Speed
B1200* = 11 B1800* {.importc, header: "<termios.h>".}: Speed
B1800* = 12 B2400* {.importc, header: "<termios.h>".}: Speed
B2400* = 13 B4800* {.importc, header: "<termios.h>".}: Speed
B4800* = 14 B9600* {.importc, header: "<termios.h>".}: Speed
B9600* = 15 B19200* {.importc, header: "<termios.h>".}: Speed
B19200* = 16 B38400* {.importc, header: "<termios.h>".}: Speed
B38400* = 17 EXTA* {.importc, header: "<termios.h>".}: Speed
EXTA* = B19200 EXTB* {.importc, header: "<termios.h>".}: Speed
EXTB* = B38400 CSIZE* {.importc, header: "<termios.h>".}: Cflag
CSIZE* = 60 CS5* {.importc, header: "<termios.h>".}: Cflag
CS5* = 0 CS6* {.importc, header: "<termios.h>".}: Cflag
CS6* = 20 CS7* {.importc, header: "<termios.h>".}: Cflag
CS7* = 40 CS8* {.importc, header: "<termios.h>".}: Cflag
CS8* = 60 CSTOPB* {.importc, header: "<termios.h>".}: Cflag
CSTOPB* = 100 CREAD* {.importc, header: "<termios.h>".}: Cflag
CREAD* = 200 PARENB* {.importc, header: "<termios.h>".}: Cflag
PARENB* = 400 PARODD* {.importc, header: "<termios.h>".}: Cflag
PARODD* = 1000 HUPCL* {.importc, header: "<termios.h>".}: Cflag
HUPCL* = 2000 CLOCAL* {.importc, header: "<termios.h>".}: Cflag
CLOCAL* = 4000
CBAUDEX* = 10000
B57600* = 10001
B115200* = 10002
B230400* = 10003
B460800* = 10004
B500000* = 10005
B576000* = 10006
B921600* = 10007
B1000000* = 10010
B1152000* = 10011
B1500000* = 10012
B2000000* = 10013
B2500000* = 10014
B3000000* = 10015
B3500000* = 10016
B4000000* = 10017
MAX_BAUD* = B4000000
CIBAUD* = 2003600000
CMSPAR* = 0o010000000000
CRTSCTS* = 0o020000000000
# lflag bits # lflag bits
const var
ISIG* = 1 ISIG* {.importc, header: "<termios.h>".}: Cflag
ICANON* = 2 ICANON* {.importc, header: "<termios.h>".}: Cflag
XCASE* = 4 ECHO* {.importc, header: "<termios.h>".}: Cflag
ECHO* = 10 ECHOE* {.importc, header: "<termios.h>".}: Cflag
ECHOE* = 20 ECHOK* {.importc, header: "<termios.h>".}: Cflag
ECHOK* = 40 ECHONL* {.importc, header: "<termios.h>".}: Cflag
ECHONL* = 100 NOFLSH* {.importc, header: "<termios.h>".}: Cflag
NOFLSH* = 200 TOSTOP* {.importc, header: "<termios.h>".}: Cflag
TOSTOP* = 400 IEXTEN* {.importc, header: "<termios.h>".}: Cflag
ECHOCTL* = 1000
ECHOPRT* = 2000
ECHOKE* = 4000
FLUSHO* = 10000
PENDIN* = 40000
IEXTEN* = 0o000000100000
EXTPROC* = 0o000000200000
# tcflow() and TCXONC use these # tcflow() and TCXONC use these
const var
TCOOFF* = 0 TCOOFF* {.importc, header: "<termios.h>".}: cint
TCOON* = 1 TCOON* {.importc, header: "<termios.h>".}: cint
TCIOFF* = 2 TCIOFF* {.importc, header: "<termios.h>".}: cint
TCION* = 3 TCION* {.importc, header: "<termios.h>".}: cint
# tcflush() and TCFLSH use these # tcflush() and TCFLSH use these
const var
TCIFLUSH* = 0 TCIFLUSH* {.importc, header: "<termios.h>".}: cint
TCOFLUSH* = 1 TCOFLUSH* {.importc, header: "<termios.h>".}: cint
TCIOFLUSH* = 2 TCIOFLUSH* {.importc, header: "<termios.h>".}: cint
# tcsetattr uses these # tcsetattr uses these
const var
TCSANOW* = 0 TCSANOW* {.importc, header: "<termios.h>".}: cint
TCSADRAIN* = 1 TCSADRAIN* {.importc, header: "<termios.h>".}: cint
TCSAFLUSH* = 2 TCSAFLUSH* {.importc, header: "<termios.h>".}: cint
# Compare a character C to a value VAL from the `cc' array in a # Compare a character C to a value VAL from the `cc' array in a
# `struct termios'. If VAL is _POSIX_VDISABLE, no character can match it. # `struct termios'. If VAL is _POSIX_VDISABLE, no character can match it.
@ -221,10 +183,6 @@ proc cfSetIspeed*(termios: ptr Termios; speed: Speed): cint {.
importc: "cfsetispeed", header: "<termios.h>".} importc: "cfsetispeed", header: "<termios.h>".}
# Set both the input and output baud rates in *TERMIOS_OP to SPEED. # Set both the input and output baud rates in *TERMIOS_OP to SPEED.
proc cfSetSpeed*(termios: ptr Termios; speed: Speed): cint {.
importc: "cfsetspeed", header: "<termios.h>".}
# Put the state of FD into *TERMIOS_P.
proc tcGetAttr*(fd: cint; termios: ptr Termios): cint {. proc tcGetAttr*(fd: cint; termios: ptr Termios): cint {.
importc: "tcgetattr", header: "<termios.h>".} importc: "tcgetattr", header: "<termios.h>".}
# Set the state of FD to *TERMIOS_P. # Set the state of FD to *TERMIOS_P.
@ -234,10 +192,6 @@ proc tcSetAttr*(fd: cint; optional_actions: cint; termios: ptr Termios): cint {.
importc: "tcsetattr", header: "<termios.h>".} importc: "tcsetattr", header: "<termios.h>".}
# Set *TERMIOS_P to indicate raw mode. # Set *TERMIOS_P to indicate raw mode.
proc cfMakeRaw*(termios: ptr Termios) {.importc: "cfmakeraw",
header: "<termios.h>".}
# Send zero bits on FD.
proc tcSendBreak*(fd: cint; duration: cint): cint {.importc: "tcsendbreak", proc tcSendBreak*(fd: cint; duration: cint): cint {.importc: "tcsendbreak",
header: "<termios.h>".} header: "<termios.h>".}
# Wait for pending output to be written on FD. # Wait for pending output to be written on FD.
@ -258,13 +212,10 @@ proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
header: "<termios.h>".} header: "<termios.h>".}
# Get process group ID for session leader for controlling terminal FD. # Get process group ID for session leader for controlling terminal FD.
proc tcGetSid*(fd: cint): Pid {.importc: "tcgetsid", header: "<termios.h>".}
# Window size ioctl. Should work on on any Unix that xterm has been ported to. # Window size ioctl. Should work on on any Unix that xterm has been ported to.
var TIOCGWINSZ*{.importc, header: "<sys/ioctl.h>".}: culong var TIOCGWINSZ*{.importc, header: "<sys/ioctl.h>".}: culong
type IOctl_WinSize* {.importc: "struct winsize", header: "<sys/ioctl.h>", type IOctl_WinSize* = object
final, pure.} = object
ws_row*, ws_col*, ws_xpixel*, ws_ypixel*: cushort ws_row*, ws_col*, ws_xpixel*, ws_ypixel*: cushort
proc ioctl*(fd: cint, request: culong, reply: ptr IOctl_WinSize): int {. proc ioctl*(fd: cint, request: culong, reply: ptr IOctl_WinSize): int {.

View file

@ -116,16 +116,15 @@ template hasKeyOrPutImpl(enlarge) {.dirty.} =
maybeRehashPutImpl(enlarge) maybeRehashPutImpl(enlarge)
else: result = true else: result = true
proc default[T](t: typedesc[T]): T {.inline.} = discard template default[T](t: typedesc[T]): T =
var v: T
v
template delImpl() {.dirty.} = template delImpl() {.dirty.} =
var hc: Hash var hc: Hash
var i = rawGet(t, key, hc) var i = rawGet(t, key, hc)
let msk = maxHash(t) let msk = maxHash(t)
if i >= 0: if i >= 0:
t.data[i].hcode = 0
t.data[i].key = default(type(t.data[i].key))
t.data[i].val = default(type(t.data[i].val))
dec(t.counter) dec(t.counter)
block outer: block outer:
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1 while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1

View file

@ -154,6 +154,10 @@ proc add*(headers: HttpHeaders, key, value: string) =
else: else:
headers.table[key.toLowerAscii].add(value) headers.table[key.toLowerAscii].add(value)
proc del*(headers: HttpHeaders, key: string) =
## Delete the header entries associated with ``key``
headers.table.del(key.toLowerAscii)
iterator pairs*(headers: HttpHeaders): tuple[key, value: string] = iterator pairs*(headers: HttpHeaders): tuple[key, value: string] =
## Yields each key, value pair. ## Yields each key, value pair.
for k, v in headers.table: for k, v in headers.table:

View file

@ -83,6 +83,13 @@ when defined(windows):
return int(csbi.srWindow.Right - csbi.srWindow.Left + 1) return int(csbi.srWindow.Right - csbi.srWindow.Left + 1)
return 0 return 0
proc terminalHeightIoctl*(handles: openArray[Handle]): int =
var csbi: CONSOLE_SCREEN_BUFFER_INFO
for h in handles:
if getConsoleScreenBufferInfo(h, addr csbi) != 0:
return int(csbi.srWindow.Bottom - csbi.srWindow.Top + 1)
return 0
proc terminalWidth*(): int = proc terminalWidth*(): int =
var w: int = 0 var w: int = 0
w = terminalWidthIoctl([ getStdHandle(STD_INPUT_HANDLE), w = terminalWidthIoctl([ getStdHandle(STD_INPUT_HANDLE),
@ -91,6 +98,14 @@ when defined(windows):
if w > 0: return w if w > 0: return w
return 80 return 80
proc terminalHeight*(): int =
var h: int = 0
h = terminalHeightIoctl([ getStdHandle(STD_INPUT_HANDLE),
getStdHandle(STD_OUTPUT_HANDLE),
getStdHandle(STD_ERROR_HANDLE) ] )
if h > 0: return h
return 0
proc setConsoleCursorPosition(hConsoleOutput: HANDLE, proc setConsoleCursorPosition(hConsoleOutput: HANDLE,
dwCursorPosition: COORD): WINBOOL{. dwCursorPosition: COORD): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "SetConsoleCursorPosition".} stdcall, dynlib: "kernel32", importc: "SetConsoleCursorPosition".}
@ -177,7 +192,17 @@ else:
return int(win.ws_col) return int(win.ws_col)
return 0 return 0
proc terminalHeightIoctl*(fds: openArray[int]): int =
## Returns terminal height from first fd that supports the ioctl.
var win: IOctl_WinSize
for fd in fds:
if ioctl(cint(fd), TIOCGWINSZ, addr win) != -1:
return int(win.ws_row)
return 0
var L_ctermid{.importc, header: "<stdio.h>".}: cint var L_ctermid{.importc, header: "<stdio.h>".}: cint
proc terminalWidth*(): int = proc terminalWidth*(): int =
## Returns some reasonable terminal width from either standard file ## Returns some reasonable terminal width from either standard file
## descriptors, controlling terminal, environment variables or tradition. ## descriptors, controlling terminal, environment variables or tradition.
@ -195,6 +220,29 @@ else:
return w return w
return 80 #Finally default to venerable value return 80 #Finally default to venerable value
proc terminalHeight*(): int =
## Returns some reasonable terminal height from either standard file
## descriptors, controlling terminal, environment variables or tradition.
## Zero is returned if the height could not be determined.
var h = terminalHeightIoctl([0, 1, 2]) # Try standard file descriptors
if h > 0: return h
var cterm = newString(L_ctermid) # Try controlling tty
var fd = open(ctermid(cstring(cterm)), O_RDONLY)
if fd != -1:
h = terminalHeightIoctl([ int(fd) ])
discard close(fd)
if h > 0: return h
var s = getEnv("LINES") # Try standard env var
if len(s) > 0 and parseInt(string(s), h) > 0 and h > 0:
return h
return 0 # Could not determine height
proc terminalSize*(): tuple[w, h: int] =
## Returns the terminal width and height as a tuple. Internally calls
## `terminalWidth` and `terminalHeight`, so the same assumptions apply.
result = (terminalWidth(), terminalHeight())
when defined(windows): when defined(windows):
proc setCursorVisibility(f: File, visible: bool) = proc setCursorVisibility(f: File, visible: bool) =
var ccsi: CONSOLE_CURSOR_INFO var ccsi: CONSOLE_CURSOR_INFO

View file

@ -91,13 +91,24 @@ proc rawTag*(n: XmlNode): string {.inline.} =
shallowCopy(result, n.fTag) shallowCopy(result, n.fTag)
proc innerText*(n: XmlNode): string = proc innerText*(n: XmlNode): string =
## gets the inner text of `n`. `n` has to be an ``xnElement`` node. Only ## gets the inner text of `n`:
## ``xnText`` and ``xnEntity`` nodes are considered part of `n`'s inner text, ##
## other child nodes are silently ignored. ## - If `n` is `xnText` or `xnEntity`, returns its content.
## - If `n` is `xnElement`, runs recursively on each child node and
## concatenates the results.
## - Otherwise returns an empty string.
proc worker(res: var string, n: XmlNode) =
case n.k
of xnText, xnEntity:
res.add(n.fText)
of xnElement:
for sub in n.s:
worker(res, sub)
else:
discard
result = "" result = ""
assert n.k == xnElement worker(result, n)
for i in 0 .. n.s.len-1:
if n.s[i].k in {xnText, xnEntity}: result.add(n.s[i].fText)
proc tag*(n: XmlNode): string {.inline.} = proc tag*(n: XmlNode): string {.inline.} =
## gets the tag name of `n`. `n` has to be an ``xnElement`` node. ## gets the tag name of `n`. `n` has to be an ``xnElement`` node.

View file

@ -153,13 +153,19 @@ template setColor(c, col) =
proc writeCell(msg: cstring, c: PCell) = proc writeCell(msg: cstring, c: PCell) =
var kind = -1 var kind = -1
if c.typ != nil: kind = ord(c.typ.kind) var typName: cstring = "nil"
if c.typ != nil:
kind = ord(c.typ.kind)
when defined(nimTypeNames):
if not c.typ.name.isNil:
typName = c.typ.name
when leakDetector: when leakDetector:
c_fprintf(stdout, "[GC] %s: %p %d rc=%ld from %s(%ld)\n", c_fprintf(stdout, "[GC] %s: %p %d %s rc=%ld from %s(%ld)\n",
msg, c, kind, c.refcount shr rcShift, c.filename, c.line) msg, c, kind, typName, c.refcount shr rcShift, c.filename, c.line)
else: else:
c_fprintf(stdout, "[GC] %s: %p %d rc=%ld; color=%ld\n", c_fprintf(stdout, "[GC] %s: %p %d %s rc=%ld; color=%ld\n",
msg, c, kind, c.refcount shr rcShift, c.color) msg, c, kind, typName, c.refcount shr rcShift, c.color)
template gcTrace(cell, state: expr): stmt {.immediate.} = template gcTrace(cell, state: expr): stmt {.immediate.} =
when traceGC: traceCell(cell, state) when traceGC: traceCell(cell, state)

View file

@ -293,6 +293,8 @@ proc SSL_get_error*(s: SslPtr, ret_code: cInt): cInt{.cdecl, dynlib: DLLSSLName,
proc SSL_accept*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.} proc SSL_accept*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.}
proc SSL_pending*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.} proc SSL_pending*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.}
proc BIO_new_mem_buf*(data: pointer, len: cint): BIO{.cdecl,
dynlib: DLLSSLName, importc.}
proc BIO_new_ssl_connect*(ctx: SslCtx): BIO{.cdecl, proc BIO_new_ssl_connect*(ctx: SslCtx): BIO{.cdecl,
dynlib: DLLSSLName, importc.} dynlib: DLLSSLName, importc.}
proc BIO_ctrl*(bio: BIO, cmd: cint, larg: int, arg: cstring): int{.cdecl, proc BIO_ctrl*(bio: BIO, cmd: cint, larg: int, arg: cstring): int{.cdecl,

View file

@ -0,0 +1,28 @@
import unittest
import httpcore
suite "httpcore":
test "HttpCode":
assert $Http418 == "418 I'm a teapot"
assert Http418.is4xx() == true
assert Http418.is2xx() == false
test "headers":
var h = newHttpHeaders()
assert h.len == 0
h.add("Cookie", "foo")
assert h.len == 1
assert h.hasKey("cooKIE")
assert h["Cookie"] == "foo"
assert h["cookie"] == "foo"
h["cookie"] = @["bar", "x"]
assert h["Cookie"] == "bar"
assert h["Cookie", 1] == "x"
assert h["Cookie"].contains("BaR") == true
assert h["Cookie"].contains("X") == true
assert "baR" in h["cookiE"]
h.del("coOKie")
assert h.len == 0

View file

@ -1,6 +1,11 @@
discard """ discard """
file: "txmltree.nim" file: "txmltree.nim"
output: "true" output: '''true
true
true
true
true
'''
""" """
import xmltree, strtabs import xmltree, strtabs
@ -9,5 +14,14 @@ var x = <>a(href="nim.de", newText("www.nim-test.de"))
echo($x == "<a href=\"nim.de\">www.nim-test.de</a>") echo($x == "<a href=\"nim.de\">www.nim-test.de</a>")
echo(newText("foo").innerText == "foo")
echo(newEntity("bar").innerText == "bar")
echo(newComment("baz").innerText == "")
let y = newXmlTree("x", [
newText("foo"),
newXmlTree("y", [
newText("bar")
])
])
echo(y.innerText == "foobar")

View file

@ -39,7 +39,8 @@ Library Additions
``deques`` provides a superset of ``queues`` API with clear naming. ``deques`` provides a superset of ``queues`` API with clear naming.
``queues`` module is now deprecated and will be removed in the future. ``queues`` module is now deprecated and will be removed in the future.
- Added ``hideCursor`` and ``showCursor`` to the ``terminal`` - Added ``hideCursor``, ``showCursor``, ``terminalWidth``,
``terminalWidthIoctl`` and ``terminalSize`` to the ``terminal``
`(doc) <http://nim-lang.org/docs/terminal.html>`_ module. `(doc) <http://nim-lang.org/docs/terminal.html>`_ module.