# Word wrap function?

**URL:** https://discourse.julialang.org/t/word-wrap-function/20438
**Category:** General Usage
**Created:** [February 4, 2019, 5:07pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438 "2019-02-04T17:07:23Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![dmbates](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmbates/32/44_2.png) [@dmbates](https://discourse.julialang.org/u/dmbates)
#### Post date: [February 4, 2019, 5:07pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438/1 "2019-02-04T17:07:23Z")

</div>

I need a word-wrap function that takes an input string and returns a similar string with some of the spaces replaced by newlines such that no line in the returned string exceeds a threshold length. My current version is

```julia
function word_wrap(str::AbstractString, maxlen = 92)
    outbuf = IOBuffer()
    tokens = split(str)
    firsttoken = 1
    linelen = 0
    for (i, t) in enumerate(tokens)
        toksz = length(t)
        if (linelen + toksz) > maxlen
            join(outbuf, view(tokens, firsttoken:(i-1)), ' ')
            println(outbuf)
            firsttoken = i
            linelen = 0
        end
        linelen += toksz + 1
    end
    join(outbuf, view(tokens, firsttoken:length(tokens)), ' ')
    String(take!(outbuf))
end

```

Am I reinventing the wheel?

---

<div class="post-metadata">

### Author: ![dmbates](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmbates/32/44_2.png) [@dmbates](https://discourse.julialang.org/u/dmbates)
#### Post date: [February 4, 2019, 5:18pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438/2 "2019-02-04T17:18:25Z")

</div>

I see that I don’t actually use `t`, the token in the `for` loop so I would probably be better off looping over

```julia
length.(tokens)

```

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [February 4, 2019, 5:35pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438/4 "2019-02-04T17:35:03Z")

</div>

> [@dmbates](#):
>
> Am I reinventing the wheel?

There is a `Markdown.wrapped_lines` and similar, but that could change any point since it is not part of a documented API (which is a pity, there is so much useful stuff there).

---

<div class="post-metadata">

### Author: ![dmbates](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmbates/32/44_2.png) [@dmbates](https://discourse.julialang.org/u/dmbates)
#### Post date: [February 4, 2019, 7:06pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438/5 "2019-02-04T19:06:13Z")

</div>

Thanks @Tamas_Papp. That was what I was hoping to find.

Of course, now that I have started thinking about it I realize that I just need to work with the cumulative sums of the lengths of the `tokens` from my function so this is where I am now.

```julia
"""
    intervals(lengths, width)

Return a partition of 1:length(lengths) such that each interval in the partition
is as large as it can be without its cumulative length exceeding `width`.

The tokens whose lengths are passed will be `join`ed with a space, which is why
the cumulative lengths consist of the length of the token plus the separator.
""" 
function intervals(lengths, width)
    value = UnitRange{Int}[]
    start = 1
    offset = 0
    cumlengths = cumsum(lengths .+ 1)
    while (endpoint = searchsortedlast(cumlengths, offset + width)) != 0
        push!(value, start:endpoint)
        (start = endpoint + 1) <= length(cumlengths) || break
        offset = cumlengths[endpoint] - 1
    end
    value
end

function word_wrap(str::AbstractString, maxlen = 92)
    tokens = split(str)
    join([join(view(tokens, i), ' ') for i in
        intervals(length.(tokens), maxlen+1)], '\n')
end

```

---

<div class="post-metadata">

### Author: ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)
#### Post date: [February 4, 2019, 7:06pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438/6 "2019-02-04T19:06:16Z")

</div>

This is an older package but should be easy enough to update:

[https://github.com/carlobaldassi/TextWrap.jl](https://github.com/carlobaldassi/TextWrap.jl)

---

<div class="post-metadata">

### Author: ![dmbates](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmbates/32/44_2.png) [@dmbates](https://discourse.julialang.org/u/dmbates)
#### Post date: [February 4, 2019, 7:13pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438/7 "2019-02-04T19:13:11Z")

</div>

Thanks @StefanKarpinski. That package does exactly what I want. I’m not sure how I managed to miss it in a search of available packages.

Nonetheless it was interesting to work with `split` and `join` a bit as an exercise.

---

<div class="post-metadata">

### Author: ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)
#### Post date: [February 4, 2019, 9:24pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438/8 "2019-02-04T21:24:46Z")

</div>

This is a classic job for ~~`reduce`~~ `foldl` (thanks @tkf!):

```julia
function wrap_foldl(str::AbstractString, maxlen = 92)
    foldl(((k,s),w) -> (k+=m=length(w*=" ")) > maxlen+1 ?
        (m,s*"\n"*w) : (k,s*w), split(str); init=(0,""))[2]
end

```

Alternatively just use a regex:

```julia
function wrap_regex(str::AbstractString, maxlen = 92)
    replace(str, Regex(".{1,$maxlen}( |\$)") => @s_str "\\0\n")
end

```

Testing it:

```julia
julia> s = join("x".^rand(1:5,20), " ")
"xxxx xxxx x xxxx x xx x x xx x xxx xxx x xxxx xxx xxx xxxxx xxxxx x xxx "

julia> println(word_wrap(s, 20)) # from original post
xxxx xxxx x xxxx x
xx x x xx x xxx xxx
x xxxx xxx xxx xxxxx
xxxxx x xxx

julia> println(wrap_foldl(s, 20))
xxxx xxxx x xxxx x
xx x x xx x xxx xxx
x xxxx xxx xxx xxxxx
xxxxx x xxx

julia> println(wrap_regex(s, 20))
xxxx xxxx x xxxx x
xx x x xx x xxx xxx
x xxxx xxx xxx xxxxx
xxxxx x xxx

```

---

<div class="post-metadata">

### Author: ![dmbates](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmbates/32/44_2.png) [@dmbates](https://discourse.julialang.org/u/dmbates)
#### Post date: [February 4, 2019, 10:10pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438/9 "2019-02-04T22:10:12Z")

</div>

That’s impressive. @bennedich

---

<div class="post-metadata">

### Author: ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)
#### Post date: [February 5, 2019, 12:09am UTC](https://discourse.julialang.org/t/word-wrap-function/20438/10 "2019-02-05T00:09:43Z")

</div>

I suppose you need to use `foldl` instead of `reduce` here, since the binary function you passed to `reduce` is not associative? (But cool example!)

---

<div class="post-metadata">

### Author: ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)
#### Post date: [February 6, 2019, 3:25pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438/11 "2019-02-06T15:25:52Z")

</div>

_ **BEAUTIFUL SOLUTION HAS BEEN DISCOVERED** _

```julia
word_wrap!(s::AbstractString, n=92; i=n, p=1, w=1) =
    for c=s; (i-=1)<-1 && (i=w-p+n; unsafe_store!(
        pointer(s,w),10)); c==' ' && (w=p); p+=1end

```

Verification:

```julia
julia> s = join("x".^rand(1:5,20), " ")
"xxxx xxxx x xxxx x xx x x xx x xxx xxx x xxxx xxx xxx xxxxx xxxxx x xxx "

julia> word_wrap!(s, 20)

julia> println(s)
xxxx xxxx x xxxx x
xx x x xx x xxx xxx
x xxxx xxx xxx xxxxx
xxxxx x xxx

```

Behold:

```julia
julia> @btime word_wrap!($s, 20)
  135.182 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [February 6, 2019, 3:44pm UTC](https://discourse.julialang.org/t/word-wrap-function/20438/12 "2019-02-06T15:44:40Z")

</div>

The solution is clever, but I think that `unsafe_store` and especially `pointer` may be problematic, as an `AbstractString` could have an implementation totally different from a `String` and not support either.

Also, squeezing everything into a single line is not necessary, standard line breaking and indentation would make your code more readable.
