about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-01-23 21:27:19 +0100
committerbptato <nincsnevem662@gmail.com>2021-01-23 21:27:19 +0100
commit09feae49e82e36b9a9b9f39e933f78cf7abec1ec (patch)
treedbf864efe4fd8a5001ea395ff57a79cbded5990c
parented3886ab51a76f184d023d79e085f928427b8328 (diff)
downloadchawan-09feae49e82e36b9a9b9f39e933f78cf7abec1ec.tar.gz
eeeh still not really working with unicode
-rw-r--r--buffer.nim97
-rw-r--r--display.nim61
-rw-r--r--fmttext.nim8
-rw-r--r--htmlelement.nim6
-rw-r--r--keymap56
-rw-r--r--search.html1586
-rw-r--r--twtio.nim20
-rw-r--r--twtstr.nim253
8 files changed, 338 insertions, 1749 deletions
diff --git a/buffer.nim b/buffer.nim
index f7cac95b..fcc24b76 100644
--- a/buffer.nim
+++ b/buffer.nim
@@ -2,6 +2,7 @@ import options
 import uri
 import tables
 import strutils
+import unicode
 
 import fusion/htmlparser/xmltree
 
@@ -9,6 +10,7 @@ import termattrs
 import htmlelement
 import twtio
 import enums
+import twtstr
 
 type
   Buffer* = ref BufferObj
@@ -52,21 +54,26 @@ func lastLine*(buffer: Buffer): int =
 func lastVisibleLine*(buffer: Buffer): int =
   return min(buffer.fromY + buffer.height - 1, buffer.lastLine())
 
+func currentLine*(buffer: Buffer): int =
+  return buffer.cursorY - 1
+
+func textBetween*(buffer: Buffer, s: int, e: int): string =
+  return buffer.text.runeSubstr(s, e - s)
+
 #doesn't include newline
 func lineLength*(buffer: Buffer, line: int): int =
   assert buffer.lines.len > line
-  let len = buffer.lines[line] - buffer.lines[line - 1] - 2
+  let str = buffer.textBetween(buffer.lines[line - 1], buffer.lines[line])
+  let len = mk_wcswidth_cjk(str)
   if len >= 0:
     return len
   else:
     return 0
 
-func currentLine*(buffer: Buffer): int =
-  return buffer.cursorY - 1
-
 func rawLineLength*(buffer: Buffer, line: int): int =
   assert buffer.rawlines.len > line
-  let len = buffer.rawlines[line] - buffer.rawlines[line - 1] - 2
+  let str = buffer.textBetween(buffer.rawlines[line - 1], buffer.rawlines[line])
+  let len = mk_wcswidth_cjk(str)
   if len >= 0:
     return len
   else:
@@ -84,9 +91,9 @@ func cursorAtLineEnd*(buffer: Buffer): bool =
 func atPercentOf*(buffer: Buffer): int =
   return (100 * buffer.cursorY) div buffer.lastLine()
 
+
 func visibleText*(buffer: Buffer): string = 
-  result = buffer.text.substr(buffer.lines[buffer.fromY], buffer.lines[buffer.lastVisibleLine()])
-  result.stripLineEnd()
+  return buffer.textBetween(buffer.lines[buffer.fromY], buffer.lines[buffer.lastVisibleLine()])
 
 func lastNode*(buffer: Buffer): HtmlNode =
   return buffer.nodes[^1]
@@ -113,9 +120,6 @@ func findSelectedElement*(buffer: Buffer): Option[HtmlElement] =
 func cursorAt*(buffer: Buffer): int =
   return buffer.rawlines[buffer.currentLine()] + buffer.cursorX
 
-func cursorChar*(buffer: Buffer): char =
-  return buffer.text[buffer.cursorAt()]
-
 func canScroll*(buffer: Buffer): bool =
   return buffer.lastLine() > buffer.height
 
@@ -211,9 +215,9 @@ proc scrollTo*(buffer: Buffer, y: int): bool =
 
 proc cursorTo*(buffer: Buffer, x: int, y: int): bool =
   result = false
-  buffer.cursorY = min(max(y, 0), buffer.lastLine())
-  if buffer.fromY > buffer.cursorY:
-    buffer.fromY = min(buffer.cursorY - 1, buffer.lastLine() - buffer.height)
+  buffer.cursorY = min(max(y, 1), buffer.lastLine())
+  if buffer.fromY >= buffer.cursorY:
+    buffer.fromY = max(buffer.cursorY - 1, 0)
     result = true
   elif buffer.fromY + buffer.height <= buffer.cursorY:
     buffer.fromY = max(buffer.cursorY - buffer.height + 1, 0)
@@ -251,7 +255,7 @@ proc cursorUp*(buffer: Buffer): bool =
 
 proc cursorRight*(buffer: Buffer): bool =
   if buffer.cursorX < buffer.currentRawLineLength():
-    buffer.cursorX += 1
+    buffer.cursorX += mk_wcwidth_cjk(buffer.rawtext.runeAt(buffer.rawlines[buffer.currentLine()] + buffer.cursorX))
     buffer.xend = 0
   else:
     buffer.xend = buffer.cursorX
@@ -259,6 +263,7 @@ proc cursorRight*(buffer: Buffer): bool =
 
 proc cursorLeft*(buffer: Buffer): bool =
   if buffer.cursorX > 0:
+    buffer.cursorX -= mk_wcwidth_cjk(buffer.rawtext.runeAt(buffer.rawlines[buffer.currentLine()] + buffer.cursorX))
     buffer.cursorX -= 1
   buffer.xend = 0
   return false
@@ -271,24 +276,29 @@ proc cursorLineEnd*(buffer: Buffer) =
   buffer.cursorX = buffer.currentRawLineLength()
   buffer.xend = buffer.cursorX
 
-proc cursorNextNode*(buffer: Buffer):  bool =
-  if buffer.cursorAtLineEnd():
-    if buffer.cursorY < buffer.lastLine():
-      let ret = buffer.cursorDown()
-      buffer.cursorLineEnd()
-      return ret
-    else:
-      buffer.cursorLineBegin()
-      return false
+iterator revnodes*(buffer: Buffer): HtmlNode {.inline.} =
+  var i = buffer.nodes.len - 1
+  while i >= 0:
+    yield buffer.nodes[i]
+    i -= 1
 
-  let selectedNode = buffer.findSelectedNode()
-  var res = buffer.cursorRight()
-  if selectedNode.isNone:
-    return res
-  while buffer.findSelectedNode().isNone or buffer.findSelectedNode().get() == selectedNode.get():
-    if buffer.cursorAtLineEnd():
-      return res
-    res = buffer.cursorRight()
+proc cursorNextNode*(buffer: Buffer): bool =
+  for node in buffer.nodes:
+    if node.displayed():
+      if node.y > buffer.cursorY or (node.y == buffer.cursorY and node.x > buffer.cursorX):
+        return buffer.cursorTo(node.x, node.y)
+  buffer.cursorLineEnd()
+  return false
+
+proc cursorPrevNode*(buffer: Buffer): bool =
+  var prevnode: HtmlNode
+  for node in buffer.nodes:
+    if node.displayed():
+      if node.y >= buffer.cursorY and node.x >= buffer.cursorX and prevnode != nil:
+        return buffer.cursorTo(prevnode.x, prevnode.y)
+      prevnode = node
+  buffer.cursorLineBegin()
+  return false
 
 proc cursorNextWord*(buffer: Buffer): bool =
   if buffer.cursorAtLineEnd():
@@ -301,30 +311,11 @@ proc cursorNextWord*(buffer: Buffer): bool =
       return false
 
   var res = buffer.cursorRight()
-  while buffer.rawtext[buffer.rawlines[buffer.currentLine()] + buffer.cursorX] != ' ':
+  while buffer.rawtext.runeAt(buffer.rawlines[buffer.currentLine()] + buffer.cursorX) != Rune(' '):
     if buffer.cursorAtLineEnd():
       return res
     res = res or buffer.cursorRight()
 
-proc cursorPrevNode*(buffer: Buffer): bool =
-  if buffer.cursorX <= 1:
-    if buffer.cursorY > 1:
-      let res = buffer.cursorUp()
-      buffer.cursorLineEnd()
-      return res
-    else:
-      buffer.cursorLineBegin()
-      return false
-
-  let selectedNode = buffer.findSelectedNode()
-  var res = buffer.cursorLeft()
-  if selectedNode.isNone:
-    return res
-  while buffer.findSelectedNode().isNone or buffer.findSelectedNode().get() == selectedNode.get():
-    if buffer.cursorX == 0:
-      return res
-    res = res or buffer.cursorLeft()
-
 proc cursorPrevWord*(buffer: Buffer): bool =
   if buffer.cursorX <= 1:
     if buffer.cursorY > 1:
