1#
2#
3#            Nim's Runtime Library
4#        (c) Copyright 2018 Nim contributors
5#
6#    See the file "copying.txt", included in this
7#    distribution, for details about the copyright.
8#
9
10## This module contains an algorithm to wordwrap a Unicode string.
11
12import strutils, unicode
13
14proc olen(s: string; start, lastExclusive: int): int =
15  var i = start
16  result = 0
17  while i < lastExclusive:
18    inc result
19    let L = graphemeLen(s, i)
20    inc i, L
21
22proc wrapWords*(s: string, maxLineWidth = 80,
23               splitLongWords = true,
24               seps: set[char] = Whitespace,
25               newLine = "\n"): string {.noSideEffect.} =
26  ## Word wraps `s`.
27  runnableExamples:
28    doAssert "12345678901234567890".wrapWords() == "12345678901234567890"
29    doAssert "123456789012345678901234567890".wrapWords(20) == "12345678901234567890\n1234567890"
30    doAssert "Hello Bob. Hello John.".wrapWords(13, false) == "Hello Bob.\nHello John."
31    doAssert "Hello Bob. Hello John.".wrapWords(13, true, {';'}) == "Hello Bob. He\nllo John."
32  result = newStringOfCap(s.len + s.len shr 6)
33  var spaceLeft = maxLineWidth
34  var lastSep = ""
35
36  var i = 0
37  while true:
38    var j = i
39    let isSep = j < s.len and s[j] in seps
40    while j < s.len and (s[j] in seps) == isSep: inc(j)
41    if j <= i: break
42    #yield (substr(s, i, j-1), isSep)
43    if isSep:
44      lastSep.setLen 0
45      for k in i..<j:
46        if s[k] notin {'\L', '\C'}: lastSep.add s[k]
47      if lastSep.len == 0:
48        lastSep.add ' '
49        dec spaceLeft
50      else:
51        spaceLeft = spaceLeft - olen(lastSep, 0, lastSep.len)
52    else:
53      let wlen = olen(s, i, j)
54      if wlen > spaceLeft:
55        if splitLongWords and wlen > maxLineWidth:
56          var k = 0
57          while k < j - i:
58            if spaceLeft <= 0:
59              spaceLeft = maxLineWidth
60              result.add newLine
61            dec spaceLeft
62            let L = graphemeLen(s, k+i)
63            for m in 0 ..< L: result.add s[i+k+m]
64            inc k, L
65        else:
66          spaceLeft = maxLineWidth - wlen
67          result.add(newLine)
68          for k in i..<j: result.add(s[k])
69      else:
70        spaceLeft = spaceLeft - wlen
71        result.add(lastSep)
72        for k in i..<j: result.add(s[k])
73        #lastSep.setLen(0)
74    i = j
75