|
1 | 1 | # This file is a part of Julia. License is MIT: https://julialang.org/license
|
2 | 2 |
|
3 |
| -# Wrapping |
| 3 | +const AnnotIO = Union{AnnotatedIOBuffer, IOContext{AnnotatedIOBuffer}} |
4 | 4 |
|
5 |
| -function ansi_length(s) |
6 |
| - replace(s, r"\e\[[0-9]+m" => "") |> textwidth |
| 5 | +function annotprint(f::Function, args...) |
| 6 | + buf = AnnotatedIOBuffer() |
| 7 | + f(buf, args...) |
| 8 | + read(buf, AnnotatedString) |
7 | 9 | end
|
8 | 10 |
|
9 |
| -words(s) = split(s, " ") |
10 |
| -lines(s) = split(s, "\n") |
| 11 | +""" |
| 12 | + with_output_annotations(f::Function, io::AnnotIO, annots::Pair{Symbol, <:Any}...) |
11 | 13 |
|
12 |
| -function wrapped_line(io::IO, s::AbstractString, width, i) |
13 |
| - ws = words(s) |
14 |
| - lines = String[] |
15 |
| - for word in ws |
16 |
| - word_length = ansi_length(word) |
17 |
| - word_length == 0 && continue |
18 |
| - if isempty(lines) || i + word_length + 1 > width |
19 |
| - i = word_length |
20 |
| - if length(lines) > 0 |
21 |
| - last_line = lines[end] |
22 |
| - maybe_underline = findlast(Base.text_colors[:underline], last_line) |
23 |
| - if !isnothing(maybe_underline) |
24 |
| - # disable underline style at end of line if not already disabled. |
25 |
| - maybe_disable_underline = max( |
26 |
| - last(something(findlast(Base.disable_text_style[:underline], last_line), -1)), |
27 |
| - last(something(findlast(Base.text_colors[:normal], last_line), -1)), |
28 |
| - ) |
| 14 | +Call `f(io)`, and apply `annots` to the output created by doing so. |
| 15 | +""" |
| 16 | +function with_output_annotations(f::Function, io::AnnotIO, annots::Pair{Symbol, <:Any}...) |
| 17 | + @nospecialize f annots |
| 18 | + aio = if io isa AnnotatedIOBuffer io else io.io end |
| 19 | + start = position(aio) + 1 |
| 20 | + f(io) |
| 21 | + stop = position(aio) |
| 22 | + for annot in annots |
| 23 | + push!(aio.annotations, (start:stop, annot)) |
| 24 | + end |
| 25 | +end |
29 | 26 |
|
30 |
| - if maybe_disable_underline < 0 || maybe_disable_underline < last(maybe_underline) |
| 27 | +""" |
| 28 | + wraplines(content::AnnotatedString, width::Integer = 80, column::Integer = 0) |
31 | 29 |
|
32 |
| - lines[end] = last_line * Base.disable_text_style[:underline] |
33 |
| - word = Base.text_colors[:underline] * word |
34 |
| - end |
| 30 | +Wrap `content` into a vector of lines of at most `width` (according to |
| 31 | +`textwidth`), with the first line starting at `column`. |
| 32 | +""" |
| 33 | +function wraplines(content::Annot, width::Integer = 80, column::Integer = 0) where { Annot <: AnnotatedString} |
| 34 | + s, lines = content.string, SubString{Annot}[] |
| 35 | + i, lastwrap, slen = firstindex(s), 0, ncodeunits(s) |
| 36 | + most_recent_break_oppotunity = 1 |
| 37 | + while i < slen |
| 38 | + if s[i] == ' ' |
| 39 | + most_recent_break_oppotunity = i |
| 40 | + elseif s[i] == '\n' |
| 41 | + push!(lines, content[nextind(s, lastwrap):prevind(s, i)]) |
| 42 | + lastwrap = i |
| 43 | + column = 0 |
| 44 | + elseif column >= width && most_recent_break_oppotunity > 1 |
| 45 | + if lastwrap == most_recent_break_oppotunity |
| 46 | + nextbreak = findfirst(' ', @view s[nextind(s, lastwrap):end]) |
| 47 | + if isnothing(nextbreak) |
| 48 | + break |
| 49 | + else |
| 50 | + most_recent_break_oppotunity = lastwrap + nextbreak |
35 | 51 | end
|
| 52 | + i = most_recent_break_oppotunity |
36 | 53 | end
|
37 |
| - push!(lines, word) |
38 |
| - else |
39 |
| - i += word_length + 1 |
40 |
| - lines[end] *= " " * word # this could be more efficient |
| 54 | + push!(lines, content[nextind(s, lastwrap):prevind(s, most_recent_break_oppotunity)]) |
| 55 | + lastwrap = most_recent_break_oppotunity |
| 56 | + column = 0 |
41 | 57 | end
|
| 58 | + column += textwidth(s[i]) |
| 59 | + i = nextind(s, i) |
42 | 60 | end
|
43 |
| - return i, lines |
44 |
| -end |
45 |
| - |
46 |
| -function wrapped_lines(io::IO, s::AbstractString; width = 80, i = 0) |
47 |
| - ls = String[] |
48 |
| - for ss in lines(s) |
49 |
| - i, line = wrapped_line(io, ss, width, i) |
50 |
| - append!(ls, line) |
| 61 | + if lastwrap < slen |
| 62 | + push!(lines, content[nextind(s, lastwrap):end]) |
51 | 63 | end
|
52 |
| - return ls |
| 64 | + lines |
53 | 65 | end
|
54 |
| - |
55 |
| -wrapped_lines(io::IO, f::Function, args...; width = 80, i = 0) = |
56 |
| - wrapped_lines(io, sprint(f, args...; context=io), width = width, i = 0) |
57 |
| - |
58 |
| -function print_wrapped(io::IO, s...; width = 80, pre = "", i = 0) |
59 |
| - lines = wrapped_lines(io, s..., width = width, i = i) |
60 |
| - isempty(lines) && return 0, 0 |
61 |
| - print(io, lines[1]) |
62 |
| - for line in lines[2:end] |
63 |
| - print(io, '\n', pre, line) |
64 |
| - end |
65 |
| - length(lines), length(pre) + ansi_length(lines[end]) |
66 |
| -end |
67 |
| - |
68 |
| -print_wrapped(f::Function, io::IO, args...; kws...) = print_wrapped(io, f, args...; kws...) |
0 commit comments