@@ -336,7 +327,7 @@ proc cursorPrevWord*(buffer: Buffer): bool =
       return false
 
   discard buffer.cursorLeft()
-  while buffer.rawtext[buffer.rawlines[buffer.currentLine()] + buffer.cursorX] != ' ':
+  while buffer.rawtext.runeAt(buffer.rawlines[buffer.currentLine()] + buffer.cursorX) != Rune(' '):
     if buffer.cursorX == 0:
       return false
     discard buffer.cursorLeft()
@@ -461,7 +452,7 @@ proc gotoAnchor*(buffer: Buffer): bool =
   if buffer.document.location.anchor != "":
     let node =  buffer.getElementById(buffer.document.location.anchor)
     if node != nil:
-      return buffer.scrollTo(node.y)
+      return buffer.scrollTo(max(node.y - buffer.height div 2, 0))
   return false
 
 proc setLocation*(buffer: Buffer, uri: Uri) =
diff --git a/display.nim b/display.nim
index ec4552de..30081c57 100644
--- a/display.nim
+++ b/display.nim
@@ -64,7 +64,6 @@ proc addSpaces(buffer: Buffer, state: var RenderState, n: int) =
   state.atchar += n
   state.atrawchar += n
 
-const runeSpace = " ".toRunes()[0]
 proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) =
   state.lastwidth = 0
   var n = 0
@@ -77,30 +76,31 @@ proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) =
       continue
 
     for r in w.runes:
+      if r == Rune(' '):
+        var s: Rune
+        fastRuneAt(rawword, 0, s, false)
+        #if s == Rune(' ') and prevl:
+        #  fmtword = fmtword.runeSubstr(1)
+        #  rawword = rawword.runeSubstr(1)
+        #  state.x -= 1
+        buffer.writefmt(fmtword)
+        buffer.writeraw(rawword)
+        state.atchar += fmtword.runeLen()
+        state.atrawchar += rawword.runeLen()
+        var a = rawword
+        fmtword = ""
+        rawword = ""
+
       fmtword &= r
       rawword &= r
 
-      state.x += 1
+      state.x += mk_wcwidth_cjk(r)
 
       if prevl:
-        state.x += rawword.runeLen
+        state.x += mk_wcswidth_cjk(rawword)
         prevl = false
 
-      if r == runeSpace:
-        buffer.writefmt(fmtword)
-        buffer.writeraw(rawword)
-        state.atchar += fmtword.len
-        state.atrawchar += rawword.runeLen()
-        fmtword = ""
-        rawword = ""
-
       if state.x > buffer.width:
-        if buffer.rawtext.len > 0 and buffer.rawtext[^1] == ' ':
-          buffer.rawtext = buffer.rawtext.substr(0, buffer.rawtext.len - 2)
-          buffer.text = buffer.text.substr(0, buffer.text.len - 2)
-          state.atchar -= 1
-          state.atrawchar -= 1
-          state.x -= 1
         state.lastwidth = max(state.lastwidth, state.x)
         buffer.flushLine(state)
         state.x = -1
@@ -112,7 +112,7 @@ proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) =
 
   buffer.writefmt(fmtword)
   buffer.writeraw(rawword)
-  state.atchar += fmtword.len
+  state.atchar += fmtword.runeLen()
   state.atrawchar += rawword.runeLen()
   state.lastwidth = max(state.lastwidth, state.x)
 
@@ -150,7 +150,7 @@ proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) =
     buffer.addSpaces(state, state.indent)
     buffer.write(listchar)
     state.x += listchar.runeLen()
-    state.atchar += listchar.len
+    state.atchar += listchar.runeLen()
     state.atrawchar += listchar.runeLen()
     buffer.addSpaces(state, 1)
 
@@ -169,7 +169,8 @@ proc postAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) =
   if node.closeblock:
     while state.blanklines < max(elem.margin, elem.marginbottom):
       buffer.flushLine(state)
-    state.indent -= elem.indent
+    if node.isElemNode():
+      state.indent -= elem.indent
 
   if elem.tagType == TAG_BR and not node.openblock:
     buffer.flushLine(state)
@@ -238,7 +239,7 @@ type
 proc setLastHtmlLine(buffer: Buffer, state: var RenderState) =
   if buffer.text.len != buffer.lines[^1]:
     state.atchar = buffer.text.len
-    state.atrawchar = buffer.rawtext.len
+    state.atrawchar = buffer.rawtext.runeLen()
   buffer.flushLine(state)
 
 proc renderHtml*(buffer: Buffer) =
@@ -284,8 +285,6 @@ proc statusMsgForBuffer(buffer: Buffer) =
 
 proc cursorBufferPos(buffer: Buffer) =
   var x = buffer.cursorX
-  if x > buffer.currentRawLineLength():
-    x = buffer.currentRawLineLength()
   var y = buffer.cursorY - 1 - buffer.fromY
   termGoto(x, y)
 
@@ -362,7 +361,7 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool =
         buffer.setLocation(parseUri(url))
         return true
     of ACTION_LINE_INFO:
-      statusMsg("line " & $buffer.cursorY & "/" & $buffer.lastLine() & " col " & $buffer.cursorX & "/" & $buffer.currentLineLength(), buffer.width)
+      statusMsg("line " & $buffer.cursorY & "/" & $buffer.lastLine() & " col " & $buffer.cursorX & "/" & $buffer.currentRawLineLength(), buffer.width)
       nostatus = true
     of ACTION_FEED_NEXT:
       feedNext = true
@@ -383,14 +382,14 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool =
     let sel = buffer.checkLinkSelection()
     if prevlink != nil and prevlink != buffer.selectedlink:
       termGoto(prevlink.x - buffer.fromX, prevlink.y - buffer.fromY - 1)
-      print(buffer.text.substr(prevlink.fmtchar, prevlink.fmtend))
-    if sel:
+      print(buffer.textBetween(prevlink.fmtchar, prevlink.fmtend).ansiReset())
+    if sel and buffer.selectedlink.y < buffer.fromY + buffer.height:
       termGoto(buffer.selectedlink.x - buffer.fromX, buffer.selectedlink.y - buffer.fromY - 1)
-      let str = buffer.text.substr(buffer.selectedlink.fmtchar, buffer.selectedlink.fmtend)
-      var i = str.findChar('\n') 
-      while i != -1:
-        print("".ansiStyle(styleUnderscore))
-        i = str.findChar('\n', i + 1)
+      let str = buffer.textBetween(buffer.selectedlink.fmtchar, buffer.selectedlink.fmtend)
+      #var i = str.findChar(Rune('\n'))
+      #while i != -1:
+      #  print("".ansiStyle(styleUnderscore))
+      #  i = str.findChar(Rune('\n'), i + 1)
       print(str.ansiStyle(styleUnderscore).ansiReset())
 
     if not nostatus:
diff --git a/fmttext.nim b/fmttext.nim
deleted file mode 100644
index 148ac626..00000000
--- a/fmttext.nim
+++ /dev/null
@@ -1,8 +0,0 @@
-type
-  FmtText* = object
-    str*: string
-    beginStyle*: string
-    endStyle*: string
-
-func `$`*(stt: FmtText): string =
-  return stt.beginStyle & stt.str & stt.endStyle
diff --git a/htmlelement.nim b/htmlelement.nim
index 8a61acb7..3ead2f15 100644
--- a/htmlelement.nim
+++ b/htmlelement.nim
@@ -355,14 +355,14 @@ proc getHtmlNode*(xmlElement: XmlNode, parent: HtmlNode): HtmlNode =
   result.parentNode = parent
   if parent.isElemNode():
     result.parentElement = HtmlElement(parent)
-
-  result.rawtext = result.getRawText()
-  result.fmttext = result.getFmtText()
   if parent.childNodes.len > 0:
     result.previousSibling = parent.childNodes[^1]
     result.previousSibling.nextSibling = result
   parent.childNodes.add(result)
 
+  result.rawtext = result.getRawText()
+  result.fmttext = result.getFmtText()
+
 func newDocument*(): Document =
   new(result)
   result.nodeType = NODE_DOCUMENT
