summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/htmlparser.nim6
-rw-r--r--lib/pure/pegs.nim4
-rw-r--r--lib/pure/strutils.nim255
-rw-r--r--lib/pure/unicode.nim134
-rw-r--r--web/news/version_0_15_released.rst6
5 files changed, 336 insertions, 69 deletions
diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim
index d620e816e..fd58bed25 100644
--- a/lib/pure/htmlparser.nim
+++ b/lib/pure/htmlparser.nim
@@ -433,7 +433,7 @@ proc htmlTag*(n: XmlNode): HtmlTag =
 proc htmlTag*(s: string): HtmlTag =
   ## converts `s` to a ``HtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
   ## returned.
-  let s = if allLower(s): s else: s.toLower
+  let s = if allLower(s): s else: toLowerAscii(s)
   result = toHtmlTag(s)
 
 proc entityToUtf8*(entity: string): string =
@@ -513,13 +513,13 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
     errors.add(errorMsg(x))
     next(x)
   of xmlElementStart:
-    result = newElement(x.elemName.toLower)
+    result = newElement(toLowerAscii(x.elemName))
     next(x)
     untilElementEnd(x, result, errors)
   of xmlElementEnd:
     errors.add(errorMsg(x, "unexpected ending tag: " & x.elemName))
   of xmlElementOpen:
-    result = newElement(x.elemName.toLower)
+    result = newElement(toLowerAscii(x.elemName))
     next(x)
     result.attrs = newStringTable()
     while true:
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 7e1f50266..5c978a2f8 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -1841,8 +1841,8 @@ when isMainModule:
       result.add ", "
 
     result.add case n:
-      of 2: c[0].toLower & ": '" & c[1] & "'"
-      of 1: c[0].toLower & ": ''"
+      of 2: toLowerAscii(c[0]) & ": '" & c[1] & "'"
+      of 1: toLowerAscii(c[0]) & ": ''"
       else: ""
 
   assert("Var1=key1;var2=Key2;   VAR3".
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 7d1b1a3d9..38807b29a 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -64,8 +64,8 @@ const
     ##   doAssert "01234".find(invalid) == -1
     ##   doAssert "01A34".find(invalid) == 2
 
-proc isAlpha*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaChar".}=
+proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsAlphaAsciiChar".}=
   ## Checks whether or not `c` is alphabetical.
   ##
   ## This checks a-z, A-Z ASCII characters only.
@@ -85,27 +85,27 @@ proc isDigit*(c: char): bool {.noSideEffect, procvar,
   ## This checks 0-9 ASCII characters only.
   return c in Digits
 
-proc isSpace*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsSpaceChar".}=
+proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsSpaceAsciiChar".}=
   ## Checks whether or not `c` is a whitespace character.
   return c in Whitespace
 
-proc isLower*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsLowerChar".}=
+proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsLowerAsciiChar".}=
   ## Checks whether or not `c` is a lower case character.
   ##
   ## This checks ASCII characters only.
   return c in {'a'..'z'}
 
-proc isUpper*(c: char): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsUpperChar".}=
+proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsUpperAsciiChar".}=
   ## Checks whether or not `c` is an upper case character.
   ##
   ## This checks ASCII characters only.
   return c in {'A'..'Z'}
 
-proc isAlpha*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaStr".}=
+proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsAlphaAsciiStr".}=
   ## Checks whether or not `s` is alphabetical.
   ##
   ## This checks a-z, A-Z ASCII characters only.
@@ -117,7 +117,7 @@ proc isAlpha*(s: string): bool {.noSideEffect, procvar,
 
   result = true
   for c in s:
-    result = c.isAlpha() and result
+    result = c.isAlphaAscii() and result
 
 proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsAlphaNumericStr".}=
@@ -149,8 +149,8 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar,
   for c in s:
     result = c.isDigit() and result
 
-proc isSpace*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsSpaceStr".}=
+proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsSpaceAsciiStr".}=
   ## Checks whether or not `s` is completely whitespace.
   ##
   ## Returns true if all characters in `s` are whitespace
@@ -160,11 +160,11 @@ proc isSpace*(s: string): bool {.noSideEffect, procvar,
 
   result = true
   for c in s:
-    if not c.isSpace():
+    if not c.isSpaceAscii():
       return false
 
-proc isLower*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsLowerStr".}=
+proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsLowerAsciiStr".}=
   ## Checks whether or not `s` contains all lower case characters.
   ##
   ## This checks ASCII characters only.
@@ -175,10 +175,10 @@ proc isLower*(s: string): bool {.noSideEffect, procvar,
 
   result = true
   for c in s:
-    result = c.isLower() and result
+    result = c.isLowerAscii() and result
 
-proc isUpper*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsUpperStr".}=
+proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsUpperAsciiStr".}=
   ## Checks whether or not `s` contains all upper case characters.
   ##
   ## This checks ASCII characters only.
@@ -189,10 +189,10 @@ proc isUpper*(s: string): bool {.noSideEffect, procvar,
 
   result = true
   for c in s:
-    result = c.isUpper() and result
+    result = c.isUpperAscii() and result
 
-proc toLower*(c: char): char {.noSideEffect, procvar,
-  rtl, extern: "nsuToLowerChar".} =
+proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
+  rtl, extern: "nsuToLowerAsciiChar".} =
   ## Converts `c` into lower case.
   ##
   ## This works only for the letters ``A-Z``. See `unicode.toLower
@@ -203,8 +203,8 @@ proc toLower*(c: char): char {.noSideEffect, procvar,
   else:
     result = c
 
-proc toLower*(s: string): string {.noSideEffect, procvar,
-  rtl, extern: "nsuToLowerStr".} =
+proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
+  rtl, extern: "nsuToLowerAsciiStr".} =
   ## Converts `s` into lower case.
   ##
   ## This works only for the letters ``A-Z``. See `unicode.toLower
@@ -212,10 +212,10 @@ proc toLower*(s: string): string {.noSideEffect, procvar,
   ## character.
   result = newString(len(s))
   for i in 0..len(s) - 1:
-    result[i] = toLower(s[i])
+    result[i] = toLowerAscii(s[i])
 
-proc toUpper*(c: char): char {.noSideEffect, procvar,
-  rtl, extern: "nsuToUpperChar".} =
+proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
+  rtl, extern: "nsuToUpperAsciiChar".} =
   ## Converts `c` into upper case.
   ##
   ## This works only for the letters ``A-Z``.  See `unicode.toUpper
@@ -226,8 +226,8 @@ proc toUpper*(c: char): char {.noSideEffect, procvar,
   else:
     result = c
 
-proc toUpper*(s: string): string {.noSideEffect, procvar,
-  rtl, extern: "nsuToUpperStr".} =
+proc toUpperAscii*(s: string): string {.noSideEffect, procvar,
+  rtl, extern: "nsuToUpperAsciiStr".} =
   ## Converts `s` into upper case.
   ##
   ## This works only for the letters ``A-Z``.  See `unicode.toUpper
@@ -235,14 +235,145 @@ proc toUpper*(s: string): string {.noSideEffect, procvar,
   ## character.
   result = newString(len(s))
   for i in 0..len(s) - 1:
-    result[i] = toUpper(s[i])
+    result[i] = toUpperAscii(s[i])
+
+proc capitalizeAscii*(s: string): string {.noSideEffect, procvar,
+  rtl, extern: "nsuCapitalizeAscii".} =
+  ## Converts the first character of `s` into upper case.
+  ##
+  ## This works only for the letters ``A-Z``.
+  result = toUpperAscii(s[0]) & substr(s, 1)
+
+proc isSpace*(c: char): bool {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuIsSpaceChar".}=
+  ## Checks whether or not `c` is a whitespace character.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead.
+  isSpaceAscii(c)
+
+proc isLower*(c: char): bool {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuIsLowerChar".}=
+  ## Checks whether or not `c` is a lower case character.
+  ##
+  ## This checks ASCII characters only.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead.
+  isLowerAscii(c)
+
+proc isUpper*(c: char): bool {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuIsUpperChar".}=
+  ## Checks whether or not `c` is an upper case character.
+  ##
+  ## This checks ASCII characters only.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead.
+  isUpperAscii(c)
+
+proc isAlpha*(c: char): bool {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuIsAlphaChar".}=
+  ## Checks whether or not `c` is alphabetical.
+  ##
+  ## This checks a-z, A-Z ASCII characters only.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead.
+  isAlphaAscii(c)
+
+proc isAlpha*(s: string): bool {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuIsAlphaStr".}=
+  ## Checks whether or not `s` is alphabetical.
+  ##
+  ## This checks a-z, A-Z ASCII characters only.
+  ## Returns true if all characters in `s` are
+  ## alphabetic and there is at least one character
+  ## in `s`.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``isAlphaAscii`` instead.
+  isAlphaAscii(s)
+
+proc isSpace*(s: string): bool {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuIsSpaceStr".}=
+  ## Checks whether or not `s` is completely whitespace.
+  ##
+  ## Returns true if all characters in `s` are whitespace
+  ## characters and there is at least one character in `s`.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``isSpaceAscii`` instead.
+  isSpaceAscii(s)
+
+proc isLower*(s: string): bool {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuIsLowerStr".}=
+  ## Checks whether or not `s` contains all lower case characters.
+  ##
+  ## This checks ASCII characters only.
+  ## Returns true if all characters in `s` are lower case
+  ## and there is at least one character  in `s`.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``isLowerAscii`` instead.
+  isLowerAscii(s)
+
+proc isUpper*(s: string): bool {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuIsUpperStr".}=
+  ## Checks whether or not `s` contains all upper case characters.
+  ##
+  ## This checks ASCII characters only.
+  ## Returns true if all characters in `s` are upper case
+  ## and there is at least one character in `s`.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``isUpperAscii`` instead.
+  isUpperAscii(s)
+
+proc toLower*(c: char): char {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuToLowerChar".} =
+  ## Converts `c` into lower case.
+  ##
+  ## This works only for the letters ``A-Z``. See `unicode.toLower
+  ## <unicode.html#toLower>`_ for a version that works for any Unicode
+  ## character.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead.
+  toLowerAscii(c)
+
+proc toLower*(s: string): string {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuToLowerStr".} =
+  ## Converts `s` into lower case.
+  ##
+  ## This works only for the letters ``A-Z``. See `unicode.toLower
+  ## <unicode.html#toLower>`_ for a version that works for any Unicode
+  ## character.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``toLowerAscii`` instead.
+  toLowerAscii(s)
+
+proc toUpper*(c: char): char {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuToUpperChar".} =
+  ## Converts `c` into upper case.
+  ##
+  ## This works only for the letters ``A-Z``.  See `unicode.toUpper
+  ## <unicode.html#toUpper>`_ for a version that works for any Unicode
+  ## character.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead.
+  toUpperAscii(c)
+
+proc toUpper*(s: string): string {.noSideEffect, procvar,
+  rtl, deprecated, extern: "nsuToUpperStr".} =
+  ## Converts `s` into upper case.
+  ##
+  ## This works only for the letters ``A-Z``.  See `unicode.toUpper
+  ## <unicode.html#toUpper>`_ for a version that works for any Unicode
+  ## character.
+  ##
+  ## **Deprecated since version 0.15.0**: use ``toUpperAscii`` instead.
+  toUpperAscii(s)
 
 proc capitalize*(s: string): string {.noSideEffect, procvar,
-  rtl, extern: "nsuCapitalize".} =
+  rtl, deprecated, extern: "nsuCapitalize".} =
   ## Converts the first character of `s` into upper case.
   ##
   ## This works only for the letters ``A-Z``.
-  result = toUpper(s[0]) & substr(s, 1)
+  ##
+  ## **Deprecated since version 0.15.0**: use ``capitalizeAscii`` instead.
+  capitalizeAscii(s)
 
 proc normalize*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuNormalize".} =
@@ -271,7 +402,7 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
   var i = 0
   var m = min(a.len, b.len)
   while i < m:
-    result = ord(toLower(a[i])) - ord(toLower(b[i]))
+    result = ord(toLowerAscii(a[i])) - ord(toLowerAscii(b[i]))
     if result != 0: return
     inc(i)
   result = a.len - b.len
@@ -292,8 +423,8 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
   while true:
     while a[i] == '_': inc(i)
     while b[j] == '_': inc(j) # BUGFIX: typo
-    var aa = toLower(a[i])
-    var bb = toLower(b[j])
+    var aa = toLowerAscii(a[i])
+    var bb = toLowerAscii(b[j])
     result = ord(aa) - ord(bb)
     if result != 0 or aa == '\0': break
     inc(i)
@@ -2138,13 +2269,13 @@ when isMainModule:
 
   doAssert "  foo\n  bar".indent(4, "Q") == "QQQQ  foo\nQQQQ  bar"
 
-  doAssert isAlpha('r')
-  doAssert isAlpha('A')
-  doAssert(not isAlpha('$'))
+  doAssert isAlphaAscii('r')
+  doAssert isAlphaAscii('A')
+  doAssert(not isAlphaAscii('$'))
 
-  doAssert isAlpha("Rasp")
-  doAssert isAlpha("Args")
-  doAssert(not isAlpha("$Tomato"))
+  doAssert isAlphaAscii("Rasp")
+  doAssert isAlphaAscii("Args")
+  doAssert(not isAlphaAscii("$Tomato"))
 
   doAssert isAlphaNumeric('3')
   doAssert isAlphaNumeric('R')
@@ -2163,13 +2294,13 @@ when isMainModule:
   doAssert(not isDigit("12.33"))
   doAssert(not isDigit("A45b"))
 
-  doAssert isSpace('\t')
-  doAssert isSpace('\l')
-  doAssert(not isSpace('A'))
+  doAssert isSpaceAscii('\t')
+  doAssert isSpaceAscii('\l')
+  doAssert(not isSpaceAscii('A'))
 
-  doAssert isSpace("\t\l \v\r\f")
-  doAssert isSpace("       ")
-  doAssert(not isSpace("ABc   \td"))
+  doAssert isSpaceAscii("\t\l \v\r\f")
+  doAssert isSpaceAscii("       ")
+  doAssert(not isSpaceAscii("ABc   \td"))
 
   doAssert(isNilOrEmpty(""))
   doAssert(isNilOrEmpty(nil))
@@ -2182,24 +2313,24 @@ when isMainModule:
   doAssert(isNilOrWhitespace("\t\l \v\r\f"))
   doAssert(not isNilOrWhitespace("ABc   \td"))
 
-  doAssert isLower('a')
-  doAssert isLower('z')
-  doAssert(not isLower('A'))
-  doAssert(not isLower('5'))
-  doAssert(not isLower('&'))
+  doAssert isLowerAscii('a')
+  doAssert isLowerAscii('z')
+  doAssert(not isLowerAscii('A'))
+  doAssert(not isLowerAscii('5'))
+  doAssert(not isLowerAscii('&'))
 
-  doAssert isLower("abcd")
-  doAssert(not isLower("abCD"))
-  doAssert(not isLower("33aa"))
+  doAssert isLowerAscii("abcd")
+  doAssert(not isLowerAscii("abCD"))
+  doAssert(not isLowerAscii("33aa"))
 
-  doAssert isUpper('A')
-  doAssert(not isUpper('b'))
-  doAssert(not isUpper('5'))
-  doAssert(not isUpper('%'))
+  doAssert isUpperAscii('A')
+  doAssert(not isUpperAscii('b'))
+  doAssert(not isUpperAscii('5'))
+  doAssert(not isUpperAscii('%'))
 
-  doAssert isUpper("ABC")
-  doAssert(not isUpper("AAcc"))
-  doAssert(not isUpper("A#$"))
+  doAssert isUpperAscii("ABC")
+  doAssert(not isUpperAscii("AAcc"))
+  doAssert(not isUpperAscii("A#$"))
 
   doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"]
   doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"]
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index ac25dccef..b5383c5d3 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -1369,6 +1369,64 @@ proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
     (c >= 0x20d0 and c <= 0x20ff) or
     (c >= 0xfe20 and c <= 0xfe2f))
 
+template runeCheck(s, runeProc) =
+  ## Common code for rune.isLower, rune.isUpper, etc
+  result = if len(s) == 0: false else: true
+
+  var
+    i = 0
+    rune: Rune
+
+  while i < len(s) and result:
+    fastRuneAt(s, i, rune, doInc=true)
+    result = runeProc(rune) and result
+
+proc isUpper*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nuc$1Str".} =
+  ## Returns true iff `s` contains all upper case unicode characters.
+  runeCheck(s, isUpper)
+
+proc isLower*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nuc$1Str".} =
+  ## Returns true iff `s` contains all lower case unicode characters.
+  runeCheck(s, isLower)
+
+proc isAlpha*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nuc$1Str".} =
+  ## Returns true iff `s` contains all alphabetic unicode characters.
+  runeCheck(s, isAlpha)
+
+proc isSpace*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nuc$1Str".} =
+  ## Returns true iff `s` contains all whitespace unicode characters.
+  runeCheck(s, isWhiteSpace)
+
+template convertRune(s, runeProc) =
+  ## Convert runes in `s` using `runeProc` as the converter.
+  result = newString(len(s))
+
+  var
+    i = 0
+    lastIndex = 0
+    rune: Rune
+
+  while i < len(s):
+    lastIndex = i
+    fastRuneAt(s, i, rune, doInc=true)
+    rune = runeProc(rune)
+
+    rune.fastToUTF8Copy(result, lastIndex)
+
+proc toUpper*(s: string): string {.noSideEffect, procvar,
+  rtl, extern: "nuc$1Str".} =
+  ## Converts `s` into upper-case unicode characters.
+  convertRune(s, toUpper)
+
+proc toLower*(s: string): string {.noSideEffect, procvar,
+  rtl, extern: "nuc$1Str".} =
+  ## Converts `s` into lower-case unicode characters.
+  convertRune(s, toLower)
+
 proc swapCase*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1".} =
   ## Swaps the case of unicode characters in `s`
@@ -1395,6 +1453,20 @@ proc swapCase*(s: string): string {.noSideEffect, procvar,
 
     rune.fastToUTF8Copy(result, lastIndex)
 
+proc capitalize*(s: string): string {.noSideEffect, procvar,
+  rtl, extern: "nuc$1".} =
+  ## Converts the first character of `s` into an upper-case unicode character.
+  if len(s) == 0:
+    return s
+
+  var
+    rune: Rune
+    i = 0
+
+  fastRuneAt(s, i, rune, doInc=true)
+
+  result = $toUpper(rune) & substr(s, i)
+
 proc translate*(s: string, replacements: proc(key: string): string): string {.
   rtl, extern: "nuc$1".} =
   ## Translates words in a string using the `replacements` proc to substitute
@@ -1618,6 +1690,10 @@ when isMainModule:
   doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma"
   doAssert title("") == ""
 
+  doAssert capitalize("βeta") == "Βeta"
+  doAssert capitalize("foo") == "Foo"
+  doAssert capitalize("") == ""
+
   doAssert isTitle("Foo")
   doAssert(not isTitle("Foo bar"))
   doAssert(not isTitle("αlpha Βeta"))
@@ -1630,6 +1706,64 @@ when isMainModule:
   doAssert swapCase("a✓B") == "A✓b"
   doAssert swapCase("") == ""
 
+  doAssert isAlpha("r")
+  doAssert isAlpha("α")
+  doAssert(not isAlpha("$"))
+  doAssert(not isAlpha(""))
+
+  doAssert isAlpha("Βeta")
+  doAssert isAlpha("Args")
+  doAssert(not isAlpha("$Foo✓"))
+
+  doAssert isSpace("\t")
+  doAssert isSpace("\l")
+  doAssert(not isSpace("Β"))
+  doAssert(not isSpace("Βeta"))
+
+  doAssert isSpace("\t\l \v\r\f")
+  doAssert isSpace("       ")
+  doAssert(not isSpace(""))
+  doAssert(not isSpace("ΑΓc   \td"))
+
+  doAssert isLower("a")
+  doAssert isLower("γ")
+  doAssert(not isLower("Γ"))
+  doAssert(not isLower("4"))
+  doAssert(not isLower(""))
+
+  doAssert isLower("abcdγ")
+  doAssert(not isLower("abCDΓ"))
+  doAssert(not isLower("33aaΓ"))
+
+  doAssert isUpper("Γ")
+  doAssert(not isUpper("b"))
+  doAssert(not isUpper("α"))
+  doAssert(not isUpper("✓"))
+  doAssert(not isUpper(""))
+
+  doAssert isUpper("ΑΒΓ")
+  doAssert(not isUpper("AAccβ"))
+  doAssert(not isUpper("A#$β"))
+
+  doAssert toUpper("Γ") == "Γ"
+  doAssert toUpper("b") == "B"
+  doAssert toUpper("α") == "Α"
+  doAssert toUpper("✓") == "✓"
+  doAssert toUpper("") == ""
+
+  doAssert toUpper("ΑΒΓ") == "ΑΒΓ"
+  doAssert toUpper("AAccβ") == "AACCΒ"
+  doAssert toUpper("A✓$β") == "A✓$Β"
+
+  doAssert toLower("a") == "a"
+  doAssert toLower("γ") == "γ"
+  doAssert toLower("Γ") == "γ"
+  doAssert toLower("4") == "4"
+  doAssert toLower("") == ""
+
+  doAssert toLower("abcdγ") == "abcdγ"
+  doAssert toLower("abCDΓ") == "abcdγ"
+  doAssert toLower("33aaΓ") == "33aaγ"
 
   doAssert reversed("Reverse this!") == "!siht esreveR"
   doAssert reversed("先秦兩漢") == "漢兩秦先"
diff --git a/web/news/version_0_15_released.rst b/web/news/version_0_15_released.rst
index ecda59fcd..96d9abc99 100644
--- a/web/news/version_0_15_released.rst
+++ b/web/news/version_0_15_released.rst
@@ -23,8 +23,10 @@ Library Additions
 - Added ``center`` and ``rsplit`` to ``strutils.nim`` to
   provide similar Python functionality for Nim's strings.
 
-- Added ``isTitle``, ``title``, and ``swapCase`` to ``unicode.nim`` to
-  provide unicode aware string case manipulation.
+- Added ``isTitle``, ``title``, ``swapCase``, ``isUpper``, ``toUpper``,
+  ``isLower``, ``toLower``, ``isAlpha``, ``isSpace``, and ``capitalize``
+  to ``unicode.nim`` to provide unicode aware case manipulation and case
+  testing.
 
 - Added a new module ``lib/pure/strmisc.nim`` to hold uncommon string
   operations. Currently contains ``partition``, ``rpartition``