diff --git a/keymap b/keymap
deleted file mode 100644
index 51708c8e..00000000
--- a/keymap
+++ /dev/null
@@ -1,56 +0,0 @@
-#"normal mode" keybindings
-nmap q ACTION_QUIT
-nmap h ACTION_CURSOR_LEFT
-nmap j ACTION_CURSOR_DOWN
-nmap k ACTION_CURSOR_UP
-nmap l ACTION_CURSOR_RIGHT
-nmap \e[D ACTION_CURSOR_LEFT
-nmap \e[B ACTION_CURSOR_DOWN
-nmap \e[A ACTION_CURSOR_UP
-nmap \e[C ACTION_CURSOR_RIGHT
-nmap ^ ACTION_CURSOR_LINEBEGIN
-nmap $ ACTION_CURSOR_LINEEND
-nmap b ACTION_CURSOR_PREV_WORD
-nmap B ACTION_CURSOR_PREV_NODE
-nmap w ACTION_CURSOR_NEXT_WORD
-nmap W ACTION_CURSOR_NEXT_NODE
-nmap [ ACTION_CURSOR_PREV_LINK
-nmap ] ACTION_CURSOR_NEXT_LINK
-nmap H ACTION_CURSOR_TOP
-nmap M ACTION_CURSOR_MIDDLE
-nmap L ACTION_CURSOR_BOTTOM
-nmap C-d ACTION_HALF_PAGE_DOWN
-nmap C-u ACTION_HALF_PAGE_UP
-nmap C-f ACTION_PAGE_DOWN
-nmap C-b ACTION_PAGE_UP
-nmap \e[6~ ACTION_PAGE_DOWN
-nmap \e[5~ ACTION_PAGE_UP
-nmap C-e ACTION_SCROLL_DOWN
-nmap C-y ACTION_SCROLL_UP
-nmap J ACTION_SCROLL_DOWN
-nmap K ACTION_SCROLL_UP
-nmap C-m ACTION_CLICK
-nmap C-j ACTION_CLICK
-nmap C-l ACTION_CHANGE_LOCATION
-nmap U ACTION_RELOAD
-nmap r ACTION_RESHAPE
-nmap R ACTION_REDRAW
-nmap gg ACTION_CURSOR_FIRST_LINE
-nmap G ACTION_CURSOR_LAST_LINE
-nmap \e[H ACTION_CURSOR_FIRST_LINE
-nmap \e[F ACTION_CURSOR_LAST_LINE
-
-#line editing keybindings
-lemap C-m ACTION_LINED_SUBMIT
-lemap C-j ACTION_LINED_SUBMIT
-lemap C-h ACTION_LINED_BACKSPACE
-lemap C-? ACTION_LINED_BACKSPACE
-lemap C-c ACTION_LINED_CANCEL
-lemap \eb ACTION_LINED_PREV_WORD
-lemap \ef ACTION_LINED_NEXT_WORD
-lemap C-b ACTION_LINED_BACK
-lemap C-f ACTION_LINED_FORWARD
-lemap C-u ACTION_LINED_CLEAR
-lemap C-k ACTION_LINED_KILL
-lemap C-w ACTION_LINED_KILL_WORD
-lemap C-v ACTION_LINED_ESC
diff --git a/search.html b/search.html
deleted file mode 100644
index d4c3facd..00000000
--- a/search.html
+++ /dev/null
@@ -1,1586 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
-  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=3.0, user-scalable=1;">
-  <meta name="referrer" content="origin">
-  <meta name="HandheldFriendly" content="true" />
-  <meta name="robots" content="noindex, nofollow" />
-  <title>rhetorische at DuckDuckGo</title>
-  <link title="DuckDuckGo (Lite)" type="application/opensearchdescription+xml" rel="search" href="//duckduckgo.com/opensearch_lite_v2.xml">
-  <link href="//duckduckgo.com/favicon.ico" rel="shortcut icon" />
-  <link rel="icon" href="//duckduckgo.com/favicon.ico" type="image/x-icon" />
-  <link rel="apple-touch-icon" href="//duckduckgo.com/assets/logo_icon128.v101.png">
-  <link rel="image_src" href="//duckduckgo.com/assets/logo_homepage.normal.v101.png"/>
-
-  <style type="text/css">
-    html {
-      font-family: 'Helvetica Neue','Segoe UI', Arial, sans-serif;
-    }
-    .header {
-      font-size: 32px;
-      font-weight: bold;
-      color: #dc5e47;
-      height: 32px;
-      line-height: 32px;
-    }
-    .extra {
-      margin-top: 0px;
-      margin-bottom: 0px;
-      height: 1.3em;
-    }
-    #end-spacer {
-      height: 10em;
-    }
-    body {
-      padding-left: 20px;
-      width: 90%;
-      margin-top: 0;
-    }
-    table {
-      border-collapse: collapse;
-      border-spacing: 0;
-    }
-    th, td {
-      margin: 0;
-      padding: 0;
-      font-size: 107.1%;
-    }
-    
-    span.no-results {
-      font-size: 110%;
-    }
-    .query {
-      height: 28px;
-      border-color: #dc5e47;
-      border-style: solid solid solid solid;
-      border-width: 1px 1px 1px 1px;
-      -moz-border-radius: 3px;
-      border-radius: 3px;
-      font-size: 20px;
-      padding: 5px 6px;
-      text-align: left;
-      width: 55%; 
-      max-width: 600px;
-    }
-    .submit {
-      padding-left: 7px;
-      padding-right: 7px;
-      height: 40px;
-      font-size: 16px;
-    /*
-      color: white;
-      background-color: #019801;
-      border-style: none none none;
-      -moz-border-radius: 3px;
-      border-radius: 3px;
-      padding: 5px 6px;
-    */
-      cursor: pointer;
-    }
-    .link-text {
-      color: #662200;
-      font-size: 77.4%;
-    }
-    .result-link {
-      font-size: 107.1%;
-    }
-    .result-snippet {
-      font-size: 77.4%;
-    }
-
-    .result-sponsored:hover {
-      background: #eae3ad !important;
-    }
-
-    .result-sponsored {
-      background: #fff7d0;
-      border: 1px solid #fef9eb !important;
-    }
-
-    .did-you-mean {
-      color: #EE4444;
-      font-size: 107.1%;
-    }
-    a {
-      text-decoration: none;
-      color: #1168CC;
-    }
-    a:hover {
-      text-decoration: underline;
-    }
-    a:visited {
-      color: #6830BB;
-    }
-    .timestamp {
-      color:#555555;
-      font-size: 77.4%;
-    }
-    .navbutton {
-      background-color:transparent;
-      border: 0px none;
-      cursor:pointer;
-      font-size:16px;
-      font-weight:bold;
-      text-decoration: underline;
-      color: #1168CC;
-      border-bottom: 1px solid transparent;
-      padding: 0px;
-    }
-
-    .results--powered {
-      color: #888;
-      display: inline;
-      margin-left: 10px;
-      position: absolute;
-      bottom: 10px;
-      right: 10px;
-    }
-    .results--powered a {
-      color: #888;
-      font-size: 13px;
-    }
-    .results--powered__badge {
-      vertical-align: baseline;
-    }
-    .badge--yahoo {
-      background-image: url("//duckduckgo.com/assets/attribution/yahoo.v103.png");
-      background-size: 55px 13px;
-      width: 55px;
-      height: 13px;
-    }
-    .badge, .results--powered__badge {
-      text-indent: -999999px;
-      display: inline-block;
-      vertical-align: middle;
-      position: relative;
-      background-position: 50% 50%;
-      background-repeat: no-repeat;
-    }
-
-    .next_form {
-      float: left;
-    }
-    .prev_form {
-      float: left;
-    }
-
-    .filters {
-      margin-top: 10px;
-    }
-
-    @media only screen and (max-device-width: 700px) {
-      body {
-        width: 95%;
-        margin-left: 10px;
-        padding: 0;
-      }
-      .query {
-        width: 180px;
-        font-size: 12px;
-      }
-      .submit {
-        font-size: 12px;
-      }
-    }
-
-
-    @media only screen and (max-device-width: 701px) and (orientation:landscape) 
-    {
-      .query {
-        width: 273px;
-      }
-    }
-
-  </style>
-</head>
-
-<body>
-<p class='extra'>&nbsp;</p>
-<div class="header">DuckDuckGo</div>
-<p class='extra'>&nbsp;</p>
-
-<form action="/lite/" method="post">
-  <input class='query' type="text" size="40" name="q" value="rhetorische" >
-  <input class='submit' type="submit" value="Search" >
-    
-    
-    
-    
-
-<div class="filters">
-
-<select class="submit" name="kl">
-
-  <option value="" >All Regions</option>
-
-  <option value="ar-es" >Argentina</option>
-
-  <option value="au-en" >Australia</option>
-
-  <option value="at-de" >Austria</option>
-
-  <option value="be-fr" >Belgium (fr)</option>
-
-  <option value="be-nl" >Belgium (nl)</option>
-
-  <option value="br-pt" >Brazil</option>
-
-  <option value="bg-bg" >Bulgaria</option>
-
-  <option value="ca-en" >Canada (en)</option>
-
-  <option value="ca-fr" >Canada (fr)</option>
-
-  <option value="ct-ca" >Catalonia</option>
-
-  <option value="cl-es" >Chile</option>
-
-  <option value="cn-zh" >China</option>
-
-  <option value="co-es" >Colombia</option>
-
-  <option value="hr-hr" >Croatia</option>
-
-  <option value="cz-cs" >Czech Republic</option>
-
-  <option value="dk-da" >Denmark</option>
-
-  <option value="ee-et" >Estonia</option>
-
-  <option value="fi-fi" >Finland</option>
-
-  <option value="fr-fr" >France</option>
-
-  <option value="de-de" >Germany</option>
-
-  <option value="gr-el" >Greece</option>
-
-  <option value="hk-tzh" >Hong Kong</option>
-
-  <option value="hu-hu" >Hungary</option>
-
-  <option value="in-en" >India (en)</option>
-
-  <option value="id-en" >Indonesia (en)</option>
-
-  <option value="ie-en" >Ireland</option>
-
-  <option value="il-en" >Israel (en)</option>
-
-  <option value="it-it" >Italy</option>
-
-  <option value="jp-jp" >Japan</option>
-
-  <option value="kr-kr" >Korea</option>
-
-  <option value="lv-lv" >Latvia</option>
-
-  <option value="lt-lt" >Lithuania</option>
-
-  <option value="my-en" >Malaysia (en)</option>
-
-  <option value="mx-es" >Mexico</option>
-
-  <option value="nl-nl" >Netherlands</option>
-
-  <option value="nz-en" >New Zealand</option>
-
-  <option value="no-no" >Norway</option>
-
-  <option value="pk-en" >Pakistan (en)</option>
-
-  <option value="pe-es" >Peru</option>
-
-  <option value="ph-en" >Philippines (en)</option>
-
-  <option value="pl-pl" >Poland</option>
-
-  <option value="pt-pt" >Portugal</option>
-
-  <option value="ro-ro" >Romania</option>
-
-  <option value="ru-ru" >Russia</option>
-
-  <option value="xa-ar" >Saudi Arabia</option>
-
-  <option value="sg-en" >Singapore</option>
-
-  <option value="sk-sk" >Slovakia</option>
-
-  <option value="sl-sl" >Slovenia</option>
-
-  <option value="za-en" >South Africa</option>
-
-  <option value="es-ca" >Spain (ca)</option>
-
-  <option value="es-es" >Spain (es)</option>
-
-  <option value="se-sv" >Sweden</option>
-
-  <option value="ch-de" >Switzerland (de)</option>
-
-  <option value="ch-fr" >Switzerland (fr)</option>
-
-  <option value="tw-tzh" >Taiwan</option>
-
-  <option value="th-en" >Thailand (en)</option>
-
-  <option value="tr-tr" >Turkey</option>
-
-  <option value="us-en" >US (English)</option>
-
-  <option value="us-es" >US (Spanish)</option>
-
-  <option value="ua-uk" >Ukraine</option>
-
-  <option value="uk-en" >United Kingdom</option>
-
-  <option value="vn-en" >Vietnam (en)</option>
-
-</select>
-
-<select class="submit" name="df">
-
-  <option value="" selected>Any Time</option>
-
-  <option value="d" >Past Day</option>
-
-  <option value="w" >Past Week</option>
-
-  <option value="m" >Past Month</option>
-
-  <option value="y" >Past Year</option>
-
-</select>
-
-</form>
-
-
-  <p class="extra">&nbsp;</p>
-    <table border="0"><tr>
-    <td>
-      
-    </td>
-    <td>
-      
-        <form action="/lite/" method="post">
-          <!-- <a rel="next" href="/lite/?q=rhetorische&amp;v=l&amp;l=us-en&amp;p=-1&amp;s=30&amp;ex=-1&amp;o=json&amp;dl=en&amp;ct=AT&amp;ss_mkt=us&amp;sp=0&amp;vqd=3-317185285141429630887821832006479517976-203666168757858907862519566843911872030&amp;dc=27&amp;api=%2Fd.js">Next Page &gt;</a> //-->
-          <input type="submit" class='navbutton' value="Next Page &gt;">
-          <input type="hidden" name="q" value="rhetorische">
-          <input type="hidden" name="s" value="30">
-          <input type="hidden" name="nextParams" value="">
-          <input type="hidden" name="v" value="l">
-          <input type="hidden" name="o" value="json">
-          <input type="hidden" name="dc" value="27">
-          <input type="hidden" name="api" value="/d.js">
-          <input type="hidden" name="vqd" value="3-317185285141429630887821832006479517976-203666168757858907862519566843911872030">
-              
-              
-          
-          <input name="kl" value="wt-wt" type="hidden">
-              
-              
-              
-              
-        </form>
-      
-    </td>
-    </tr></table>
-
-
-
-
-<p class='extra'>&nbsp;</p>
-
-<table border="0">
-
-</table>
-
-<table border="0">
-
-<!-- Web results are present -->
-
-
-
-
-
- <tr>
-
-    <td valign="top">1.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fde.wikipedia.org%2Fwiki%2FRhetorische_Frage" class='result-link'><b>Rhetorische</b> Frage - Wikipedia</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Die <b>rhetorische</b> Frage gilt als Stilmittel der Rhetorik. <b>Rhetorische</b> Fragen dienen nicht dem Informationsgewinn, sondern sind sprachliche Mittel der Beeinflussung. Semantisch stehen <b>rhetorische</b> Fragen den Behauptungen nahe.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>de.wikipedia.org/wiki/Rhetorische_Frage</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">2.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwortwuchs.net%2Fstilmittel%2Frhetorische%2Dfrage%2F" class='result-link'><b>Rhetorische</b> Frage | Definition, Wirkung und Beispiel</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Die <b>rhetorische</b> Frage ist ein Stilmittel der Rhetorik. Äußerlich betrachtet, unterscheidet sich eine <b>rhetorische</b> Wer eine <b>rhetorische</b> Frage stellt, fragt nicht nach Informationen, sondern versucht...
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>wortwuchs.net/stilmittel/rhetorische-frage/</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">3.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.lingvolive.com%2Fru%2Dru%2Ftranslate%2Fde%2Dru%2FRhetorische" class='result-link'>Переводы «<b>Rhetorische</b>» (De-Ru) на ABBYY Lingvo Live</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Jahrzehnte später, beim Klassentreffen erinnern sich Frauen selten an die <b>rhetorische</b> Gewandtheit, die Forschheit oder die Bereitschaft der Mitschülerinnen, ungestüm eigene Interessen zu vertreten.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.lingvolive.com/ru-ru/translate/de-ru/Rhetorische</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">4.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=http%3A%2F%2Fwww.abipedia.de%2Frhetorische%2Dmittel.php" class='result-link'><b>Rhetorische</b> Mittel</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      <b>Rhetorische</b> Mittel sind allgegenwärtig. Kaum ein Werbespruch kommt heute noch ohne eine <b>rhetorische</b> Figur aus. Auch wir selbst benutzen sie ständig, ohne uns dabei bewusst zu sein.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.abipedia.de/rhetorische-mittel.php</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">5.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fcontext.reverso.net%2Ftranslation%2Fgerman%2Denglish%2Frhetorische" class='result-link'><b>rhetorische</b> - Translation into English - examples... | Reverso Context</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Translations in context of &quot;<b>rhetorische</b>&quot; in German-English from Reverso Context: <b>rhetorische</b> Frage, <b>rhetorische</b> Figur. Das darf nicht nur eine <b>rhetorische</b> Verpflichtung bleiben.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>context.reverso.net/translation/german-english/rhetorische</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">6.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fen.glosbe.com%2Fde%2Fen%2Frhetorische" class='result-link'><b>rhetorische</b> - translation - German-English Dictionary - Glosbe</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      translation and definition &quot;<b>rhetorische</b>&quot;, German-English Dictionary online. Inflected form of rhetorisch. Automatic translation: <b>rhetorische</b>.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>en.glosbe.com/de/en/rhetorische</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">7.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.dw.com%2Fde%2Frhetorische%2Dfiguren%2Fa%2D1606602" class='result-link'><b>Rhetorische</b> Figuren | Alltagsdeutsch - Podcast | DW | 08.11.2006</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      <b>Rhetorische</b> Figuren sind doch keine Menschen! Diese <b>rhetorische</b> Figur zählt übrigens zu den grammatischen Figuren, die bewusst vom korrekten grammatischen Sprachgebrauch abweichen.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.dw.com/de/rhetorische-figuren/a-1606602</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">8.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fkarrierebibel.de%2Frhetorische%2Dfragen%2F" class='result-link'><b>Rhetorische</b> Fragen: 25 Beispiele - und ihre Subbotschaften</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Auf den ersten Blick ist die Frage völlig harmlos: „Wie geht es dir?&quot; Oft handelt es sich dabei nur um eine Plattitüde, eine Art Eisbrecher beim Smalltalk, um ins Gespräch zu finden.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>karrierebibel.de/rhetorische-fragen/</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">9.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.schreiben.net%2Fartikel%2Frhetorische%2Dfrage%2D3620%2F" class='result-link'><b>Rhetorische</b> Frage: Definition, Wirkung &amp; 16 typische Beispiele</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Alles Wissenswerte über die <b>rhetorische</b> Frage: Definition, Wirkung, typische Beispiele und wie sie sich von der Suggestivfrage unterscheidet.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.schreiben.net/artikel/rhetorische-frage-3620/</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">10.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Flearnattack.de%2Fjournal%2F40%2Dwichtige%2Drhetorische%2Dmittel%2Dtextanalyse%2F" class='result-link'>40 wichtige <b>rhetorische</b> Mittel für deine nächste Textanalyse</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      ©iStockphoto.com/avosb. <b>Rhetorische</b> Mittel sind heute in jedermanns Sprachgebrauch zu finden, doch sind wir uns dessen selten bewusst.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>learnattack.de/journal/40-wichtige-rhetorische-mittel-textanalyse/</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">11.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.multitran.com%2Fm.exe%3Fl1%3D3%26l2%3D2%26s%3Drhetorische" class='result-link'><b>rhetorische</b></a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      <b>rhetorische</b>: 5 фраз в 2 тематиках. Лингвистика.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.multitran.com/m.exe?l1=3&amp;l2=2&amp;s=rhetorische</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">12.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Funiversal_de_ru.academic.ru%2F755953%2Frhetorisch" class='result-link'>rhetorisch - это... Что такое rhetorisch?</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Rhetorisch — Rhetorisch, 1) was nach Anleitung u. Zweck der Redekunst gefertigt ist od. in Verbindung mit derselben steht, z.B. Rhetorischer Ausdruck, <b>Rhetorische</b> Figuren (s.u. Figur 2) B); 2)...
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>universal_de_ru.academic.ru/755953/rhetorisch</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">13.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.facebook.com%2Fafdnpd%2F" class='result-link'><b>Rhetorische</b> Perlen von AfD- und NPD-Anhängern und... | Facebook</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      See more of <b>Rhetorische</b> Perlen von AfD- und NPD-Anhängern und Verschwörungstheoretikern on Facebook.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.facebook.com/afdnpd/</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">14.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Ftwitter.com%2Frhetorischea" class='result-link'><b>Rhetorische</b> Antwort (@RhetorischeA) | Твиттер</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Последние твиты от <b>Rhetorische</b> Antwort (@RhetorischeA). Es gibt keine dummen Antworten! #primatechangenotclimatechange.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>twitter.com/rhetorischea</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">15.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fde.thefreedictionary.com%2Frhetorisch" class='result-link'>Rhetorisch Übersetzung rhetorisch Definition auf TheFreeDictionary</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      rhetorisch nicht steig. geh. die Rhetorik betreffend <b>rhetorische</b> Fähigkeiten haben/erwerben, eine rhetorisch gute Rede halten eine <b>rhetorische</b> Frage eine Frage, auf die man keine ernsthafte Antwort...
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>de.thefreedictionary.com/rhetorisch</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">16.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.inhaltsangabe.de%2Fwissen%2Fstilmittel%2Frhetorische%2Dfrage%2F" class='result-link'><b>Rhetorische</b> Frage (Stilmittel) - Definition, Merkmale und Beispiele</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Die <b>rhetorische</b> Frage gehört zu den ältesten und meist genutzten Stilmitteln der Rhetorik. Zu finden sind <b>rhetorische</b> Fragen in der Politik, der Literatur, in der Werbung und im ganz alltäglichen...
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.inhaltsangabe.de/wissen/stilmittel/rhetorische-frage/</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">17.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dclick.itkeller.lars.lernen%26hl%3Dsv" class='result-link'><b>Rhetorische</b>-Mittel-Deutsch - Appar på Google Play</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      <b>Rhetorische</b>-Mittel-Deutsch. IT-KellerUtbildning. Ingen åldersgräns.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>play.google.com/store/apps/details?id=click.itkeller.lars.lernen&amp;hl=sv</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">18.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.frustfrei%2Dlernen.de%2Fdeutsch%2Frhetorik%2Dsprachliche%2Dmittel%2Dstillistische%2Dfiguren.html" class='result-link'>Rhetorik: Sprachliche Mittel und stilistische Figuren</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      <b>Rhetorische</b> Frage Eine <b>rhetorische</b> Frage ist eine Frage, deren Antwort schon bekannt ist und welche durch die Fragestellung schon hervorgegangen ist. Hier wird durch die Wirkung der Frage die...
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.frustfrei-lernen.de/deutsch/rhetorik-sprachliche-mittel-stillistische-figuren.html</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">19.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fvimeo.com%2Fgroups%2F156428" class='result-link'><b>Rhetorische</b> Stilmittel / sylistic devices of rhetoric on Vimeo</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      1 Moderator. Related RSS Feeds. <b>Rhetorische</b> Stilmittel / sylistic devices of rhetoric. This is a Vimeo Group. Groups allow you to create mini communities around the things you like.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>vimeo.com/groups/156428</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">20.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.spektrum.de%2Fastrowissen%2Frhetorik.html" class='result-link'>Andreas Müller - Rhetorik | <b>Rhetorische</b> Figuren</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      <b>Rhetorische</b> Mittel leisten diese Zielsetzung bei richtigem Gebrauch. Der Inhalt kann durch <b>rhetorische</b> Stilmittel Man differenziert <b>rhetorische</b> Figuren weiterhin in Figuren und Bilder: Die...
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.spektrum.de/astrowissen/rhetorik.html</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">21.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.storyboardthat.com%2Fde%2Farticles%2Fe%2Fethos%2Dpathos%2Dlogos" class='result-link'>Ethos Pathos Logos | <b>Rhetorisches</b> Dreieck | Überredendes Schreiben</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Lernen Sie das <b>rhetorische</b> Dreieck von Ethos Pathos Logos mit lustigen und leicht verständlichen Storyboards. Ethos, Pathos und Logos sind wichtige Fähigkeiten für das Sprechen und...
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.storyboardthat.com/de/articles/e/ethos-pathos-logos</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">22.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.bachelorprint.at%2Fwissenschaftliches%2Dschreiben%2Frhetorische%2Dmittel%2F" class='result-link'><b>Rhetorische</b> Mittel | Übersicht | Erklärungen | Beispiele</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      <b>Rhetorische</b> Mittel: Erklärungen &amp; Beispiele | Übersicht: die wichtigsten rhetorischen Übersichts-PDF <b>rhetorische</b> Mittel. Noch nicht genug? Hier stellen wir dir eine ausführlichere Liste mit rhetorischen...
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.bachelorprint.at/wissenschaftliches-schreiben/rhetorische-mittel/</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">23.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fquizlet.com%2F30344516%2Frhetorische%2Dfigurenstilmittel%2Dflash%2Dcards%2F" class='result-link'><b>Rhetorische</b> Figuren/Stilmittel Flashcards | Quizlet</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Start studying <b>Rhetorische</b> Figuren/Stilmittel. Learn vocabulary, terms and more with flashcards, games and other study tools. <b>Rhetorische</b> Figuren/Stilmittel. STUDY. Flashcards.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>quizlet.com/30344516/rhetorische-figurenstilmittel-flash-cards/</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">24.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.dict.cc%2F%3Fs%3Drhetorische" class='result-link'><b>rhetorische</b> | Übersetzung Englisch-Deutsch</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      Deutsch-Englisch-Übersetzung für: <b>rhetorische</b>. ling. lit. rhetorical means. <b>rhetorische</b> Mittel {pl}. » Weitere 1 Übersetzungen für <b>rhetorische</b> innerhalb von Kommentaren.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.dict.cc/?s=rhetorische</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">25.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fdoclecture.net%2F1%2D48272.html" class='result-link'><b>Rhetorische</b> Figuren 94</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      <b>Rhetorische</b> Figuren 94. (4) die Aneignung der Rede durch Auswendiglernen (Memoria f), (5) die Kunst der <b>rhetorische</b> Frage: 1.im eigentlichen Sinn (als eine ↑ Immutatio syntactica) Aussage in...
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>doclecture.net/1-48272.html</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
- <tr>
-
-    <td valign="top">26.&nbsp;</td>
-    <td>
-
-    
-        <a rel="nofollow" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.soft%2Dskills.com%2Frhetorische%2Dkompetenz%2F" class='result-link'><b>Rhetorische</b> Kompetenz im Soft Skills Würfel (Rhetorik)</a>
-    
-    </td>
-  </tr>
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td class='result-snippet'>
-      <b>Rhetorische</b> Kompetenz als Soft Skill ist die Summe von Redegewandtheit (Wortgewandtheit, Eloquenz) und Teilen der Soft Skills Präsentationskompetenz und Überzeugungsvermögen.
-    </td>
-  </tr>
-  
-
-  
-  <tr>
-    <td>&nbsp;&nbsp;&nbsp;</td>
-    <td>
-      <span class='link-text'>www.soft-skills.com/rhetorische-kompetenz/</span>
-      
-    </td>
-  </tr>
-  
-
-  <tr>
-    <td>&nbsp;</td>
-    <td>&nbsp;</td>
-  </tr>
-
-
-
-
-
-
-
-
-
-  
-    <tr>
-    <td colspan=2>
-      
-      
-        <form class="next_form" action="/lite/" method="post">
-          <!-- <a rel="next" href="/lite/?q=rhetorische&amp;v=l&amp;l=us-en&amp;p=-1&amp;s=30&amp;ex=-1&amp;o=json&amp;dl=en&amp;ct=AT&amp;ss_mkt=us&amp;sp=0&amp;vqd=3-317185285141429630887821832006479517976-203666168757858907862519566843911872030&amp;dc=27&amp;api=%2Fd.js">Next Page &gt;</a> //-->
-          <input type="submit" class='navbutton' value="Next Page &gt;">
-          <input type="hidden" name="q" value="rhetorische">
-          <input type="hidden" name="s" value="30">
-          <input type="hidden" name="o" value="json">
-          <input type="hidden" name="dc" value="27">
-          <input type="hidden" name="api" value="/d.js">
-          &nbsp;&nbsp;&nbsp;&nbsp;
-                
-                
-          
-          <input name="kl" value="wt-wt" type="hidden">
-                
-              
-                
-                
-        </form>
-      
-      
-    </td>
-  </tr>
-
-</table>
-
-<p class='extra'>&nbsp;</p>
-
-<form action="/lite/" method="post">
-  <input class='query' type="text" size="40" name="q" value="rhetorische" >
-  <input class='submit' type="submit" value="Search" >
-    
-    
-    
-    
-
-
-  <input name="kl" value="wt-wt" type="hidden">
-    
-    
-</form>
-
-<p class='extra'>&nbsp;</p>
-
-<!--
-<a href="#top">Top</a>
-//-->
-
-
-  <img src="//duckduckgo.com/t/sl_l"/>
-
-<div id='end-spacer'>&nbsp;</div>
-
-
-
-</body>
-</html>
diff --git a/twtio.nim b/twtio.nim
index 32e4d55c..16dfb254 100644
--- a/twtio.nim
+++ b/twtio.nim
@@ -71,10 +71,10 @@ proc readLine*(prompt: string, current: var string): bool =
         print(' '.repeat(rl - cursor + 1))
         print('\b'.repeat(rl - cursor + 1))
         print("\b \b")
-        new = new.runeSubstr(0, cursor - 1) & new.runeSubstr(cursor, rl)
+        new = new.runeSubstr(0, cursor - 1) & new.runeSubstr(cursor)
         rl = new.runeLen()
         cursor -= 1
-        printesc(new.runeSubstr(cursor, rl))
+        printesc(new.runeSubstr(cursor))
         print('\b'.repeat(rl - cursor))
     of ACTION_LINED_ESC:
       escNext = true
@@ -84,7 +84,7 @@ proc readLine*(prompt: string, current: var string): bool =
       print('\b'.repeat(cursor))
       print(' '.repeat(cursor))
       print('\b'.repeat(cursor))
-      new = new.runeSubstr(cursor, rl)
+      new = new.runeSubstr(cursor)
       rl = new.runeLen()
       printesc(new)
       print('\b'.repeat(rl))
@@ -109,7 +109,7 @@ proc readLine*(prompt: string, current: var string): bool =
         cursor -= 1
         var rune: Rune
         new.fastRuneAt(cursor, rune, false)
-        if rune == runeSpace:
+        if rune == Rune(' '):
           break
     of ACTION_LINED_NEXT_WORD:
       while cursor < rl:
@@ -119,7 +119,7 @@ proc readLine*(prompt: string, current: var string): bool =
         cursor += 1
         if cursor < rl:
           new.fastRuneAt(cursor, rune, false)
-          if rune == runeSpace:
+          if rune == Rune(' '):
             break
     of ACTION_LINED_KILL_WORD:
       var chars = 0
@@ -127,16 +127,16 @@ proc readLine*(prompt: string, current: var string): bool =
         chars += 1
         var rune: Rune
         new.fastRuneAt(cursor - chars, rune, false)
-        if rune == runeSpace:
+        if rune == Rune(' '):
           break
       if chars > 0:
         print(' '.repeat(rl - cursor + 1))
         print('\b'.repeat(rl - cursor + 1))
         print("\b \b".repeat(chars))
-        new = new.runeSubstr(0, cursor - chars) & new.runeSubstr(cursor, rl)
+        new = new.runeSubstr(0, cursor - chars) & new.runeSubstr(cursor)
         rl = new.runeLen()
         cursor -= chars
-        printesc(new.runeSubstr(cursor, rl))
+        printesc(new.runeSubstr(cursor))
         print('\b'.repeat(rl - cursor))
     of ACTION_FEED_NEXT:
       feedNext = true
@@ -153,9 +153,9 @@ proc readLine*(prompt: string, current: var string): bool =
         continue
       print(' '.repeat(rl - cursor + 1))
       print('\b'.repeat(rl - cursor + 1))
-      new = new.runeSubstr(0, cursor) & cs & new.runeSubstr(cursor, rl)
+      new = new.runeSubstr(0, cursor) & cs & new.runeSubstr(cursor)
       rl = new.runeLen()
-      printesc(new.runeSubstr(cursor, rl))
+      printesc(new.runeSubstr(cursor))
       print('\b'.repeat(rl - cursor - 1))
       cursor += 1
     else:
diff --git a/twtstr.nim b/twtstr.nim
index 5f762780..9c03436e 100644
--- a/twtstr.nim
+++ b/twtstr.nim
@@ -2,8 +2,6 @@ import terminal
 import strutils
 import unicode
 
-const runeSpace* = " ".runeAt(0)
-
 func ansiStyle*(str: string, style: Style): seq[string] =
   result &= ansiStyleCode(style)
   result &= str
@@ -74,3 +72,254 @@ func findChar*(str: string, c: char, start: int = 0): int =
       return i
     i += 1
   return -1
+
+func findChar*(str: string, c: Rune, start: int = 0): int =
+  var i = start
+  var n = i
+  while i < str.runeLen():
+    var r: Rune
+    fastRuneAt(str, n, r)
+    if r == c:
+      return i
+    i = n
+  return -1
+
+
+#Measure length of rune. Transpiled from https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+
+#auxiliary function for binary search in interval table
+#TODO: use binary search in stdlib?
+func bisearch(ucs: Rune, table: openarray[(int, int)]): bool =
+  var max = table.high
+  var min = 0
+  var mid: int
+
+  if int(ucs) < table[0][0] or int(ucs) > table[max][1]:
+    return false
+
+  while max >= min:
+    mid = (min + max) div 2
+    if int(ucs) > table[mid][1]:
+      min = mid + 1
+    elif int(ucs) < table[mid][0]:
+      max = mid - 1
+    else:
+      return true
+  return false
+
+
+#The following two functions define the column width of an ISO 10646
+#character as follows:
+#
+#   - The null character (U+0000) has a column width of 0.
+#
+#   - Other C0/C1 control characters and DEL will lead to a return
+#     value of -1.
+#
+#   - Non-spacing and enclosing combining characters (general
+#     category code Mn or Me in the Unicode database) have a
+#     column width of 0.
+#
+#   - SOFT HYPHEN (U+00AD) has a column width of 1.
+#
+#   - Other format characters (general category code Cf in the Unicode
+#     database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
+#
+#   - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
+#     have a column width of 0.
+#
+#   - Spacing characters in the East Asian Wide (W) or East Asian
+#     Full-width (F) category as defined in Unicode Technical
+#     Report #11 have a column width of 2.
+#
+#   - All remaining characters (including all printable
+#     ISO 8859-1 and WGL4 characters, Unicode control characters,
+#     etc.) have a column width of 1.
+#
+#This implementation assumes that wchar_t characters are encoded
+#in ISO 10646.
+#
+
+const combining = [
+  ( 0x0300, 0x036F ), ( 0x0483, 0x0486 ), ( 0x0488, 0x0489 ),
+  ( 0x0591, 0x05BD ), ( 0x05BF, 0x05BF ), ( 0x05C1, 0x05C2 ),
+  ( 0x05C4, 0x05C5 ), ( 0x05C7, 0x05C7 ), ( 0x0600, 0x0603 ),
+  ( 0x0610, 0x0615 ), ( 0x064B, 0x065E ), ( 0x0670, 0x0670 ),
+  ( 0x06D6, 0x06E4 ), ( 0x06E7, 0x06E8 ), ( 0x06EA, 0x06ED ),
+  ( 0x070F, 0x070F ), ( 0x0711, 0x0711 ), ( 0x0730, 0x074A ),
+  ( 0x07A6, 0x07B0 ), ( 0x07EB, 0x07F3 ), ( 0x0901, 0x0902 ),
+  ( 0x093C, 0x093C ), ( 0x0941, 0x0948 ), ( 0x094D, 0x094D ),
+  ( 0x0951, 0x0954 ), ( 0x0962, 0x0963 ), ( 0x0981, 0x0981 ),
+  ( 0x09BC, 0x09BC ), ( 0x09C1, 0x09C4 ), ( 0x09CD, 0x09CD ),
+  ( 0x09E2, 0x09E3 ), ( 0x0A01, 0x0A02 ), ( 0x0A3C, 0x0A3C ),
+  ( 0x0A41, 0x0A42 ), ( 0x0A47, 0x0A48 ), ( 0x0A4B, 0x0A4D ),
+  ( 0x0A70, 0x0A71 ), ( 0x0A81, 0x0A82 ), ( 0x0ABC, 0x0ABC ),
+  ( 0x0AC1, 0x0AC5 ), ( 0x0AC7, 0x0AC8 ), ( 0x0ACD, 0x0ACD ),
+  ( 0x0AE2, 0x0AE3 ), ( 0x0B01, 0x0B01 ), ( 0x0B3C, 0x0B3C ),
+  ( 0x0B3F, 0x0B3F ), ( 0x0B41, 0x0B43 ), ( 0x0B4D, 0x0B4D ),
+  ( 0x0B56, 0x0B56 ), ( 0x0B82, 0x0B82 ), ( 0x0BC0, 0x0BC0 ),
+  ( 0x0BCD, 0x0BCD ), ( 0x0C3E, 0x0C40 ), ( 0x0C46, 0x0C48 ),
+  ( 0x0C4A, 0x0C4D ), ( 0x0C55, 0x0C56 ), ( 0x0CBC, 0x0CBC ),
+  ( 0x0CBF, 0x0CBF ), ( 0x0CC6, 0x0CC6 ), ( 0x0CCC, 0x0CCD ),
+  ( 0x0CE2, 0x0CE3 ), ( 0x0D41, 0x0D43 ), ( 0x0D4D, 0x0D4D ),
+  ( 0x0DCA, 0x0DCA ), ( 0x0DD2, 0x0DD4 ), ( 0x0DD6, 0x0DD6 ),
+  ( 0x0E31, 0x0E31 ), ( 0x0E34, 0x0E3A ), ( 0x0E47, 0x0E4E ),
+  ( 0x0EB1, 0x0EB1 ), ( 0x0EB4, 0x0EB9 ), ( 0x0EBB, 0x0EBC ),
+  ( 0x0EC8, 0x0ECD ), ( 0x0F18, 0x0F19 ), ( 0x0F35, 0x0F35 ),
+  ( 0x0F37, 0x0F37 ), ( 0x0F39, 0x0F39 ), ( 0x0F71, 0x0F7E ),
+  ( 0x0F80, 0x0F84 ), ( 0x0F86, 0x0F87 ), ( 0x0F90, 0x0F97 ),
+  ( 0x0F99, 0x0FBC ), ( 0x0FC6, 0x0FC6 ), ( 0x102D, 0x1030 ),
+  ( 0x1032, 0x1032 ), ( 0x1036, 0x1037 ), ( 0x1039, 0x1039 ),
+  ( 0x1058, 0x1059 ), ( 0x1160, 0x11FF ), ( 0x135F, 0x135F ),
+  ( 0x1712, 0x1714 ), ( 0x1732, 0x1734 ), ( 0x1752, 0x1753 ),
+  ( 0x1772, 0x1773 ), ( 0x17B4, 0x17B5 ), ( 0x17B7, 0x17BD ),
+  ( 0x17C6, 0x17C6 ), ( 0x17C9, 0x17D3 ), ( 0x17DD, 0x17DD ),
+  ( 0x180B, 0x180D ), ( 0x18A9, 0x18A9 ), ( 0x1920, 0x1922 ),
+  ( 0x1927, 0x1928 ), ( 0x1932, 0x1932 ), ( 0x1939, 0x193B ),
+  ( 0x1A17, 0x1A18 ), ( 0x1B00, 0x1B03 ), ( 0x1B34, 0x1B34 ),
+  ( 0x1B36, 0x1B3A ), ( 0x1B3C, 0x1B3C ), ( 0x1B42, 0x1B42 ),
+  ( 0x1B6B, 0x1B73 ), ( 0x1DC0, 0x1DCA ), ( 0x1DFE, 0x1DFF ),
+  ( 0x200B, 0x200F ), ( 0x202A, 0x202E ), ( 0x2060, 0x2063 ),
+  ( 0x206A, 0x206F ), ( 0x20D0, 0x20EF ), ( 0x302A, 0x302F ),
+  ( 0x3099, 0x309A ), ( 0xA806, 0xA806 ), ( 0xA80B, 0xA80B ),
+  ( 0xA825, 0xA826 ), ( 0xFB1E, 0xFB1E ), ( 0xFE00, 0xFE0F ),
+  ( 0xFE20, 0xFE23 ), ( 0xFEFF, 0xFEFF ), ( 0xFFF9, 0xFFFB ),
+  ( 0x10A01, 0x10A03 ), ( 0x10A05, 0x10A06 ), ( 0x10A0C, 0x10A0F ),
+  ( 0x10A38, 0x10A3A ), ( 0x10A3F, 0x10A3F ), ( 0x1D167, 0x1D169 ),
+  ( 0x1D173, 0x1D182 ), ( 0x1D185, 0x1D18B ), ( 0x1D1AA, 0x1D1AD ),
+  ( 0x1D242, 0x1D244 ), ( 0xE0001, 0xE0001 ), ( 0xE0020, 0xE007F ),
+  ( 0xE0100, 0xE01EF )
+]
+
+func mk_wcwidth(r: Rune): int =
+  let ucs = int(r)
+  # sorted list of non-overlapping intervals of non-spacing characters
+  # generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c"
+
+  # test for 8-bit control characters
+  if ucs == 0:
+    return 0
+  if ucs < 32 or (ucs >= 0x7f and ucs < 0xa0):
+    return -1
+
+  # binary search in table of non-spacing characters
+  if bisearch(r, combining):
+    return 0
+
+  # if we arrive here, ucs is not a combining or C0/C1 control character
+
+  if (ucs >= 0x1100 and
+     (ucs <= 0x115f or                    # Hangul Jamo init. consonants
+      ucs == 0x2329 or ucs == 0x232a or
+      (ucs >= 0x2e80 and ucs <= 0xa4cf and
+       ucs != 0x303f) or                  # CJK ... Yi
+      (ucs >= 0xac00 and ucs <= 0xd7a3) or # Hangul Syllables
+      (ucs >= 0xf900 and ucs <= 0xfaff) or # CJK Compatibility Ideographs
+      (ucs >= 0xfe10 and ucs <= 0xfe19) or # Vertical forms
+      (ucs >= 0xfe30 and ucs <= 0xfe6f) or # CJK Compatibility Forms
+      (ucs >= 0xff00 and ucs <= 0xff60) or # Fullwidth Forms
+      (ucs >= 0xffe0 and ucs <= 0xffe6) or
+      (ucs >= 0x20000 and ucs <= 0x2fffd) or
+      (ucs >= 0x30000 and ucs <= 0x3fffd))):
+    return 2
+  return 1
+
+
+func mk_wcswidth(s: string): int =
+  for r in s.runes:
+    result += mk_wcwidth(r)
+
+
+# 
+# The following functions are the same as mk_wcwidth() and
+# mk_wcswidth(), except that spacing characters in the East Asian
+# Ambiguous (A) category as defined in Unicode Technical Report #11
+# have a column width of 2. This variant might be useful for users of
+# CJK legacy encodings who want to migrate to UCS without changing
+# the traditional terminal character-width behaviour. It is not
+# otherwise recommended for general use.
+# 
+const ambiguous = [
+  ( 0x00A1, 0x00A1 ), ( 0x00A4, 0x00A4 ), ( 0x00A7, 0x00A8 ),
+  ( 0x00AA, 0x00AA ), ( 0x00AE, 0x00AE ), ( 0x00B0, 0x00B4 ),
+  ( 0x00B6, 0x00BA ), ( 0x00BC, 0x00BF ), ( 0x00C6, 0x00C6 ),
+  ( 0x00D0, 0x00D0 ), ( 0x00D7, 0x00D8 ), ( 0x00DE, 0x00E1 ),
+  ( 0x00E6, 0x00E6 ), ( 0x00E8, 0x00EA ), ( 0x00EC, 0x00ED ),
+  ( 0x00F0, 0x00F0 ), ( 0x00F2, 0x00F3 ), ( 0x00F7, 0x00FA ),
+  ( 0x00FC, 0x00FC ), ( 0x00FE, 0x00FE ), ( 0x0101, 0x0101 ),
+  ( 0x0111, 0x0111 ), ( 0x0113, 0x0113 ), ( 0x011B, 0x011B ),
+  ( 0x0126, 0x0127 ), ( 0x012B, 0x012B ), ( 0x0131, 0x0133 ),
+  ( 0x0138, 0x0138 ), ( 0x013F, 0x0142 ), ( 0x0144, 0x0144 ),
+  ( 0x0148, 0x014B ), ( 0x014D, 0x014D ), ( 0x0152, 0x0153 ),
+  ( 0x0166, 0x0167 ), ( 0x016B, 0x016B ), ( 0x01CE, 0x01CE ),
+  ( 0x01D0, 0x01D0 ), ( 0x01D2, 0x01D2 ), ( 0x01D4, 0x01D4 ),
+  ( 0x01D6, 0x01D6 ), ( 0x01D8, 0x01D8 ), ( 0x01DA, 0x01DA ),
+  ( 0x01DC, 0x01DC ), ( 0x0251, 0x0251 ), ( 0x0261, 0x0261 ),
+  ( 0x02C4, 0x02C4 ), ( 0x02C7, 0x02C7 ), ( 0x02C9, 0x02CB ),
+  ( 0x02CD, 0x02CD ), ( 0x02D0, 0x02D0 ), ( 0x02D8, 0x02DB ),
+  ( 0x02DD, 0x02DD ), ( 0x02DF, 0x02DF ), ( 0x0391, 0x03A1 ),
+  ( 0x03A3, 0x03A9 ), ( 0x03B1, 0x03C1 ), ( 0x03C3, 0x03C9 ),
+  ( 0x0401, 0x0401 ), ( 0x0410, 0x044F ), ( 0x0451, 0x0451 ),
+  ( 0x2010, 0x2010 ), ( 0x2013, 0x2016 ), ( 0x2018, 0x2019 ),
+  ( 0x201C, 0x201D ), ( 0x2020, 0x2022 ), ( 0x2024, 0x2027 ),
+  ( 0x2030, 0x2030 ), ( 0x2032, 0x2033 ), ( 0x2035, 0x2035 ),
+  ( 0x203B, 0x203B ), ( 0x203E, 0x203E ), ( 0x2074, 0x2074 ),
+  ( 0x207F, 0x207F ), ( 0x2081, 0x2084 ), ( 0x20AC, 0x20AC ),
+  ( 0x2103, 0x2103 ), ( 0x2105, 0x2105 ), ( 0x2109, 0x2109 ),
+  ( 0x2113, 0x2113 ), ( 0x2116, 0x2116 ), ( 0x2121, 0x2122 ),
+  ( 0x2126, 0x2126 ), ( 0x212B, 0x212B ), ( 0x2153, 0x2154 ),
+  ( 0x215B, 0x215E ), ( 0x2160, 0x216B ), ( 0x2170, 0x2179 ),
+  ( 0x2190, 0x2199 ), ( 0x21B8, 0x21B9 ), ( 0x21D2, 0x21D2 ),
+  ( 0x21D4, 0x21D4 ), ( 0x21E7, 0x21E7 ), ( 0x2200, 0x2200 ),
+  ( 0x2202, 0x2203 ), ( 0x2207, 0x2208 ), ( 0x220B, 0x220B ),
+  ( 0x220F, 0x220F ), ( 0x2211, 0x2211 ), ( 0x2215, 0x2215 ),
+  ( 0x221A, 0x221A ), ( 0x221D, 0x2220 ), ( 0x2223, 0x2223 ),
+  ( 0x2225, 0x2225 ), ( 0x2227, 0x222C ), ( 0x222E, 0x222E ),
+  ( 0x2234, 0x2237 ), ( 0x223C, 0x223D ), ( 0x2248, 0x2248 ),
+  ( 0x224C, 0x224C ), ( 0x2252, 0x2252 ), ( 0x2260, 0x2261 ),
+  ( 0x2264, 0x2267 ), ( 0x226A, 0x226B ), ( 0x226E, 0x226F ),
+  ( 0x2282, 0x2283 ), ( 0x2286, 0x2287 ), ( 0x2295, 0x2295 ),
+  ( 0x2299, 0x2299 ), ( 0x22A5, 0x22A5 ), ( 0x22BF, 0x22BF ),
+  ( 0x2312, 0x2312 ), ( 0x2460, 0x24E9 ), ( 0x24EB, 0x254B ),
+  ( 0x2550, 0x2573 ), ( 0x2580, 0x258F ), ( 0x2592, 0x2595 ),
+  ( 0x25A0, 0x25A1 ), ( 0x25A3, 0x25A9 ), ( 0x25B2, 0x25B3 ),
+  ( 0x25B6, 0x25B7 ), ( 0x25BC, 0x25BD ), ( 0x25C0, 0x25C1 ),
+  ( 0x25C6, 0x25C8 ), ( 0x25CB, 0x25CB ), ( 0x25CE, 0x25D1 ),
+  ( 0x25E2, 0x25E5 ), ( 0x25EF, 0x25EF ), ( 0x2605, 0x2606 ),
+  ( 0x2609, 0x2609 ), ( 0x260E, 0x260F ), ( 0x2614, 0x2615 ),
+  ( 0x261C, 0x261C ), ( 0x261E, 0x261E ), ( 0x2640, 0x2640 ),
+  ( 0x2642, 0x2642 ), ( 0x2660, 0x2661 ), ( 0x2663, 0x2665 ),
+  ( 0x2667, 0x266A ), ( 0x266C, 0x266D ), ( 0x266F, 0x266F ),
+  ( 0x273D, 0x273D ), ( 0x2776, 0x277F ), ( 0xE000, 0xF8FF ),
+  ( 0xFFFD, 0xFFFD ), ( 0xF0000, 0xFFFFD ), ( 0x100000, 0x10FFFD )
+]
+
+func mk_wcwidth_cjk*(ucs: Rune): int =
+  # sorted list of non-overlapping intervals of East Asian Ambiguous
+  # characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c"
+
+  # binary search in table of non-spacing characters
+  if bisearch(ucs, ambiguous):
+    return 2;
+
+  return mk_wcwidth(ucs);
+
+
+func mk_wcswidth_cjk*(s: string): int =
+  result = 0
+  for r in s.runes:
+    result += mk_wcwidth_cjk(r)
+  return result
+  result = 0
+  var i = 0
+  while i < len(s):
+    var r: Rune
+    fastRuneAt(s, i, r, false)
+    if uint(s[i]) <= 127: inc(i)
+    elif uint(s[i]) shr 5 == 0b110: inc(i, 2)
+    elif uint(s[i]) shr 4 == 0b1110: inc(i, 3)
+    elif uint(s[i]) shr 3 == 0b11110: inc(i, 4)
+    elif uint(s[i]) shr 2 == 0b111110: inc(i, 5)
+    elif uint(s[i]) shr 1 == 0b1111110: inc(i, 6)
+    else: inc i
+    inc(result, mk_wcwidth_cjk(r))